《苍穹外卖》前端课程知识点记录

一、VUE基础知识

基于脚手架创建前端工程

1. 环境要求

安装node.js:Node.js安装与配置(详细步骤)_nodejs安装及环境配置-CSDN博客
查看node和npm的版本号

安装Vue CLI:Vue.js安装与创建默认项目(详细步骤)_nodejs安装及环境配置-CSDN博客

查看vue版本

使用Vue CLI创建前端工程

  • 方式一:vue create项目名称

  ① 创建一个不带中文的文件夹,如下图:


② 创建工程---选择Vue 2

③ 选择npm


④ 如果中间有报错,如下:
npm ERR! code EPERM
npm ERR! syscall mkdir
npm ERR! path C:\Program Files\nodejs\node_cache\_cacache\index-v5\ee\aa
npm ERR! errno -4048
npm ERR! Error: EPERM: operation not permitted, mkdir 'C:\Program Files\nodejs\node_cache\_cacache\index-v5\ee\aa'

找到nodejs的安装目录,右击属性->安全->编辑->把所有权限都勾选上


⑤ 结果:

  • 方式二:vue ui

①打开ui界面

② 点击创建

③ 填写项目信息

④ 选择vue2,创建项目

⑤结果:

项目结构

运行项目

npm run serve

命令的最后一个单词并不是固定的,与package.json下写的这一项相关,如下

如果8080端口号被占用,可以在vue.config.js中更改端口号

如果上面这种方式不起作用的,可以到项目对应文件夹用cmd试试

退出运行:Ctrl + C

vue基本使用方式

Vue组件(Vue2)

Vue的组件文件以.vue结尾,每个组件由三部分组成:结构、样式、逻辑。

示例

Vue 2:一个Vue组件的模板只能有一个根元素。这是因为Vue 2使用的是基于AST(抽象语法树)的模板编译方式,需要将模板编译为render函数,而render函数只能返回一个根节点。

Vue 3 : Vue的模板编译器进行了重大改进,支持多个根元素。Vue 3使用了基于编译器的模板编译方式,这意味着在Vue 3中,一个组件的模板可以有多个根元素,而不再需要包裹在一个单独的根元素内。

文本插值

作用:用来绑定 data 方法返回的对象属性

用法:{{}}

属性绑定

作用:为标签的属性绑定data方法中返回的属性

用法:v-bind:xxx,简写为 :xxx

事件绑定

作用:为元素绑定对应的事件

用法:v-on:xxx,简写为@xxx

双向绑定

作用:表单输入项和data方法中的属性进行绑定,任意一方改变都会同步给另一方

用法:v-model

条件渲染

作用:根据表达式的值来动态渲染页面元素

用法:v-ifv-elsev-else-if

axios

Axios是一个基于promise的网络请求库,作用于浏览器和node.js中

安装命令:npm install axios

导入命令:import axios from 'axios'

axios的API列表:

请求备注
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url, data[, config]])
axios.patch(url[, data[, config]])

参数说明:

  • url:请求路径
  • data:请求体数据,最常见的是JSON格式数据
  • config:配置对象,可以设置查询参数、请求体信息

为了解决跨域问题,可以在vue.config.js文件中配置代理:

 axios统一使用方式:axios(config)

请求配置

网址:请求配置 | Axios中文文档 | Axios中文网 (axios-http.cn)

{// `url` 是用于请求的服务器 URLurl: '/user',// `method` 是创建请求时使用的方法method: 'get', // 默认值// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URLbaseURL: 'https://some-domain.com/api/',// `transformRequest` 允许在向服务器发送前,修改请求数据// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream// 你可以修改请求头。transformRequest: [function (data, headers) {// 对发送的 data 进行任意转换处理return data;}],// `transformResponse` 在传递给 then/catch 前,允许修改响应数据transformResponse: [function (data) {// 对接收的 data 进行任意转换处理return data;}],// 自定义请求头headers: {'X-Requested-With': 'XMLHttpRequest'},// `params` 是与请求一起发送的 URL 参数// 必须是一个简单对象或 URLSearchParams 对象params: {ID: 12345},// `paramsSerializer`是可选方法,主要用于序列化`params`// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)paramsSerializer: function (params) {return Qs.stringify(params, {arrayFormat: 'brackets'})},// `data` 是作为请求体被发送的数据// 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法// 在没有设置 `transformRequest` 时,则必须是以下类型之一:// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams// - 浏览器专属: FormData, File, Blob// - Node 专属: Stream, Bufferdata: {firstName: 'Fred'},// 发送请求体数据的可选语法// 请求方式 post// 只有 value 会被发送,key 则不会data: 'Country=Brasil&City=Belo Horizonte',// `timeout` 指定请求超时的毫秒数。// 如果请求时间超过 `timeout` 的值,则请求会被中断timeout: 1000, // 默认值是 `0` (永不超时)// `withCredentials` 表示跨域请求时是否需要使用凭证withCredentials: false, // default// `adapter` 允许自定义处理请求,这使测试更加容易。// 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。adapter: function (config) {/* ... */},// `auth` HTTP Basic Authauth: {username: 'janedoe',password: 's00pers3cret'},// `responseType` 表示浏览器将要响应的数据类型// 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'// 浏览器专属:'blob'responseType: 'json', // 默认值// `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)// 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求// Note: Ignored for `responseType` of 'stream' or client-side requestsresponseEncoding: 'utf8', // 默认值// `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称xsrfCookieName: 'XSRF-TOKEN', // 默认值// `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值// `onUploadProgress` 允许为上传处理进度事件// 浏览器专属onUploadProgress: function (progressEvent) {// 处理原生进度事件},// `onDownloadProgress` 允许为下载处理进度事件// 浏览器专属onDownloadProgress: function (progressEvent) {// 处理原生进度事件},// `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数maxContentLength: 2000,// `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数maxBodyLength: 2000,// `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。// 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),// 则promise 将会 resolved,否则是 rejected。validateStatus: function (status) {return status >= 200 && status < 300; // 默认值},// `maxRedirects` 定义了在node.js中要遵循的最大重定向数。// 如果设置为0,则不会进行重定向maxRedirects: 5, // 默认值// `socketPath` 定义了在node.js中使用的UNIX套接字。// e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。// 只能指定 `socketPath` 或 `proxy` 。// 若都指定,这使用 `socketPath` 。socketPath: null, // default// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http// and https requests, respectively, in node.js. This allows options to be added like// `keepAlive` that are not enabled by default.httpAgent: new http.Agent({ keepAlive: true }),httpsAgent: new https.Agent({ keepAlive: true }),// `proxy` 定义了代理服务器的主机名,端口和协议。// 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。// 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。// `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。// 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。// 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`proxy: {protocol: 'https',host: '127.0.0.1',port: 9000,auth: {username: 'mikeymike',password: 'rapunz3l'}},// see https://axios-http.com/zh/docs/cancellationcancelToken: new CancelToken(function (cancel) {}),// `decompress` indicates whether or not the response body should be decompressed // automatically. If set to `true` will also remove the 'content-encoding' header // from the responses objects of all decompressed responses// - Node only (XHR cannot turn off decompression)decompress: true // 默认值}

示例——配置代理

记得要先运行后端服务,启动redis

HelloWorld.vue

<template><div class="hello"><div><input type="button" value="发送POST请求" @click="handleSendPOST"/></div><div><input type="button" value="发送GET请求" @click="handleSendGET"/></div><div><input type="button" value="统一请求方式" @click="handleSend"/></div></div>
</template><script>
import axiox from 'axios'
export default {name: 'HelloWorld',props: {msg: String},methods: {handleSendPOST() {// 通过axios发送异域POST方式的http请求axiox.post('/api/admin/employee/login', {username: 'admin',password: '123456'}).then(res => {console.log(res.data)}).catch(error => {console.log(error.response)})},handleSendGET() {// 通过axios发送GET方式请求axiox.get('/api/admin/shop/status', {headers: {token: 'eyJhbGciOiJIUzI1NiJ9.eyJlbXBJZCI6MSwiZXhwIjoxNzE0MzIyNDAyfQ.gMfQXajaBTKnMuz19_BsmhWLGWov24rqZDLcPLwZCSA'}}).then(res => {console.log(res.data)})},handleSend() {// 使用axios提供的统一调用方式发送请求axiox({url: '/api/admin/employee/login',method: 'post',data: {  // data表示通过请求体传参username: 'admin',password: '123456'}}).then(res => {console.log(res.data.data.token)axiox({url: '/api/admin/shop/status',method: 'get',headers: {token: res.data.data.token}})})}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {margin: 40px 0 0;
}
ul {list-style-type: none;padding: 0;
}
li {display: inline-block;margin: 0 10px;
}
a {color: #42b983;
}
</style>

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,devServer:{port:8082,proxy: {'/api' : {target: 'http://localhost:8081',pathRewrite: {'^/api' : ''}}}}
})

结果

二、VUE进阶(router、vuex、typescript)

路由 Vue-Router

Vue-Router介绍

vue属于单页面应用,所谓的路由,就是根据浏览器路径不同,用不同的视图组件替换这个页面内容。

vue应用中如何实现路由?

  • 通过vue-router实现路由功能,需要安装js库(npm install vue-router)

基于Vue CLI创建带有路由功能的前端项目

命令:vue ui

①包管理器选择:npm

②预设选择:手动

③功能添加:Router

④配置版本选择:2.x,linter config选择:ESLint with error prevention only

⑤选择创建项目,不保存预设

⑥查看创建结果

⑦运行项目

路由配置

路由组成

VueRouter:路由器,根据路由请求在路由视图中动态渲染对应的视图组件

<router-link>:路由链接组件,浏览器会解析成<a>

<router-view>:路由视图组件,用来展示与路由匹配的视图组件

路由跳转

  • 标签式<router-link>
  • 编程式

如果请求的路径不存在,应该如何处理?

①当上面的路径都匹配不到时,重定向到最后一项

嵌套路由

嵌套路由:组件内要切换内容,就需要用到嵌套路由(子路由)

实现步骤:

  • 安装并导入elementui,实现页面布局(Container布局容器)---ContainerView.vue
    npm i element-ui -S

     
  • 提供子视图组件,用于效果展示 ---P1View.vue、P2View.vue、P3View.vue
    view/container/ContainerView.vue
    <template><el-container><el-header>Header</el-header><el-container><el-aside width="200px">Aside</el-aside><el-main>Main</el-main></el-container></el-container>
    </template><script>
    export default {};
    </script><style>.el-header, .el-footer {background-color: #B3C0D1;color: #333;text-align: center;line-height: 60px;}.el-aside {background-color: #D3DCE6;color: #333;text-align: center;line-height: 200px;}.el-main {background-color: #E9EEF3;color: #333;text-align: center;line-height: 160px;}body > .el-container {margin-bottom: 40px;}.el-container:nth-child(5) .el-aside,.el-container:nth-child(6) .el-aside {line-height: 260px;}.el-container:nth-child(7) .el-aside {line-height: 320px;}
    </style>
  • 在src/router/index.js中配置路由映射规则(嵌套路由配置)
  • 在布局容器视图中添加<router-view>,实现子视图组件展示
  • 在布局容器视图中添加<router-link>,实现路由请求

注意事项:子路由变化,切换的是【ContainerView组件】中‘<router-view></router-view>’部分的内容。

思考

1. 对于前面的案例,如果用户访问的路由是/c,会有什么效果呢?

2. 如果实现在访问/c时,默认就展示某个子视图组件呢?

状态管理vuex

vuex介绍

  • vuex是一个专为Vue.js应用程序开发的状态管理库
  • vuex可以在多个组件之间共享数据,并且共享的数据是响应式的,即数据的变更能及时渲染到模板
  • vuex采用集中式存储管理所有组件的状态

安装

npm install vuex@next --save

核心概念

  • state:状态对象,集中定义各个组件共享的数据
  • mutations:类似于一个事件,用于修改共享数据,要求必须是同步函数
  • actions:类似于mutation,可以包含异步操作,通过调用mutation来改变共享数据

使用方式

①创建带有vuex功能的脚手架工程

②src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)// 集中管理多个组件共享的数据
export default new Vuex.Store({state: {},getters: {},mutations: {},actions: {},modules: {}
})

③src/main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({// 使用vuex功能store,render: h => h(App)
}).$mount('#app')

④定义和展示共享数据

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)// 集中管理多个组件共享的数据
export default new Vuex.Store({// 集中定义共享数据state: {name: '未登录游客'},getters: {},mutations: {},actions: {},modules: {}
})

⑤在mutations中定义函数,修改共享数据

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)// 集中管理多个组件共享的数据
export default new Vuex.Store({// 集中定义共享数据state: {name: '未登录游客'},getters: {},// 修改共享数据只能通过mutation实现,必须是同步操作mutations: {setName(state, newName) {state.name = newName}},// 通过actions可以调用mutations,在action中可以进行异步操作actions: {},modules: {}
})
<template><div id="app">欢迎您,{{$store.state.name}}<input type = "button" value = "通过mutations修改共享数据" @click="handleUpdate"/><img alt="Vue logo" src="./assets/logo.png"><HelloWorld msg="Welcome to Your Vue.js App"/></div>
</template><script>
import HelloWorld from './components/HelloWorld.vue'export default {name: 'App',components: {HelloWorld},methods: {handleUpdate() {// mutations中定义的函数不能直接调用,必须通过这种方式来调用// setName为mutations中定义的函数名称,lisi为需要传递的参数this.$store.commit('setName', 'lisi')}}
}
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>

④在actions中定义函数,用于调用mutation

先安装axios

npm install axios
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'Vue.use(Vuex)// 集中管理多个组件共享的数据
export default new Vuex.Store({// 集中定义共享数据state: {name: '未登录游客'},getters: {},// 修改共享数据只能通过mutation实现,必须是同步操作mutations: {setName(state, newName) {state.name = newName}},// 通过actions可以调用mutations,在action中可以进行异步操作actions: {setNameByAxios(context) {axios ({url: '/api/admin/employee/login',method: 'post',data: {username: 'admin',password: '123456'}}).then(res => {if(res.data.code == 1) {// 异步请求后,需要修改共享数据// 调用mutation中定义的setName函数context.commit('setName', res.data.data.name)}})}},modules: {}
})
// App.vue
<template><div id="app">欢迎您,{{$store.state.name}}<input type = "button" value = "通过mutations修改共享数据" @click="handleUpdate"/><input type = "button" value = "调用actions中定义的函数" @click="handleCallAction"/><img alt="Vue logo" src="./assets/logo.png"><HelloWorld msg="Welcome to Your Vue.js App"/></div>
</template><script>
import HelloWorld from './components/HelloWorld.vue'export default {name: 'App',components: {HelloWorld},methods: {handleUpdate() {// mutations中定义的函数不能直接调用,必须通过这种方式来调用// setName为mutations中定义的函数名称,lisi为需要传递的参数this.$store.commit('setName', 'lisi')},handleCallAction() {// 调用actions中定义的函数,setNameByAxios为函数名this.$store.dispatch('setNameByAxios')}}
}
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,devServer: {port:8082,proxy: {'/api': {target: 'http://location:8081',pathRewrite: {'^/api': ''}}}}
})

思考

1. 如何理解vuex?

  • 实现多个组件之间的数据共享
  • 共享数据是响应式的,实时渲染到模板
  • 可以集中管理共享数据

2. 如何使用vuex?

  • 在store对象的state属性中定义共享数据
  • 在store对象的mutations属性中定义修改共享数据的函数
  • 在store对象的actions属性中定义调用mutation的函数,可以进行异或操作
  • mutations中的函数不能直接调用,只能通过store对象的commit方法调用
  • actions中定义的函数不能直接调用,只能通过store对象的dispatch方法调用

TypeScript

TypeScript介绍

  • TypeScript(简称:TS)是微软推出的开源语言
  • TypeScript是JavaScript的超集(JS有的TS都有)
  • TypeScript = Type + JavaScript(在JS基础上增加了类型支持)
  • TypeScript文件扩展名为ts
  • TypeScript可编译成标准的JavaScript,并且在编译时进行类型检查

安装typescript(全局安装)

如果安装失败,以管理员身份运行命令行窗口,可以在安装命令后加上 @5.0.2,以指定版本

npm install -g typescript

查看TS版本

tsc -v

示例

// 通过ts代码,指定函数的参数类型为string
function hello(msg:string) {console.log(msg)
}// 传入的参数类型为number
hello(123)

编译:tsc + 文件名

改正后(传参为:'123')

思考

1. TS为什么要增加类型支持?

  • TS属于静态类型编程语言,JS属于动态类型编程语言
  • 静态类型在编译期做类型检查,动态类型在执行期间做类型检查
  • 对于JS来说,需要等到代码执行的时候才可以发现错误(晚)
  • 对于TS来说,在代码编译的时候就可以发现错误(早)
  • 配合VSCode开发工具,TS可以提前在编写代码的同时就发现代码中的错误,减少找Bug、改Bug的时间

2. 如何理解TypeScript?

  • 是JavaScript的超集,兼容JavaScript
  • 扩展了JavaScript的语法,文件扩展名为ts
  • 可以编译成标准的JavaScript,并且可以在编译时进行类型检查
  • 全局安装npm install -g typescript
  • 视图tsc命令将ts文件编译成js文件
  • 使用node命令运行js文件

TypeScript常用类型

类型备注
字符串类型string
数字类型number
布尔类型boolean
数组类型number[], string[], boolean[]依此类推
任意类型any相当于又回到了没有类型的时代
复杂类型type与interface
函数类型() => void对函数的参数和返回值进行说明
字面量类型"a"|"b"|"c"限制变量或参数的取值
class类class Animal

类型标注的位置

  • 标注变量
  • 标注参数
  • 标注返回值

项目示例

1. 创建项目时勾选上TypeScript、Router、Vuex

2. 字符串类型、布尔类型、数字类型

// 字符串类型
let username: string = 'itcast'// 数字类型
let age: number = 20// 布尔类型
let isTrue: boolean = trueconsole.log(username)
console.log(age)
console.log(isTrue)

3. 字面量类型

// 字面量类型
function printText(s: string, alignment: 'left'|'right'|'center') {console.log(s, alignment)
}printText('hello', 'left')
printText('hello', 'right')

4. 复杂类型——interface

小技巧:可以通过在属性名后面加上?,表示当前属性为可选

// 定义接口
interface Cat {name: string,age: number
}// 定义变量为Cat类型
const c1: Cat = {name: '小白', age: 1}
// const c2: Cat = {name: '小白'}  // 错误:缺少age属性
// const c3: Cat = {name: '小白', age: 1, sex: '公'}  // 错误:多了sex属性console.log(c1)

5. class类

注意:使用class关键字来定义类,类中可以包含属性、构造方法、普通方法

// 定义一个类,使用class关键字
class User {name: string;  // 属性constructor(name: string) {// 构造方法this.name = name}// 方法study() {console.log(this.name + '正在学习')}
}// 使用User类型
const user = new User('张三')
// 输出类中的属性
console.log(user.name)
// 调用类中的方法
user.study()

6. Class类实现interface

interface Animal {name: stringeat(): void
}// 定义一个类Bird,实现上面的Animal接口
class Bird implements Animal {name: stringconstructor(name: string) {this.name = name}eat(): void {console.log(this.name + ' eat')}
}// 创建类型为Bird的对象
const b1 = new Bird('杜鹃')
console.log(b1.name)
b1.eat()

7. class类——类的继承


// 定义一个类Bird,实现上面的Animal接口
class Bird implements Animal {name: stringconstructor(name: string) {this.name = name}eat(): void {console.log(this.name + ' eat')}
}// 定义Parrot类,并且继承Bird类
class Parrot extends Bird {say():void {console.log(this.name + ' say hello')}
}
const myParrot = new Parrot('Polly')
myParrot.say()
myParrot.eat()

小结

1.TypeScript的常用类型有哪些?

  • string、number、boolean
  • 字面量、void
  • interface、class

2. TypeScript文件能直接运行吗?

  • 需要将TS文件编译为JS文件才能运行
  • 编译后的JS文件中类型会擦除

三、苍穹外卖前端项目环境搭建、员工管理

技术选型

  • node.js
  • vue
  • ElementUI
  • axios
  • vuex
  • vue-router
  • typescript

熟悉前端代码结构

1. 代码导入:直接导入课程资料中提供的前端工程,在此基础上开发即可

在苍穹外卖前端课程->资料->day02->资料->苍穹外卖前端初始工程

2. 重点文件/目录

3. 通过登录功能梳理前端代码

①先运行后端服务

②下载前端中的依赖(不需要指定安装哪些包,会自动扫描):npm install

把nodejs的版本降级到12版本,如果出现安全性问题,代开cmd执行下面的命令

可以参考这篇文章:node.js安装配置详细介绍以及nodejs版本降级_nodejs低版本-CSDN博客

我是把node.js降级到了12.22.12

npm config set strict-ssl false


npm install

④修改后端服务的地址(如果前面课程中修改了后端服务的端口号)

⑤npm run serve,前端的端口号为8888

⑥通过登录功能梳理前端代码

  • 获得登录页面路由地址
  • 从main.ts中找到路由文件
  • 从路由文件中找到登录视图组件
  • 从登录视图组件中找到登录方法
  • 跟踪登录方法的执行过程

员工分页查询

需求分析和接口设计

业务规则

根据页码展示员工信息

每页展示10条数据

分页查询可以根据需要,输入员工姓名进行查询

接口设计

代码开发

①从路由文件router.ts中找到员工管理页面(组件)

②初始页面

③制作页面头部效果

    <div class="container"><div class="tableBar"><label style="margin-right: 5px">员工姓名:</label><el-input placeholder="请输入员工姓名" style="width: 15%" clearable/><el-button type="primary" style="margin-left: 20px">查询</el-button><el-button type="primary" style="float: right"> + 添加员工</el-button></div></div>

注意

  • 输入框和按钮都是使用ElementUI提供的组件
  • 对于前端的组件只需要参考ElementUI提供的文档,进行修改即可

链接:Element - The world's most popular Vue UI framework

④员工分页查询

src/api/employee.ts

// 分页查询
export const getEmployeeList = (params: any) =>request({'url': `/employee/page`,'method': 'get','params': params})

src/view/employee/index.vue

<template><div class="dashboard-container"><div class="container"><div class="tableBar"><label style="margin-right: 5px">员工姓名:</label><el-inputv-model="name"placeholder="请输入员工姓名"style="width: 15%"clearable/><el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button><el-button type="primary" style="float: right"> + 添加员工</el-button></div><el-table :data="records" stripe style="width: 100%"><el-table-column prop="name" label="员工姓名" width="180"></el-table-column><el-table-column prop="username" label="账号" width="180"></el-table-column><el-table-column prop="phone" label="手机号"> </el-table-column><el-table-column prop="status" label="账号状态"><template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template></el-table-column><el-table-column prop="updateTime" label="最后操作时间"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="text">修改</el-button><el-button type="text">{{scope.row.status === 1 ? '禁用' : '启用'}}</el-button></template></el-table-column></el-table><el-paginationclass="pageList"@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page":page-sizes="[10, 20, 30, 40, 50]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div></div>
</template><script lang="ts">
import { getEmployeeList } from '@/api/employee'export default {// 模型数据data() {return {name: '', // 员工姓名,对应上面的输入框page: 1, // 页码pageSize: 10, // 每页记录数total: 0, // 总记录数records: [], // 当前页要展示的数据集合}},// 自动调用pageQuery方法// 这段代码是 Vue.js 组件中的生命周期钩子函数 created()。在 Vue.js 组件中,created() 是一个生命周期钩子函数,在组件实例被创建之后立即调用。这个钩子函数通常用于在组件实例创建后执行一些初始化任务。created() {this.pageQuery()},methods: {// 分页查询pageQuery() {// 准备请求参数const params = {name: this.name,page: this.page,pageSize: this.pageSize,}// 发送Ajax请求,访问后端服务,获取分页数据getEmployeeList(params).then((res) => {if (res.data.code === 1) {this.total = res.data.data.totalthis.records = res.data.data.records}}).catch((err) => {this.$message.console.error('请求出错了:' + err.message)})},// pageSize发送变化时触发handleSizeChange(pageSize) {this.pageSize = pageSizethis.pageQuery()},// page发生变化时触发handleCurrentChange(page) {this.page = pagethis.pageQuery()},},
}
</script><style lang="scss" scoped>
.disabled-text {color: #bac0cd !important;
}
</style>

功能测试

启用、禁用员工账号

需求分析和接口设计

业务规则

可以对状态为“启用”的员工账号进行“禁用”操作

可以对状态为“禁用”的员工账号进行“启用”操作

状态为“禁用”的员工账号不能登录系统

接口设计

代码开发

①src/api/employee.ts

// 启用禁用员工账号
export const enableOrDisableEmployee = (params: any) =>request({'url': `/employee/status/${params.status}`,'method': 'post','params': {id: params.id}})

②src/view/employee/index.vue

<template><div class="dashboard-container"><div class="container"><div class="tableBar"><label style="margin-right: 5px">员工姓名:</label><el-inputv-model="name"placeholder="请输入员工姓名"style="width: 15%"clearable/><el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button><el-button type="primary" style="float: right"> + 添加员工</el-button></div><el-table :data="records" stripe style="width: 100%"><el-table-column prop="name" label="员工姓名" width="180"></el-table-column><el-table-column prop="username" label="账号" width="180"></el-table-column><el-table-column prop="phone" label="手机号"> </el-table-column><el-table-column prop="status" label="账号状态"><template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template></el-table-column><el-table-column prop="updateTime" label="最后操作时间"></el-table-column><el-table-column label="操作"><template slot-scope="scope"><el-button type="text">修改</el-button><el-button type="text" @click="handleStartOrStop(scope.row)">{{scope.row.status === 1 ? '禁用' : '启用'}}</el-button></template></el-table-column></el-table><el-paginationclass="pageList"@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="page":page-sizes="[10, 20, 30, 40, 50]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div></div>
</template><script lang="ts">
import { getEmployeeList, enableOrDisableEmployee} from '@/api/employee'export default {// 模型数据data() {return {name: '', // 员工姓名,对应上面的输入框page: 1, // 页码pageSize: 10, // 每页记录数total: 0, // 总记录数records: [], // 当前页要展示的数据集合}},// 自动调用pageQuery方法created() {this.pageQuery()},methods: {// 分页查询pageQuery() {// 准备请求参数const params = {name: this.name,page: this.page,pageSize: this.pageSize,}// 发送Ajax请求,访问后端服务,获取分页数据getEmployeeList(params).then((res) => {if (res.data.code === 1) {this.total = res.data.data.totalthis.records = res.data.data.records}}).catch((err) => {this.$message.console.error('请求出错了:' + err.message)})},// pageSize发送变化时触发handleSizeChange(pageSize) {this.pageSize = pageSizethis.pageQuery()},// page发生变化时触发handleCurrentChange(page) {this.page = pagethis.pageQuery()},// 启用禁用员工账号handleStartOrStop(row) {if(row.username === 'admin') {this.$message.error('admin为系统的管理员账号,不能更改帐号状态!')return}// alert(`id=${row.id} status=${row.status}`)// 弹出确认提示框this.$confirm('确认要修改当前员工账号的状态吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {const p = {id: row.id,status: !row.status ? 1 : 0}enableOrDisableEmployee(p).then(res => {if(res.data.code === 1) {this.$message.success('员工的账号状态修改成功!')this.pageQuery()}})})}},
}
</script><style lang="scss" scoped>
.disabled-text {color: #bac0cd !important;
}
</style>

功能测试

添加员工

需求分析和接口设计

产品原型

接口设计

代码开发

添加员工操作步骤

  • 点击“添加员工”按钮,跳转到新增页面
  • 在新增员工页面录入员工相关信息
  • 点击“保存”按钮完成新增操作

①为“添加员工”按钮绑定单击事件:src/views/employee/index.vue

②提供handleAddEmp方法,进行路由跳转

③src/api/employee.ts

  // 新增员工
export const addEmployee = (params: any) =>request({'url': '/employee','method': 'post','data': params})

④src/views/employee/addEmployee.vue

<template><div class="addBrand-container"><div class="container"><el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="180px"><el-form-item label="账号" prop="username"><el-input v-model="ruleForm.username"></el-input></el-form-item><el-form-item label="员工姓名" prop="name"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model="ruleForm.phone"></el-input></el-form-item><el-form-item label="性别" prop="sex"><el-radio v-model="ruleForm.sex" label="1">男</el-radio><el-radio v-model="ruleForm.sex" label="2">女</el-radio></el-form-item><el-form-item label="身份证号" prop="idNumber"><el-input v-model="ruleForm.idNumber"></el-input></el-form-item><div class="subBox"><el-button type="primary" @click="submitForm('ruleForm',false)">保存</el-button><el-button v-if="this.optType === 'add'" type="primary" @click="submitForm('ruleForm',true)">保存并继续添加员工</el-button><el-button @click="() => this.$router.push('/employee')">返回</el-button></div></el-form></div></div>
</template><script lang="ts">
import {addEmployee} from '@/api/employee'
export default {data() {return {optType: 'add',ruleForm: {name: '',username: '',sex: '1',phone: '',idNumber: ''},rules: {name: [{ required: true, message: '请输入员工姓名', trigger: 'blur' }],username: [{ required: true, message: '请输入员工账号', trigger: 'blur' }],phone: [{ required: true, trigger: 'blur', validator: (rule, value, callback) => {if(value === '' || (!/^1(3|4|5|6|7|8)\d{9}$/.test(value))) {callback(new Error('请输入正确的手机号!'))} else {callback()}}}],idNumber: [{ required: true, trigger: 'blur', validator: (rule, value, callback) => {if(value === '' || (!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(X|x)$)/.test(value))) {callback(new Error('请输入正确的身份证号!'))} else {callback()}}}]}}},methods: {submitForm(formName, isContinue) {// 进行表单校验this.$refs[formName].validate((valid) => {if(valid) {// alert('所有表单项都符合要求')// 表单校验通过,发起Ajax请求,将数据提交到后端addEmployee(this.ruleForm).then((res) => {if(res.data.code === 1) {this.$message.success('员工添加成功!')if(isContinue) {  // 保存并继续添加this.ruleForm = {name: '',username: '',sex: '1',phone: '',idNumber: ''}} else {this.$router.push('/employee')}} else {this.$message.error(res.data.msg)}})}})}}
}
</script><style lang="scss" scoped>
.addBrand {&-container {margin: 30px;margin-top: 30px;.HeadLable {background-color: transparent;margin-bottom: 0px;padding-left: 0px;}.container {position: relative;z-index: 1;background: #fff;padding: 30px;border-radius: 4px;// min-height: 500px;.subBox {padding-top: 30px;text-align: center;border-top: solid 1px $gray-5;}}.idNumber {margin-bottom: 39px;}.el-form-item {margin-bottom: 29px;}.el-input {width: 293px;}}
}
</style>

功能测试

修改员工

需求分析和接口设计

产品原型

编辑员工功能涉及到两个接口:

  • 根据id查询员工信息
  • 编辑员工信息

代码开发

修改员工操作步骤:

  • 点击“修改”按钮,跳转到修改页面
  • 在修改员工页面录入员工相关信息
  • 点击“保存”按钮完成修改操作

注意

  • 由于添加员工和修改员工的表单项非常类似,所以添加和修改操作可以共用同一个页面addEmployee.vue
  • 修改员工设计原数据回显,所以需要传递员工id作为参数

①src/views/employee/index.vue,在员工管理页面中,为“修改”按钮绑定单击事件,用于跳转到修改页面

    // 跳转到修改员工页面(组件)handleUpdateEmp(row) {if(row.username === 'admin') {// 如果是内置管理员账号,不允许修改this.$message.error('admin为系统的管理员账号,不能修改!')return}// 跳转到修改页面,通过地址栏传递参数this.$router.push({path: '/employee/add',query: {id: row.id}})}

②由于addEmployee.vue为新增和修改共用页面,需要能够区分当前操作:

  • 如果路由中传递了id参数,则当前操作为修改
  • 如果路由中没有传递id参数,则当前操作为新增

③根据id查询员工,src/api/employee.ts

  // 根据id查询员工
export const queryEmployeeById = (id: number) =>request({'url': `/employee/${id}`,'method': 'get'})

④数据回显,src/views/employee/addEmployee.vue

⑤修改员工信息,src/api/employee.ts

// 修改员工
export const updateEmployee = (params: any) =>request({'url': '/employee','method': 'put','data': params})

⑥src/views/employee/addEmployee.vue

import { addEmployee, queryEmployeeById, updateEmployee} from '@/api/employee'
export default {data() {return {optType: '', // 当前新增的类型为新增或者修改ruleForm: {name: '',username: '',sex: '1',phone: '',idNumber: '',},rules: {name: [{ required: true, message: '请输入员工姓名', trigger: 'blur' }],username: [{ required: true, message: '请输入员工账号', trigger: 'blur' },],phone: [{required: true,trigger: 'blur',validator: (rule, value, callback) => {if (value === '' || !/^1(3|4|5|6|7|8)\d{9}$/.test(value)) {callback(new Error('请输入正确的手机号!'))} else {callback()}},},],idNumber: [{required: true,trigger: 'blur',validator: (rule, value, callback) => {if (value === '' ||!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(X|x)$)/.test(value)) {callback(new Error('请输入正确的身份证号!'))} else {callback()}},},],},}},// 页面加载完成执行的代码created() {// 获取路由参数{id},如果有则为修改操作,否则为新增操作this.optType = this.$route.query.id ? 'update' : 'add'if (this.optType === 'update') {// 修改操作,需要根据id查询员工信息用于页面回显queryEmployeeById(this.$route.query.id).then((res) => {if (res.data.code === 1) {this.ruleForm = res.data.data}})}},methods: {submitForm(formName, isContinue) {// 进行表单校验this.$refs[formName].validate((valid) => {if (valid) {// alert('所有表单项都符合要求')// 表单校验通过,发起Ajax请求,将数据提交到后端if (this.optType === 'add') {// 新增操作addEmployee(this.ruleForm).then((res) => {if (res.data.code === 1) {this.$message.success('员工添加成功!')if (isContinue) {// 保存并继续添加this.ruleForm = {name: '',username: '',sex: '1',phone: '',idNumber: '',}} else {this.$router.push('/employee')}} else {this.$message.error(res.data.msg)}})} else {// 修改操作updateEmployee(this.ruleForm).then(res => {if(res.data.code == 1) {this.$message.success('员工信息修改成功!')this.$router.push('/employee')}})}}})},},
}

功能测试

四、套餐管理

套餐分页查询

需求分析和接口设计

产品原型

业务规则

  • 根据页码展示套餐信息
  • 每页展示10条数据
  • 分页查询时可以根据需要输入套餐名称、套餐分类、售卖状态进行查询

接口设计

  • 套餐分页查询接口
  • 分类查询接口(用于下拉框中分类数据显示)

代码开发

①从路由文件router.ts中找到套餐管理页面(组件)

②制作页面头部效果,src/views/setmeal/index.vue

<template><div class="dashboard-container"><div class="container"><div class="tableBar"><div class="tableBar"><label style="margin-right: 5px">套餐名称:</label><el-input v-model="name" placeholder="请输入套餐名称" style="width: 15%" clearable/><label style="margin-left: 5px">套餐分类:</label><el-select v-model="value" placeholder="请选择"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"></el-option></el-select><label style="margin-left: 5px">售卖状态:</label><el-select v-model="saleStatus" placeholder="请选择"><el-optionv-for="item in saleStatusArr":key="item.value":label="item.label":value="item.value"></el-option></el-select><el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button><div style="float:right"><el-button type="danger">批量删除</el-button><el-button type="info">+新建套餐</el-button></div></div></div></div></div>
</template><script lang="ts">
export default {// 模型数据data() {return {name: '', // 套餐名称,对应上面的输入框page: 1, // 页码pageSize: 10, // 每页记录数total: 0, // 总记录数records: [], // 当前页要展示的数据集合options: [{value: '选项1',label: '黄金糕'}, {value: '选项2',label: '双皮奶'}, {value: '选项3',label: '蚵仔煎'}, {value: '选项4',label: '龙须面'}, {value: '选项5',label: '北京烤鸭'}],value: '',saleStatusArr:[{value: '1',label: '起售'}, {value: '0',label: '停售'}],saleStatus: ''}},
}
</script>
<style lang="scss">
.el-table-column--selection .cell {padding-left: 10px;
}
</style>
<style lang="scss" scoped>
.dashboard {&-container {margin: 30px;.container {background: #fff;position: relative;z-index: 1;padding: 30px 28px;border-radius: 4px;.tableBar {margin-bottom: 20px;.tableLab {float: right;span {cursor: pointer;display: inline-block;font-size: 14px;padding: 0 20px;color: $gray-2;}}}.tableBox {width: 100%;border: 1px solid $gray-5;border-bottom: 0;}.pageList {text-align: center;margin-top: 30px;}//查询黑色按钮样式.normal-btn {background: #333333;color: white;margin-left: 20px;}}}
}
</style>

注意

  • 输入框、按钮、下拉框都是使用ElementUI提供的组件
  • 对于前端的组件只需要参考ElementUI提供的文档,进行修改即可

③导入查询套餐分类的JS方法,动态填充套餐分类下拉框,src/views/setmeal/index.vue

完整代码(做了一些小调整)

<template><div class="dashboard-container"><div class="container"><div class="tableBar"><div class="tableBar"><label style="margin-right: 5px">套餐名称:</label><el-input v-model="name" placeholder="请输入套餐名称" style="width: 15%" clearable/><label style="margin-left: 5px">套餐分类:</label><el-select v-model="categoryId" placeholder="请选择"><el-optionv-for="item in options":key="item.id":label="item.name":value="item.id"></el-option></el-select><label style="margin-left: 5px">售卖状态:</label><el-select v-model="status" placeholder="请选择"><el-optionv-for="item in statusArr":key="item.value":label="item.label":value="item.value"></el-option></el-select><el-button type="primary" style="margin-left: 20px" @click="pageQuery()">查询</el-button><div style="float:right"><el-button type="danger">批量删除</el-button><el-button type="info">+新建套餐</el-button></div></div></div></div></div>
</template><script lang="ts">
import {getCategoryByType} from '@/api/category'
export default {// 模型数据data() {return {name: '', // 套餐名称,对应上面的输入框page: 1, // 页码pageSize: 10, // 每页记录数total: 0, // 总记录数records: [], // 当前页要展示的数据集合options: [],categoryId: '',  // 分类idstatusArr:[{value: '1',label: '起售'}, {value: '0',label: '停售'}],status: ''  // 售卖状态}},created() {// 查询套餐分类,用于填充查询页面的下拉框getCategoryByType({type:2}).then(res => {if(res.data.code == 1) {this.options = res.data.data}})}
}
</script>
<style lang="scss">
.el-table-column--selection .cell {padding-left: 10px;
}
</style>
<style lang="scss" scoped>
.dashboard {&-container {margin: 30px;.container {background: #fff;position: relative;z-index: 1;padding: 30px 28px;border-radius: 4px;.tableBar {margin-bottom: 20px;.tableLab {float: right;span {cursor: pointer;display: inline-block;font-size: 14px;padding: 0 20px;color: $gray-2;}}}.tableBox {width: 100%;border: 1px solid $gray-5;border-bottom: 0;}.pageList {text-align: center;margin-top: 30px;}//查询黑色按钮样式.normal-btn {background: #333333;color: white;margin-left: 20px;}}}
}
</style>

src/api/category.ts

// 根据类型查询分类:1为菜品分类 2为套餐分类
export const getCategoryByType = (params: any) => {return request({url: `/category/list`,method: 'get',params: params})
}

④为查询按钮绑定事件,发送Ajax请求获取分页数据

src/api/setMeal.js

//套餐分页查询
export const getSetmealPage = (params: any) => {return request({url: '/setmeal/page',method: 'GET',params: params})
}

src/views/setmeal/index.vue

⑤分页查询,src/views/setmeal/index.vue

        <el-table :data="records" stripe class="tableBox" @selection-change="handleSelectionChange"><el-table-column type="selection" width="25" /><el-table-column prop="name" label="套餐名称" /><el-table-column label="图片"><template slot-scope="scope"><el-image style="width: 80px; height: 40px; border: none" :src="scope.row.image"></el-image></template></el-table-column><el-table-column prop="categoryName" label="套餐分类" /><el-table-column prop="price" label="套餐价"/><el-table-column label="售卖状态"><template slot-scope="scope"><div class="tableColumn-status" :class="{ 'stop-use': scope.row.status === 0 }">{{ scope.row.status === 0 ? '停售' : '启售' }}</div></template></el-table-column><el-table-column prop="updateTime" label="最后操作时间" /><el-table-column label="操作" align="center" width="250px"><template slot-scope="scope"><el-button type="text" size="small"> 修改 </el-button><el-button type="text" size="small" @click="handleStartOrStop(scope.row)">{{ scope.row.status == '1' ? '停售' : '启售' }}</el-button><el-button type="text" size="small" @click="handleDelete('S',scope.row.id)"> 删除 </el-button></template></el-table-column></el-table><el-pagination class="pageList":page-sizes="[10, 20, 30, 40]":page-size="pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange" />

功能测试

启售停售套餐

需求分析和接口设计

产品原型

业务规则

  • 可以对状态为“启售”的套餐进行“停售:操作
  • 可以对状态为”停售“的套餐进行”启售“操作

接口设计

代码开发

①为启售、停售按钮绑定单击事件,src/views/setmeal/index.vue

import {getSetmealPage, enableOrDisableSetmeal, deleteSetmeal } from '@/api/setMeal'
    handleStartOrStop(row) {// alert(`id=${row.id} status=${row.status}`) this.$confirm('确认调整该套餐的售卖状态?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(() => {enableOrDisableSetmeal({ id: row.id, status: !row.status ? 1 : 0 }).then((res) => {if (res.status === 200) {this.$message.success('套餐售卖状态更改成功!')this.pageQuery()}}).catch((err) => {this.$message.error('请求出错了:' + err.message)})})}

src/api/setMeal.ts

//套餐启售停售
export const enableOrDisableSetmeal = (params: any) => {return request({url: `/setmeal/status/${params.status}`,method: 'POST',params: {id: params.id}})
}

注意:这里测试时要运行redis-server,否则会出现下面的错误

功能测试

删除套餐

需求分析和设计

产品原型

业务规则

  • 点击删除按钮,删除指定的一个套餐
  • 勾选需要删除的套餐,点击批量删除按钮,删除选中的一个或多个套餐

接口设计

代码开发

①在src/api/setMeal.ts中封装删除套餐方法,发送Ajax请求

//删除套餐
export const deleteSetmeal = (ids: string) => {//1,2,3return request({url: '/setmeal',method: 'DELETE',params: {ids: ids}})
}

②在src/views/setmeal/index.vue书写删除按钮单击事件

    // 删除套餐handleDelete(type:string, id:string) {deleteSetmeal(id).then(res => {if(res.data.code === 1) {this.$message.success('删除成功!')this.pageQuery()} else {this.$message.error(res.data.msg)}})}

③批量删除

在src/views/setmeal/index.vue中添加模型数据

为批量删除按钮绑定单击事件

    // 删除套餐handleDelete(type:string, id:string) {this.$confirm('确认删除当前指定的套餐,是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning',}).then(() => {let param = ''if(type == 'B') {// 批量删除// alert(this.multipleSelection.length)const arr = new Arraythis.multipleSelection.forEach(element => {arr.push(element.id)})param = arr.join(',')} else {// 单一删除param = id}deleteSetmeal(param).then(res => {if(res.data.code === 1) {this.$message.success('删除成功!')this.pageQuery()} else {this.$message.error(res.data.msg)}})})},

功能测试

新增套餐

需求分析和接口设计

产品原型

接口设计

  • 根据类型查询分类接口
  • 根据分类查询菜品接口
  • 文件上传接口
  • 新增套餐接口

代码解读

新增套餐操作步骤

①点击”新建套餐“按钮,跳转到新增页面,src/views/setmeal/index.vue

src/router.ts

②在套餐页面录入套餐相关信息,src/views/setmeal/addSetmeal.vue

<template><div class="addBrand-container"><div class="container"><el-form ref="ruleForm":model="ruleForm":rules="rules":inline="true"label-width="180px"class="demo-ruleForm"><div><el-form-item label="套餐名称:"prop="name"><el-input v-model="ruleForm.name"placeholder="请填写套餐名称"maxlength="14" /></el-form-item><el-form-item label="套餐分类:"prop="idType"><el-select v-model="ruleForm.idType"placeholder="请选择套餐分类"@change="$forceUpdate()"><el-option v-for="(item, index) in setMealList":key="index":label="item.name":value="item.id" /></el-select></el-form-item></div><div><el-form-item label="套餐价格:"prop="price"><el-input v-model="ruleForm.price"placeholder="请设置套餐价格" /></el-form-item></div><div><el-form-item label="套餐菜品:"required><el-form-item><div class="addDish"><span v-if="dishTable.length == 0"class="addBut"@click="openAddDish('new')">+ 添加菜品</span><div v-if="dishTable.length != 0"class="content"><div class="addBut"style="margin-bottom: 20px"@click="openAddDish('change')">+ 添加菜品</div><div class="table"><el-table :data="dishTable"style="width: 100%"><el-table-column prop="name"label="名称"width="180"align="center" /><el-table-column prop="price"label="原价"width="180"align="center"><template slot-scope="scope">{{ (Number(scope.row.price).toFixed(2) * 100) / 100 }}</template></el-table-column><el-table-column prop="address"label="份数"align="center"><template slot-scope="scope"><el-input-number v-model="scope.row.copies"size="small":min="1":max="99"label="描述文字" /></template></el-table-column><el-table-column prop="address"label="操作"width="180px;"align="center"><template slot-scope="scope"><el-button type="text"size="small"class="delBut non"@click="delDishHandle(scope.$index)">删除</el-button></template></el-table-column></el-table></div></div></div></el-form-item></el-form-item></div><div><el-form-item label="套餐图片:"requiredprop="image"><image-upload :prop-image-url="imageUrl"@imageChange="imageChange">图片大小不超过2M<br>仅能上传 PNG JPEG JPG类型图片<br>建议上传200*200或300*300尺寸的图片</image-upload></el-form-item></div><div class="address"><el-form-item label="套餐描述:"><el-input v-model="ruleForm.description"type="textarea":rows="3"maxlength="200"placeholder="套餐描述,最长200字" /></el-form-item></div><div class="subBox address"><el-form-item><el-button @click="() => $router.back()">取消</el-button><el-button type="primary":class="{ continue: actionType === 'add' }"@click="submitForm('ruleForm', false)">保存</el-button><el-button v-if="actionType == 'add'"type="primary"@click="submitForm('ruleForm', true)">保存并继续添加</el-button></el-form-item></div></el-form></div><el-dialog v-if="dialogVisible"title="添加菜品"class="addDishList":visible.sync="dialogVisible"width="60%":before-close="handleClose"><AddDish v-if="dialogVisible"ref="adddish":check-list="checkList":seach-key="seachKey":dish-list="dishList"@checkList="getCheckList" /><span slot="footer"class="dialog-footer"><el-button @click="handleClose">取 消</el-button><el-button type="primary"@click="addTableList">添 加</el-button></span></el-dialog></div>
</template><script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import HeadLable from '@/components/HeadLable/index.vue'
import ImageUpload from '@/components/ImgUpload/index.vue'
import AddDish from './components/AddDish.vue'
import { querySetmealById, addSetmeal, editSetmeal } from '@/api/setMeal'
import { getCategoryList } from '@/api/dish'
import { baseUrl } from '@/config.json'@Component({name: 'addShop',components: {HeadLable,AddDish,ImageUpload}
})
export default class extends Vue {private value: string = ''private setMealList: [] = []private seachKey: string = ''private dishList: [] = []private imageUrl: string = ''private actionType: string = ''private dishTable: [] = []private dialogVisible: boolean = falseprivate checkList: any[] = []private ruleForm = {name: '',categoryId: '',price: '',code: '',image: '',description: '',dishList: [],status: true,idType: ''}get rules() {return {name: {required: true,validator: (rule: any, value: string, callback: Function) => {if (!value) {callback(new Error('请输入套餐名称'))} else {const reg = /^([A-Za-z0-9\u4e00-\u9fa5]){2,20}$/if (!reg.test(value)) {callback(new Error('套餐名称输入不符,请输入2-20个字符'))} else {callback()}}},trigger: 'blur'},idType: {required: true,message: '请选择套餐分类',trigger: 'change'},image: {required: true,message: '菜品图片不能为空'},price: {required: true,// 'message': '请输入套餐价格',validator: (rules: any, value: string, callback: Function) => {const reg = /^([1-9]\d{0,5}|0)(\.\d{1,2})?$/if (!reg.test(value) || Number(value) <= 0) {callback(new Error('套餐价格格式有误,请输入大于零且最多保留两位小数的金额'))} else {callback()}},trigger: 'blur'},code: { required: true, message: '请输入商品码', trigger: 'blur' }}}created() {this.getDishTypeList()this.actionType = this.$route.query.id ? 'edit' : 'add'if (this.actionType == 'edit') {this.init()}}private async init() {querySetmealById(this.$route.query.id).then(res => {if (res && res.data && res.data.code === 1) {this.ruleForm = res.data.datathis.ruleForm.status = res.data.data.status == '1';(this.ruleForm as any).price = res.data.data.price// this.imageUrl = `http://172.17.2.120:8080/common/download?name=${res.data.data.image}`this.imageUrl = res.data.data.imagethis.checkList = res.data.data.setmealDishesthis.dishTable = res.data.data.setmealDishes.reverse()this.ruleForm.idType = res.data.data.categoryId} else {this.$message.error(res.data.msg)}})}private seachHandle() {this.seachKey = this.value}// 获取套餐分类private getDishTypeList() {getCategoryList({ type: 2, page: 1, pageSize: 1000 }).then(res => {if (res && res.data && res.data.code === 1) {this.setMealList = res.data.data.map((obj: any) => ({...obj,idType: obj.id}))} else {this.$message.error(res.data.msg)}})}// 通过套餐ID获取菜品列表分类// private getDishList (id:number) {//   getDishListType({id}).then(res => {//     if (res.data.code == 200) {//       const { data } = res.data//       this.dishList = data//     } else {//       this.$message.error(res.data.desc)//     }//   })// }// 删除套餐菜品delDishHandle(index: any) {this.dishTable.splice(index, 1)this.checkList = this.dishTable// this.checkList.splice(index, 1)}// 获取添加菜品数据 - 确定加菜倒序展示private getCheckList(value: any) {this.checkList = [...value].reverse()}// 添加菜品openAddDish(st: string) {this.seachKey = ''this.dialogVisible = true}// 取消添加菜品handleClose(done: any) {// this.$refs.adddish.close()this.dialogVisible = falsethis.checkList = JSON.parse(JSON.stringify(this.dishTable))// this.dialogVisible = false}// 保存添加菜品列表public addTableList() {this.dishTable = JSON.parse(JSON.stringify(this.checkList))this.dishTable.forEach((n: any) => {n.copies = 1})this.dialogVisible = false}public submitForm(formName: any, st: any) {;(this.$refs[formName] as any).validate((valid: any) => {if (valid) {if (this.dishTable.length === 0) {return this.$message.error('套餐下菜品不能为空')}if (!this.ruleForm.image) return this.$message.error('套餐图片不能为空')let prams = { ...this.ruleForm } as anyprams.setmealDishes = this.dishTable.map((obj: any) => ({copies: obj.copies,dishId: obj.dishId,name: obj.name,price: obj.price}));(prams as any).status =this.actionType === 'add' ? 0 : this.ruleForm.status ? 1 : 0prams.categoryId = this.ruleForm.idType// delete prams.dishListif (this.actionType == 'add') {delete prams.idaddSetmeal(prams).then(res => {if (res && res.data && res.data.code === 1) {this.$message.success('套餐添加成功!')if (!st) {this.$router.push({ path: '/setmeal' })} else {;(this as any).$refs.ruleForm.resetFields()this.dishList = []this.dishTable = []this.ruleForm = {name: '',categoryId: '',price: '',code: '',image: '',description: '',dishList: [],status: true,id: '',idType: ''} as anythis.imageUrl = ''}} else {this.$message.error(res.data.msg)}}).catch(err => {this.$message.error('请求出错了:' + err.message)})} else {delete prams.updateTimeeditSetmeal(prams).then(res => {if (res.data.code === 1) {this.$message.success('套餐修改成功!')this.$router.push({ path: '/setmeal' })} else {// this.$message.error(res.data.desc || res.data.message)}}).catch(err => {this.$message.error('请求出错了:' + err.message)})}} else {// console.log('error submit!!')return false}})}imageChange(value: any) {this.ruleForm.image = value}
}
</script>
<style>
.avatar-uploader .el-icon-plus:after {position: absolute;display: inline-block;content: ' ' !important;left: calc(50% - 20px);top: calc(50% - 40px);width: 40px;height: 40px;background: url('./../../assets/icons/icon_upload@2x.png') center centerno-repeat;background-size: 20px;
}
</style>
<style lang="scss">
// .el-form-item__error {
//   top: 90%;
// }
.addBrand-container {.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.avatar-uploader .el-upload:hover {border-color: #ffc200;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 200px;height: 160px;line-height: 160px;text-align: center;}.avatar {width: 200px;height: 160px;display: block;}// .el-form--inline .el-form-item__content {//   width: 293px;// }.el-input {width: 293px;}.address {.el-form-item__content {width: 777px !important;}}.el-input__prefix {top: 2px;}.addDish {.el-input {width: 130px;}.el-input-number__increase {border-left: solid 1px #fbe396;background: #fffbf0;}.el-input-number__decrease {border-right: solid 1px #fbe396;background: #fffbf0;}input {border: 1px solid #fbe396;}.table {border: solid 1px #ebeef5;border-radius: 3px;th {padding: 5px 0;}td {padding: 7px 0;}}}.addDishList {.seachDish {position: absolute;top: 12px;right: 20px;}.el-dialog__footer {padding-top: 27px;}.el-dialog__body {padding: 0;border-bottom: solid 1px #efefef;}.seachDish {.el-input__inner {height: 40px;line-height: 40px;}}}
}
</style>
<style lang="scss" scoped>
.addBrand {&-container {margin: 30px;.container {position: relative;z-index: 1;background: #fff;padding: 30px;border-radius: 4px;min-height: 500px;.subBox {padding-top: 30px;text-align: center;border-top: solid 1px $gray-5;}.el-input {width: 350px;}.addDish {width: 777px;.addBut {background: #ffc200;display: inline-block;padding: 0px 20px;border-radius: 3px;line-height: 40px;cursor: pointer;border-radius: 4px;color: #333333;font-weight: 500;}.content {background: #fafafb;padding: 20px;border: solid 1px #d8dde3;border-radius: 3px;}}}}
}
</style>

src/views/setmeal/components/AddDish.vue

<template><div class="addDish"><div class="leftCont"><div v-show="seachKey.trim() == ''"class="tabBut"><span v-for="(item, index) in dishType":key="index":class="{ act: index == keyInd }"@click="checkTypeHandle(index, item.id)">{{ item.name }}</span></div><div class="tabList"><div class="table":class="{ borderNone: !dishList.length }"><div v-if="dishList.length == 0"style="padding-left: 10px"><Empty /></div><el-checkbox-group v-if="dishList.length > 0"v-model="checkedList"@change="checkedListHandle"><div v-for="(item, index) in dishList":key="item.name + item.id"class="items"><el-checkbox :key="index":label="item.name"><div class="item"><span style="flex: 3; text-align: left">{{item.dishName}}</span><span>{{ item.status == 0 ? '停售' : '在售' }}</span><span>{{ (Number(item.price) ).toFixed(2)*100/100 }}</span></div></el-checkbox></div></el-checkbox-group></div></div></div><div class="ritCont"><div class="tit">已选菜品({{ checkedListAll.length }})</div><div class="items"><div v-for="(item, ind) in checkedListAll":key="ind"class="item"><span>{{ item.dishName || item.name }}</span><span class="price">¥ {{ (Number(item.price) ).toFixed(2)*100/100 }} </span><span class="del"@click="delCheck(item.name)"><img src="./../../../assets/icons/btn_clean@2x.png"alt=""></span></div></div></div></div>
</template><script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator'
// import {getDishTypeList, getDishListType} from '@/api/dish';
import { getCategoryList, queryDishList } from '@/api/dish'
import Empty from '@/components/Empty/index.vue'@Component({name: 'selectInput',components: {Empty}
})
export default class extends Vue {@Prop({ default: '' }) private value!: number@Prop({ default: [] }) private checkList!: any[]@Prop({ default: '' }) private seachKey!: stringprivate dishType: [] = []private dishList: [] = []private allDishList: any[] = []private dishListCache: any[] = []private keyInd = 0private searchValue: string = ''public checkedList: any[] = []private checkedListAll: any[] = []private ids: any = new Set()created() {this.init()}@Watch('seachKey')private seachKeyChange(value: any) {if (value.trim()) {this.getDishForName(this.seachKey)}}public init() {// 菜单列表数据获取this.getDishType()// 初始化选项this.checkedList = this.checkList.map((it: any) => it.name)// 已选项的菜品-详细信息this.checkedListAll = this.checkList.reverse()}// 获取套餐分类public getDishType() {getCategoryList({ type: 1 }).then(res => {if (res && res.data && res.data.code === 1) {this.dishType = res.data.datathis.getDishList(res.data.data[0].id)} else {this.$message.error(res.data.msg)}// if (res.data.code == 200) {//   const { data } = res.data//   this.   = data//   this.getDishList(data[0].category_id)// } else {//   this.$message.error(res.data.desc)// }})}// 通过套餐ID获取菜品列表分类private getDishList(id: number) {queryDishList({ categoryId: id }).then(res => {if (res && res.data && res.data.code === 1) {if (res.data.data.length == 0) {this.dishList = []return}let newArr = res.data.datanewArr.forEach((n: any) => {n.dishId = n.idn.copies = 1// n.dishCopies = 1n.dishName = n.name})this.dishList = newArrif (!this.ids.has(id)) {this.allDishList = [...this.allDishList, ...newArr]}this.ids.add(id)} else {this.$message.error(res.data.msg)}})}// 关键词收搜菜品列表分类private getDishForName(name: any) {queryDishList({ name }).then(res => {if (res && res.data && res.data.code === 1) {let newArr = res.data.datanewArr.forEach((n: any) => {n.dishId = n.idn.dishName = n.name})this.dishList = newArr} else {this.$message.error(res.data.msg)}})}// 点击分类private checkTypeHandle(ind: number, id: any) {this.keyInd = indthis.getDishList(id)}// 添加菜品private checkedListHandle(value: [string]) {// TODO 实现倒序 由于value是组件内封装无法从前面添加 所有取巧处理倒序添加// 倒序展示 - 数据处理前反正 为正序this.checkedListAll.reverse()// value 是一个只包含菜品名的数组 需要从 dishList中筛选出 对应的详情// 操作添加菜品const list = this.allDishList.filter((item: any) => {let datavalue.forEach((it: any) => {if (item.name == it) {data = item}})return data})// 编辑的时候需要与已有菜品合并// 与当前请求下的选择性 然后去重就是当前的列表const dishListCat = [...this.checkedListAll, ...list]let arrData: any[] = []this.checkedListAll = dishListCat.filter((item: any) => {let allArrDateif (arrData.length == 0) {arrData.push(item.name)allArrDate = item} else {const st = arrData.some(it => item.name == it)if (!st) {arrData.push(item.name)allArrDate = item}}return allArrDate})// 如果是减菜 走这里if (value.length < arrData.length) {this.checkedListAll = this.checkedListAll.filter((item: any) => {if (value.some(it => it == item.name)) {return item}})}this.$emit('checkList', this.checkedListAll)// 数据处理完反转为倒序this.checkedListAll.reverse()}open(done: any) {this.dishListCache = JSON.parse(JSON.stringify(this.checkList))}close(done: any) {this.checkList = this.dishListCache}// 删除private delCheck(name: any) {const index = this.checkedList.findIndex(it => it === name)const indexAll = this.checkedListAll.findIndex((it: any) => it.name === name)this.checkedList.splice(index, 1)this.checkedListAll.splice(indexAll, 1)this.$emit('checkList', this.checkedListAll)}
}
</script>
<style lang="scss">
.addDish {.el-checkbox__label {width: 100%;}.empty-box {margin-top: 50px;margin-bottom: 0px;}
}
</style>
<style lang="scss" scoped>
.addDish {padding: 0 20px;display: flex;line-height: 40px;.empty-box {img {width: 190px;height: 147px;}}.borderNone {border: none !important;}span,.tit {color: #333;}.leftCont {display: flex;border-right: solid 1px #efefef;width: 60%;padding: 15px;.tabBut {width: 110px;font-weight: bold;border-right: solid 2px #f4f4f4;span {display: block;text-align: center;// border-right: solid 2px #f4f4f4;cursor: pointer;position: relative;}}.act {border-color: $mine !important;color: $mine !important;}.act::after {content: ' ';display: inline-block;background-color: $mine;width: 2px;height: 40px;position: absolute;right: -2px;}.tabList {flex: 1;padding: 15px;height: 400px;overflow-y: scroll;.table {border: solid 1px #f4f4f4;border-bottom: solid 1px #f4f4f4;.items {border-bottom: solid 1px #f4f4f4;padding: 0 10px;display: flex;.el-checkbox,.el-checkbox__label {width: 100%;}.item {display: flex;padding-right: 20px;span {display: inline-block;text-align: center;flex: 1;font-weight: normal;}}}}}}.ritCont {width: 40%;.tit {margin: 0 15px;font-weight: bold;}.items {height: 338px;padding: 4px 15px;overflow: scroll;}.item {box-shadow: 0px 1px 4px 3px rgba(0, 0, 0, 0.03);display: flex;text-align: center;padding: 0 10px;margin-bottom: 20px;border-radius: 6px;color: #818693;span:first-child {text-align: left;color: #20232a;flex: 70%;}.price {display: inline-block;flex: 70%;text-align: left;}.del {cursor: pointer;img {position: relative;top: 5px;width: 20px;}}}}
}
</style>

src/api/setMeals.ts

// 修改数据接口
export const editSetmeal = (params: any) => {return request({url: '/setmeal',method: 'put',data: { ...params }})
}// 新增数据接口
export const addSetmeal = (params: any) => {return request({url: '/setmeal',method: 'post',data: { ...params }})
}// 查询详情接口
export const querySetmealById = (id: string | (string | null)[]) => {return request({url: `/setmeal/${id}`,method: 'get'})
}

src/api/dish.ts

import request from '@/utils/request'
/**** 菜品管理***/
// 查询列表接口
export const getDishPage = (params: any) => {return request({url: '/dish/page',method: 'get',params})
}// 删除接口
export const deleteDish = (ids: string) => {return request({url: '/dish',method: 'delete',params: { ids }})
}// 修改接口
export const editDish = (params: any) => {return request({url: '/dish',method: 'put',data: { ...params }})
}// 新增接口
export const addDish = (params: any) => {return request({url: '/dish',method: 'post',data: { ...params }})
}// 查询详情
export const queryDishById = (id: string | (string | null)[]) => {return request({url: `/dish/${id}`,method: 'get'})
}// 获取菜品分类列表
export const getCategoryList = (params: any) => {return request({url: '/category/list',method: 'get',params})
}// 查菜品列表的接口
export const queryDishList = (params: any) => {return request({url: '/dish/list',method: 'get',params})
}// 文件down预览
export const commonDownload = (params: any) => {return request({headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},url: '/common/download',method: 'get',params})
}// 起售停售---批量起售停售接口
export const dishStatusByStatus = (params: any) => {return request({url: `/dish/status/${params.status}`,method: 'post',params: { id: params.id }})
}//菜品分类数据查询
export const dishCategoryList = (params: any) => {return request({url: `/category/list`,method: 'get',params: { ...params }})
}

③点击”保存“按钮完成新增操作

功能测试

完结!!!

前端完整源码:https://pan.baidu.com/s/1JAI65SyP8qIIeLxh2U923g?pwd=ewap 

后端完整源码:https://pan.baidu.com/s/1hHnA-H_xOFiVEeIVi92A3Q?pwd=0k80 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享一篇关于AGI的短文:苦涩的教训

学习强化学习之父、加拿大计算机科学家理查德萨顿&#xff08; Richard S. Sutton &#xff09;2019年的经典文章《The Bitter Lesson&#xff08;苦涩的教训&#xff09;》。 文章指出&#xff0c;过去70年来AI研究走过的最大弯路&#xff0c;就是过于重视人类既有经验和知识&…

探究Android的多分辨率支持以及各种类型图标尺寸大小

术语和概念 屏幕尺寸 屏幕的物理尺寸&#xff0c;以屏幕的对角线长度作为依据&#xff08;比如 2.8寸&#xff0c; 3.5寸&#xff09;。 简而言之&#xff0c; Android把所有的屏幕尺寸简化为三大类&#xff1a;大&#xff0c;正常&#xff0c;和小。 程序可以针对这三种尺寸…

Docker部署nginx并且实现https访问

实验环境&#xff1a; 在已有的docker环境和nginx镜像的基础上进行操作 1、生成私钥 &#xff08;1&#xff09;openssl genrsa -out key.pem 2048 生成证书签名请求 (CSR) 并自签证书: &#xff08;2&#xff09;openssl req -new -x509 -key key.pem -out cert.pem -day…

DDD:根据maven的脚手架archetype生成ddd多模块项目目录结构

随着领域驱动的兴起&#xff0c;很多人都想学习如何进行ddd的项目开发&#xff0c;那ddd的项目结构是怎么样的&#xff1f;又是如何结合SpringBoot呢&#xff1f;那么针对这个问题&#xff0c;笔者使用maven的archetype封装一个相对通用的ddd的项目目录&#xff0c;方便一键生成…

karpathy Let‘s build GPT

1 introduction 按照karpathy的教程&#xff0c;一步步的完成transformer的构建&#xff0c;并在这个过程中&#xff0c;加深对transformer设计的理解。 karpathy推荐在进行网络设计的过程中&#xff0c;同时利用jupyter notebook进行快速测试和python进行主要的网络的构建。 …

STM32标准库SPI通信协议与W25Q64

目录 一、SPI通信 1.SPI通信简介 2.硬件电路 3.移位示意图 4.SPI基本时序图 &#xff08;1&#xff09;起始和终止 &#xff08;2&#xff09;交换一个字节 模式0&#xff1a; 模式1&#xff1a;​编辑 模式2&#xff1a;​编辑 模式3&#xff1a;​编辑 5.SPI时序 …

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

数据仓库实验三:分类规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、决策树分类规则挖掘&#xff08;1&#xff09;新建一个 Analysis Services 项目 jueceshu&#xff08;2&#xff09;建立数据源视图&#xff08;3&#xff09;建立挖掘结构 DST.dmm&#xff08;4&#xff…

43 单例模式

目录 1.什么是单例模式 2.什么是设计模式 3.特点 4.饿汉和懒汉 5.峨汉实现单例 6.懒汉实现单例 7.懒汉实现单例&#xff08;线程安全&#xff09; 8.STL容器是否线程安全 9.智能指针是否线程安全 10.其他常见的锁 11.读者写者问题 1. 什么是单例模式 单例模式是一种经典的&a…

线性数据结构-手写队列-哈希(散列)Hash

什么是hash散列&#xff1f; 哈希表的存在是为了解决能通过O(1)时间复杂度直接索引到指定元素。这是什么意思呢&#xff1f;通过我们使用数组存放元素&#xff0c;都是按照顺序存放的&#xff0c;当需要获取某个元素的时候&#xff0c;则需要对数组进行遍历&#xff0c;获取到指…

【skill】onedrive的烦人问题

Onedrive的迷惑行为 安装Onedrive&#xff0c;如果勾选了同步&#xff0c;会默认把当前用户的数个文件夹&#xff08;桌面、文档、图片、下载 等等&#xff09;移动到安装时提示的那个文件夹 查看其中的一个文件的路径&#xff1a; 这样一整&#xff0c;原来的文件收到严重影…

吴恩达2022机器学习专项课程C2(高级学习算法)W1(神经网络):2.1神经元与大脑

目录 神经网络1.初始动机*2.发展历史3.深度学习*4.应用历程 生物神经元1.基本功能2.神经元的互动方式3.信号传递与思维形成4.神经网络的形成 生物神经元简化1.生物神经元的结构2.信号传递过程3.生物学术语与人工神经网络 人工神经元*1.模型简化2.人工神经网络的构建3.计算和输入…

Java与Go: 生产者消费者模型

什么是生产者消费者模型 生产者-消费者模型&#xff08;也称为生产者-消费者问题&#xff09;是一种常见的并发编程模型&#xff0c;用于处理多线程或多进程之间的协同工作。该模型涉及两个主要角色&#xff1a;生产者和消费者&#xff0c;一个次要角色&#xff1a;缓冲区。 生…

18 内核开发-内核重点数据结构学习

课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中。 课程特点&#xff1a; 1. 入门级别&…

办公数据分析利器:Excel与Power Query透视功能

数据分析利器&#xff1a;Excel与Power Query透视功能 Excel透视表和Power Query透视功能是强大的数据分析工具&#xff0c;它们使用户能够从大量数据中提取有意义的信息和趋势&#xff0c;可用于汇总、分析和可视化大量数据。 本文通过示例演示Power Query透视功能的一个小技…

Linux专栏08:Linux基本指令之压缩解压缩指令

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux基本指令之压缩解压缩指令 编号&#xff1a;08 文章目录 Linu…

Spring Boot与OpenCV:融合机器学习的智能图像与视频处理平台

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

【模板】二维前缀和

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二维前缀和板题。 二维前缀和&#xff1a;pre[i][j]a[i][j]pre[i-1][j]pre[i][j-1]-pre[i-1][j-1]; 子矩阵 左上角为(x1,y1) 右下角(x2,y2…

PG控制文件的管理与重建

一.控制文件位置与大小 逻辑位置&#xff1a;pgpobal 表空间中 物理位置&#xff1a;$PGDATA/global/pg_control --pg_global表空间的物理位置就在$PGDATA/global文件夹下 物理大小&#xff1a;8K 二.存放的内容 1.数据库初始化的时候生成的永久化参数&#xff0c;无法更改…

brpc中http2 grpc协议解析

搭建gRPC服务 | bRPC https://blog.csdn.net/INGNIGHT/article/details/132657099 global.cpp http2_rpc_protocol.cpp ParseH2Message解析frame header信息 ParseResult H2Context::ConsumeFrameHead( 这个是固定长度的9字节帧头部&#xff0c;length是&#xff0c;3*8bit…