SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序wumi

773 阅读19分钟

download: SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序

SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 - SpringBoot快速上手 1、创建一个Maven工程 Maven工程 2、pom文件引入依赖 在maven工程项目的pom中引入

org.springframework.boot spring-boot-starter-parent 2.1.8.RELEASE org.springframework.boot spring-boot-starter-web 注解: (1)spring-boot-starter-parent作用 在pom.xml中引入spring-boot-start-parent,是依赖管理,引入以后在申明其它dependency的时候就不需要version了。

(2)spring-boot-starter-web作用 springweb 核心组件

3、编写HelloWorld服务 创建package命名为top.wfaceboss.api.service(根据实际情况修改) 创建HelloService类,内容如下

@RestController @EnableAutoConfiguration public class HelloService { @RequestMapping("/index") public String index() { return "Hello World"; } public static void main(String[] args) { SpringApplication.run(HelloService.class, args); } } 注解: (1)@RestController作用 在上加上RestController表示修饰该Controller所有的方法返回JSON格式,直接可以编写Restful接口

(2)@EnableAutoConfiguration作用

在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置; @EnableAutoConfiguration扫包范围:当前类 (3)SpringApplication.run(HelloController.class, args)作用 标识为启动类

4、SpringBoot启动 启动主程序,打开浏览器访问http://localhost:8080/index,可以看到页面输出Hello World

番外:其余两种启动方式及对比

(1)使用@ComponentScan注解

比如:@ComponentScan(basePackages = "top.wfaceboss.api.service")---控制器扫包范围

@ComponentScan(basePackages = "top.wfaceboss.api.service") @EnableAutoConfiguration public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } (2)使用@SpringBootApplication注解--推荐

@SpringBootApplication public class AppSpringBoot { public static void main(String[] args) { SpringApplication.run(AppSpringBoot.class, args); } } @SpringBootApplication原理 @SpringBootApplication 被 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解所修饰;在启动类上加上@SpringBootApplication注解,当前包下或者子包下所有的类都可以扫到.

@EnableAutoConfiguration注解、@ComponentScan注解、@SpringBootApplication注解对比

SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 - UniApp小程序项目开发

HbuilderX 安装配置完毕后,我们就可以开始开发了,首先我们需要创建一个项目,我们点击ide左上角 文件-> 新建 ->项目 ,然后我们选择 uniapp 项目 ,模板选择 默认模板

创建完成后,我们可以看到左侧文件树中新增了一个以项目名命名的文件,其中是hbuilder为我们内置的项目模板

以下是uniapp给我们的项目框架介绍,有一些文件夹是没有在模板中内置的,因此我们需要自己手动创建以下,例如最外层的components,用来放置我们的一些全局通用组件

┌─components 符合vue组件规范的uni-app组件目录 │ └─comp-a.vue 可复用的a组件 ├─pages 业务页面文件存放的目录 │ ├─index │ │ └─index.vue index页面 │ └─list │ └─list.vue list页面 ├─static 存放应用引用的本地静态资源(如图片、视频等)的目录,注意: 静态资源只能存放于此 ├─uni_modules 存放uni_module规范的插件。 ├─wxcomponents 存放小程序组件的目录,详见 ├─main.js Vue初始化入口文件 ├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期 ├─manifest.json 配置应用名称、appid、logo、版本等打包信息,详见 └─pages.json 配置页面路由、导航条、选项卡等页面类信息,详见

复制代码 如下图,在开发的过程中,我依据vue项目的开发习惯,在pages中依照业务功能来创建功能分类文件夹,最常见的是按照首页的tabbar来区分功能模块,每一个文件夹中存放该功能涉及的所有页面,且每一个功能文件夹中还有一个单独的components文件夹用于放置该仅功能文件夹中的页面依赖的组件。

新建页面 我们需要创建新页面的时候可以通过hbuilder内置的页面模板来快速创建,右键点击左侧文件树中当前的项目,选择 新建页面 ,输入页面名称以及选择模板就可以创建了,一般我选择的是scss的模板,创建完成后会自动帮你在page.json中注册该页面。

通用插件封装 既然uniapp选择使用vue.js作为开发框架,那么我们一定要利用上vue中的一些优秀特性,例如插件(plugin),关于vue插件的介绍大家可以直接去官网看。 vue 插件官方介绍链接 通过引入插件,我们可以极大的提升我们的开发效率,当然如果是第一次使用uniapp进行开发可能不清楚哪个功能适合封装成插件引入,下面我就介绍一下一些我在实际开发中封装的一些通用插件 在封装前我们需要写一个 config 文件,方便我们快速自定义一些颜色和请求路径等。 //congif.js const config = { baseUrl:'example.cn',//请求的基本路径 modalColor:'#5271FF', //弹窗颜色 }

module.exports = config 复制代码 弹窗插件 在小程序中,如果我们没有自定义弹窗和拟态框组件的话一般都是使用官方的showModal或者showToast api来进行一些用户交互。这种非常频繁使用到的操作非常适合封装起来快速调用。具体代码如下 插件代码 const config = require('../config.js')

var message = { toast(title, type = 'text') { if (title.length > 15) { console.error('toast长度超过15个字符,当前长度为' + title.length) return } var icon = 'none' if (type) { switch (type) { case 'text': icon = 'none' break case 'suc': icon = 'success' break case 'err': icon = 'error' break } } uni.showToast({ title, icon }) }, confirm(title, confirmColor) { return new Promise((res, rej) => { uni.showModal({ title, cancelColor: '#b6b6b6', confirmColor: confirmColor || config.modalColor, success: (result) => { if (result.cancel) { rej(result) } else if (result.confirm) { res(result) } }

}) }) }, async message(content, confrimText) { return new Promise((res) => { uni.showModal({ title: '提示', content, showCancel: false, confirmColor: config.modalColor, success: (result) => { res(result) } }) }) } } module.exports = message

复制代码 示例调用 this.message.toast('回答已删除') 复制代码 请求插件 在vue中我们常用的请求库是axios,几乎每一个开发者都会将axios进行一个二次封装,以减少调用时的代码量以及进行一些全局配置。在uni-app中我们无须引入第三方的请求库,直接使用官方提供的 uni.request(OBJECT) 这个api来进行请求的调用,当然我们也是要做一个二次封装的。具体代码如下 插件代码 //http.js const config = require('../config.js') const message = require('./message.js') var http = { post(path, params, contentType = 'form', otherUrl, ) { return new Promise((resolve, reject) => { var url = (otherUrl || config.baseUrl) + path if (!checkUrl(url)) { rej('请求失败') } uni.request({ method: 'POST', url, header: { "Content-Type": contentType === 'json' ? "application/json" : "application/x-www-form-urlencoded" }, data: params, success: (res) => { console.log('request:POST请求' + config.baseUrl + path + ' 成功', res.data) resolve(res.data) }, fail: (err) => { message.toast('请求失败', 'err') console.error('request:请求' + config.baseUrl + path + ' 失败', err) reject('请求失败') } }) }) }, put(path, params, contentType = 'form', otherUrl, ) { return new Promise((resolve, reject) => { var url = (otherUrl || config.baseUrl) + path if (!checkUrl(url)) { rej('请求失败') } uni.request({ method: 'PUT', url, header: { "Content-Type": contentType === 'json' ? "application/json" : "application/x-www-form-urlencoded" }, data: params, success: (res) => { console.log('request:PUT请求' + config.baseUrl + path + ' 成功', res.data) resolve(res.data) }, fail: (err) => { message.toast('请求失败', 'err') console.error('request:PUT请求' + config.baseUrl + path + ' 失败', err) reject('请求失败') } }) }) },

get(path, params, otherUrl) { return new Promise((resolve, reject) => { var url = (otherUrl || config.baseUrl) + path if (!checkUrl(url)) { return } uni.request({ url, data: params, success: (res) => { console.log('request:GET请求' + config.baseUrl + path + ' 成功', res.data) resolve(res.data) }, fail: (err) => { message.toast('请求失败', 'err') console.error('request:GET请求' + config.baseUrl + path + ' 失败', err) reject(err) } })

})

}, delete(path, params, otherUrl) { return new Promise((resolve, reject) => { var url = (otherUrl || config.baseUrl) + path if (!checkUrl(url)) { return } uni.request({ url, data: params, method: "DELETE", success: (res) => { console.log('request:DELETE请求' + config.baseUrl + path + ' 成功', res.data) resolve(res.data) }, fail: (err) => { message.toast('请求失败', 'err') console.error('request:DELETE请求' + config.baseUrl + path + ' 失败', err) reject(err) } })

})

},

async upload(path, fileArray, otherUrl) {

if (typeof fileArray !== 'object') { console.error('request:参数错误,请传入文件数组') return } var url = (otherUrl || config.baseUrl) + path if (!checkUrl(url)) { return } var arr = [] for (let i in fileArray) { const res = await uni.uploadFile({ url: otherUrl || config.baseUrl + path, filePath: fileArray[i], name: 'file' }) console.log(res) if (res[0]) { console.error('request:上传失败', res[0]) return } arr.push(JSON.parse(res[1].data).data) } return arr },

}

function checkUrl(url) { var urlReg = /^((ht|f)tps?)://[\w-]+(.[\w-]+)+([\w-.,@?^=%&:/+#]*[\w-@?^=%&/+#])?$/; if (!urlReg.test(url)) { console.error('request:请求路径错误' + url) return false } return true } module.exports = http

复制代码 示例调用 async getAnswer() { const res = await this.http.get('/applet/answerList', { qId: this.question.id, }) if (res.code === 200) { return res.data } else { this.message.toast('请求失败') return false } } 复制代码 在上面的代码中我们可以看到,我们引入两个依赖文件分别是config和message 这个在下面会说的,在http对象中有五个方法,分别是post,get,update,delete对应着 restful api 的增删查改操作,还有一个upload 方法用来上传文件。在小程序中请求我们不需要考虑跨域的问题,我们的默认请求路径是从 config这个依赖文件中获取的。而这时候我们还可以利用第一个 弹窗插件 快速的进行消息提示。 存储插件 在使用vue的时候我们常常会使用vuex + vuex-persistedstate来使我们的数据可以全局使用以及持久化,在小程序中我们有两种全局数据的存储方式 一种是globalData,通过在App.vue中添加globalData这个值来进行数据全局化,例如我们可以用来保存我们的小程序版本。 export default { globalData: { version: '1.0.0' } } 复制代码 但是globalData的数据不是持久化的,当我们退出小程序再进入的时候就重新初始化了,所以我们还有一种全局数据存储方式是storage,类比我们web端的localstroge,使用方式也几乎一样。通过官方提供的api实现本地存储的效果

在小程序中往往没有web端那么复杂的数据结构,虽然uniapp小程序也可以使用vuex作为一个全局数据管理的库,但我自己还是做了一个简化版的方便自己使用。这个插件是在很久以前写的了,只是非常简单的实现,如果想要省略setData操作可以用 proxy 劫持存储的值去存在本地。 插件代码 var store = { _init(){ if(uni.getStorageSync('store')==''){ uni.setStorageSync('store',{}) return } const data = uni.getStorageSync('store') for(let i in data){ if(!this[i]){ this[i] = data[i] } } }, setData(key,value){ if(key == '_init'||key=='setData'){ console.error('store:非法key值',key) return } this[key] = value uni.setStorageSync('store',this) console.log(uni.getStorageSync('store')) } }

module.exports = store

复制代码 示例使用 this.store.setData('user',{name:'oil'}) // 赋值 console.log(this.store.user) // 读值 复制代码 表单验证插件 表单验证是使用的是一个很轻量开源的表单验证库JSValidate,可以直接点击链接在仓库看源码,下面就不放代码了,讲一下如何使用。 JSValidate gitee链接 示例调用 // validate.js const validate = { userForm:{ intro: '@个人简介|require', college: '@学校|require!请选择你的学校', nickname:'@昵称|require' } }

module.exports = validate

复制代码 在表单中写入一个表单验证方法,如果条件不符合的话就使用弹窗进行提示 //form.js validateForm() { // 表单验证方法 const validate = require("./validate") const validator = new this.validator() var result = validator.check(validate.userForm, this.form, false) if (result !== true) { this.message.message(result) return false } return true }

复制代码 时间处理插件 关于时间处理插件我在项目中使用的也是一个开源的轻量级js库 day.js,具体的使用方式参考官方文档,day.js几乎包含了所有时间处理相关的操作,例如时间对比,时间转换等。 day.js官方文档链接 day.js其中有一项功能特别常用,就是一个相对时间转换的功能,相对时间转换就是将一个时间字符串例如年月日时分秒转换成几秒前,十分钟前,几个月前等等。使用的方式如下代码,我们使用vue的computed来进行时间字符串的处理,通过 闭包 向computed方法内传入时间字符串参数,然后通过day.js的formNow方法返回一个中文的相对时间。date就是我封装后引入的插件了。 示例代码 computed: { relativeDate() { return (date) => { return this.date(date).fromNow() } } } 复制代码 当我们拥有了很多个插件之后(如下图),我们的插件之间可能会共享一些配置,例如上文中的config.js,接下来我们可以写一个入口文件index.js用来将我们写的插件引入,然后在main.js中安装我们的插件。

示例代码 // index.js const http = require('./lib/http') const message = require('./lib/message') const router = require('./lib/router') const validator = require('./lib/validator') const date = require('./lib/date') const store = require('./lib/store') const to = require('./lib/to') const oil = { message, http, router, validator, date, store, to, install(Vue) { this.store._init() for (let i in this) { if (i == 'install') { continue } Vue.prototype[i] = this[i] }

delete this.install } }

export default oil 复制代码 // main.js import Vue from 'vue' import oilUni from './oil-uni/index.js' Vue.use(oilUni) app.$mount() 复制代码 我们在入门文件中定义了一个对象叫做oil,oil上除了我们写好的方法还有一个特殊的方法就是install ,install 的作用就是在我们使用Vue.use()的时候会将Vue的构造器传入install方法作为第一个参数,然后我们将所有方法都绑定在Vue的原型链上。 安装后,我们只需要在页面或组件的js中使用 this. + xx插件 就可以访问到封装的插件了。 常用组件封装 插件的作用是将我们的代码进行一个模块化,以便捷的复用一些常用的操作。而组件化则是帮助我们进行一个ui上的封装和复用。由于小程序的场景一般都不是很复杂,所以非常通用的组件也不多,下面只说说我在业务中常用的一些组件库和自己封装的组件 第三方组件库引入 colorUI 首先推荐的就是我几乎在所有小程序项目上都有用的 colorUI,与其说 colorUI 是一个组件库,不如说它是一个样式库。 colorUI github链接 在colorUI中封装了大量的行内样式,我们不需要频繁的在template和style间切换,大大的减少了我们的心智负担。可以类比 tailwindcss 这个样式库,但是在HBuilderX中,有着非常健全的colorUI样式的代码提示。其实不止是colorUI。你自己写的样式或者引用的文件在HBuilderX中都会获得很完整的一个代码提示。如下图

colorUI中并没有为我们提供功能上封装好的组件,但是在它的示例源码中,大部分场景都可以直接将样式和代码复制出来改造一下直接使用,例如图片上传和表单组件等。 colorUI示例页面 colorUI的引入也非常简单,将colorUI的包放在项目根目录下,然后在App.vue的style中引用就OK了,colorUI包含了三个文件,一个是main.css,里面是colorUI所有的样式代码,icon 是colorUI的图标,animation 是colorUI的动画,三个包的功能都是通过在 行内class 中写入指定代码来使用。

//App.vue

@import "colorui/main.css"; @import "colorui/icon.css"; @import "colorui/animation.css";

复制代码 vant-weapp vant几乎是vue生态在移动端个人认为最好用的组件库了,几乎所有场景的组件都有涵盖到,而vant有一个小程序专用的 weapp 版本。 vant weapp 官方文档 但 vant weapp 是用原生小程序的语法写的,和uniapp中的vue语法有些出入,所以引入的时候需要做一些特殊操作,下面说一下 vant weapp 如何引入。

首先需要下载vant weapp,将vant文件夹放在项目根目录下的wxcomponent文件夹里,没有就新建一个。

在App.vue中引入一下样式文件,

@import "/wxcomponents/vant/common/index.wxss";

复制代码

接下来我们就可以在页面中引入了,引入并不能直接在页面或组件中import,而是得按照小程序的使用方式在pages.json中引入,如下代码,在指定页面的 usingComponents 配置项中添加 组件名:组件路径 ,然后就可以直接在页面中使用了

{ "pages": [ { "path": "pages/home/home", "style": { "navigationBarTitleText": "", "enablePullDownRefresh": false, "usingComponents": { "van-progress": "/wxcomponents/vant/progress/index" } } } ] } 复制代码 要注意vant weapp官方文档中的语法是小程序的,自己转换成vue的就OK了 滚动容器组件 在引入以上两个组件库后,咱们开发时的大部分场景都可以在其中找到对应组件,顶多是进行一个二次封装以适配业务。 常见业务场景 登录页面 在小程序中,登录往往不像web端需要输入账号密码或者手机验证码登录,而是使用各个平台的快速鉴权功能,例如微信小程序就是可以通过向微信官方的api发送请求来获取用户信息。 获取用户信息 因此我们在设计页面的时候往往只需要非常简单的一个按钮来获取用户信息,下面我将用一个项目中的登录功能来给大家展示uniapp实现微信小程序登录获取用户信息的方法

toLocaleDateString()方法依赖于底层操作系统在格式化日期上。 例如,在美国,月份出现在日期(06/22/2018)之前,而在印度,日期出现在月份(22/06/2018)之前。

解决方案 使用new Date()构造函数来获取年月日后拼接

如果没有输入任何参数,则Date的构造器会依据系统设置的当前时间来创建一个Date对象。

Date和toLocaleDateString()的区别在于一个是获取系统当前设置的时间,一个则是底层操作系统来格式化时间 //具体代码如下 let date = new Date() date = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate() date = date.split('/') if (date[1] < 10) { date[1] = '0' + date[1] } if (date[2] < 10) { date[2] = '0' + date[2] } date = date.join('-') 复制代码 自动全局引入组件 功能介绍 如果大家有用过vue2开发项目,就知道在vue2中可以通过webpack的api进行自动全局组件引入,不需要在页面中一个一个手动引入,如下代码 const requireComponent = require.context("@/components", true, /.vue/); // 通过webpack获取conponents目录下所有组件 const global = { install(app) { const components = requireComponent.keys(); // 获得组件数组 for (let component of components) { let componentName = component.replace(/(.*\/)*([^.]+).*/gi, "2"); // 获得组件名称 app.component(componentName, requireComponent(component).default); // 将组件挂载到全局 } }, }; export default global;

复制代码 将global.js放在components的同级目录,然后在 main.js 中引入并使用 Vue.use 来使用插件就OK了,这个方法就可以帮我们自动将组件挂载到全局。 注意点 在uniapp中这个上面这个方法是不能使用的,我们在上面的代码中可以看到这一行 app.component(componentName, requireComponent(component).default); 在uniapp中 app.component 这个方法是不能传入变量作为组件名的,只能直接传入字符串,因此我们就没法使用它来自动引入组件啦。

SpringBoot2.X+Vue+UniAPP,全栈开发医疗小程序 - Vue3.0+ElementPlus实现系统登录 因为做了一些小 Demo,熟悉了之后就开始尝试写一些大一点儿的实战项目,而开发背景当然就是 Vue 3.0 正式发版和 element plus 的正式发版,时间点分别是 2020 年的 9 月份和 2020 年的 11 月份。 项目的名字想了很久也没想好,先叫它 vue3-admin 吧,毕竟是一个后台管理系统。当然,就当做是一个练手的项目,所有代码都是开源免费的,供大家学习使用。

项目开发过程 项目 vue3-admin 的开发时间应该是在 2020 年的 12 月份,当时看到 @iamkun 大佬发了一篇文章《🎉 Element UI for Vue 3.0 来了!》,文章里有提到 element plus 正式发版,就想着用它来重构之前的后台管理系统。 不过这次花的时间有点久,到今天为止已经差不多 4 个月了,刚开发完,还在测试阶段。因为我的时间比较碎,平时要正常上班、周末还要写书、写文章什么的,所以只能在其它事情都忙完、空下来的时候才能写一点点代码。 首先是开发后端 API 接口,建表、开发接口、自测.......大概花了半个月时间,把第一版的 API 开发出来。不过,也只是开发出来而已,因为后面联调接口的时候还是做了很多调整的,参数不完整、接收方式不对、返回的格式调整等等。 之后是开发页面、联调接口,这个时间花的就比较多了,搭环境、画页面、调整页面布局、找 icon 素材等等,这些都是费心费时的事情。 比如登录页面,一开始画成了这样:

第一天看的时候还行,第二天再看的时候就觉得有点不协调,而且背景图加载的过程有点儿影响体验,于是把背景改成纯色:

看起来还是不协调,主要是色调吧,各种颜色都有,黑的、白的、蓝的、青的,还得换,logo 图片也不好看。调整过程就不啰嗦了,最终的登录页面是下面这样:

删除了背景色,修改了 logo 图片和字体颜色,整体上看起来舒服了很多。 开发过程中好玩儿的事情还是有很多的,除此之外,还有三级联动功能实现、列表功能实现、弹框、图片上传、富文本编辑器整合等等,整个项目就是这样一点一点完成的,从第一行代码,到第一个组件,到完成一个页面,再到完成一个功能模块。现在想一想,其实全部都是笨功夫,静下心来才能慢慢做好的事情。还好,我这个人比较笨,就一点一点做呗。差不多 11 个页面组件 + 5 个公用组件,花了几个月时间,也做完了,继续努力吧。 还有一些问题,周末测试并集中修改。 项目开发进度 目前,开发的部分已经完成,主要是测试,然后修改和完善一些细节。 3月初,自己测了几遍,然后改了一些东西,印象比较深的是如下几个问题:

翻到下一页时,显示的图片是上一页的图片

商品图片上传后的宽高问题

输入信息限制

部分输入框需要限制为只能输入数字,不能输入字符。 部分输入框需要限制表情字符的输入。

页面中出现了错别字

编辑商品时,图片回显失败的问题

自测的时候,已经修改了不少的样式和显示问题,也修复了一些 bug。不过一个人测肯定避免不了会忽视一些问题,毕竟是自己写的代码,测试起来不会特别的全面,所以近几天会找些朋友帮忙测试一下。