# sxdx **Repository Path**: hanweiwn/sydx ## Basic Information - **Project Name**: sxdx - **Description**: 石油大学 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-08 - **Last Updated**: 2026-06-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 鸿蒙开发 软件最重要的事什么? ``` 不要关注API(别人写好给我用的组件),不重复造轮子。 关注流程,关注思想。关注设计模式,关注算法。 我知道一个软件是怎么写出来的(写的细节完全可以参考AI)。 ``` 鸿蒙的项目我会从头到尾开发。最后一天:答辩。 **你们需要答辩和提交产出物。** (演示你们做的项目,和回答问题。和提交相关文档(模版)) 6.9任务: 数据库给我装上 测试sql语句 从git下载我的代码,在你的电脑上跑起来。 把我的代码,改一改。 ### 今天任务: 分小组(不超过5个。项目立项:关于鸿蒙的什么项目)。 明天早上提交:小组名单和项目名称。我做的项目是 医院挂号系统。 DevEco Studio安装 把我的代码跑一边,看看出来是什么样子的。 ### 环境: 笔记工具 ``` 通过网盘分享的文件:typora-setup-x64.exe 链接: https://pan.baidu.com/s/1WBHVw0B_OrYp5GwQ6DKmgQ?pwd=evsv 提取码: evsv 复制这段内容后打开百度网盘手机App,操作更方便哦 ``` 前端开发: ``` DevEco Studio (自己去下载)(今天就下载) https://developer.huawei.com/consumer/cn/download/ 是鸿蒙的开发的IDE。一路next,建议:不要装在有中文,有空格的目录下。 (注意:如果你过去的电脑装过nodejs,要看版本是否大于18.如果小于18.请删除掉。删的比较干净) 鸿蒙默认装的就是nodejs18版本。 ``` 后端服务 一路next ``` jdk java开发环境工具包 通过网盘分享的文件:jdk-17.0.12_windows-x64_bin.exe 链接: https://pan.baidu.com/s/1DWb6QQ1w2ve3i0RNFVyu0g?pwd=fqcc 提取码: fqcc 复制这段内容后打开百度网盘手机App,操作更方便哦 版本需要大于等于17. 后端开发: idea: java的集成开发平台 通过网盘分享的文件: 链接: https://pan.baidu.com/s/13vCMgYVDYJygRvUkVy40ug?pwd=nncz 提取码: nncz 复制这段内容后打开百度网盘手机 App,操作更方便哦 ``` 数据库 一路next ``` 通过网盘分享的文件:mysql-8.0.21-winx64.zip 链接: https://pan.baidu.com/s/1X31H7osZKYRRy_GZnocUIg?pwd=76ab 提取码: 76ab 复制这段内容后打开百度网盘手机App,操作更方便哦 数据库链接的终端。用它能很方便的操作数据库。 通过网盘分享的文件:Navicat170.rar 链接: https://pan.baidu.com/s/1acruVrVXrVPElKVcWmAPrg?pwd=ytw8 提取码: ytw8 复制这段内容后打开百度网盘手机App,操作更方便哦 ``` ### Mysql安装: 解压 \1. 将刚才下载下来的压缩包解压到相应盘符 我这里是解压到 D:/soft 中 ![image-20260616162102216](images/image-20260616162102216.png) \2. 在 mysql-8.0.xx 文件夹下创建 my.ini 文件 image-20260616162116781 \3. 编辑刚新建的 my.ini 文件 ``` [mysqld] # 设置3306端口 port=3306 # 设置mysql的安装目录,切记此处一定要用双斜杠\\,单斜杠这里会出错。 basedir=D:\\soft\\mysql-8.0.21-winx64 # 设置mysql数据库的数据的存放目录 datadir=D:\\soft\\mysql-8.0.21-winx64\\data # 允许最大连接数 max_connections=1000 # 允许连接失败的次数。这是为了防止有人从该主机试图攻击数据库系统 max_connect_errors=10 # 服务端使用的字符集默认为UTF8 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB # 默认使用“mysql_native_password”插件认证 default_authentication_plugin=mysql_native_password [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [client] # 设置mysql客户端连接服务端时默认使用的端口 port=3306 default-character-set=utf8 ``` 安装 剩下操作都是在 cmd 中完成的,需要以管理员身份打开 cmd 窗口。 1. 打开 cmd 。 ![image-20260616162202216](images/image-20260616162202216.png) 2. 切换目录。 image-20260616162232419 3. 安装 mysql 。 在打开的MySQL的bin目录中,输入命令:mysqld --initialize --console ,完成MySQL的初始化。 ``` mysqld --initialize --console ``` ![image-20260616162252586](images/image-20260616162252586.png) 4. 创建服务。 初始化MySQL后,输入命令:mysqld --install,完成MySQL服务的安装。 ``` mysqld --install ``` ![image-20260616162306078](images/image-20260616162306078.png) 如果执行后提示,mysql已经存在,输入以下命令删除原有服务。 ``` sc delete mysql ``` 5. 启动服务。 ``` net start mysql ``` 6. 登录。 ``` mysql -u root -p ``` 7. 修改密码。 ``` ALTER USER 'root'@'localhost' IDENTIFIED BY 'ilovelaohan'; ``` 其中 ilovelaohan是你的新密码。 ## 鸿蒙 ``` https://developer.huawei.com/consumer/cn/training/result?courseType=5 官方的学习地址 学完之后就可以考一个鸿蒙的初级开发认证。 官网考试。免费考。官网报名,在线考试。 ``` ### DevEco Studio ![image-20260608101441780](images/image-20260608101441780.png) ### ArkTS语法 ```ts //此处可以写arkts语法 // 基础变量和类型 // let username:string = 'zsf' //声明一个变量 名称为username 类型为string 值为'zsf' // let age:number = 18 // let sex:boolean = true // console.log('username is ',username) // console.log('age is ',age) // console.log('sex is ',sex) //数组 // let students:string[] = ['张三丰','刘德华','张曼玉'] // console.log('stutents is ',students); // console.log('第二个学生是',students[1]); // 函数 // function printstar(){ // console.log('星星'); // console.log('星星'); // console.log('星星'); // } // printstar() // printstar() // printstar() //带参数的函数 // function buy(price:number,count:number){ // let result = price*count // return result // } // let result = buy(58,3) // console.log('results is ',result); //箭头函数应该是今后用的最多的 属于函数的简写 //let 函数名 = ()=>{函数体} // let buy2 = ()=>{ // console.log('这是一个箭头函数被调用了'); // } // buy2() //对象 //作用:描述一个物体的特征和行为 // 必须通过interface接口进行约定结构和类型 //定义接口 //今后使用它作为一个数据的载体,或者是传输的对象 //数据从数据库来,传递给前端,就要用这个 // interface Person{ // name:string // age:number // weight:number // } // //基于接口,定义对象 // let xiaoren:Person={ // name:'秦桧', // age: 39, // weight:380 // } // let daren:Person={ // name:'岳飞', // age: 2000, // weight:160 // } // console.log('xiaorenis',xiaoren.name,xiaoren.age,xiaoren.weight); // console.log('daren',daren.name,daren.age,daren.weight); //对象是可以有用方法的 // interface Person{ // eat:()=>void // drink:(what:string)=>void // } // let xiaoren:Person={ // eat:():void=>{ // console.log('小人吃石头'); // }, // drink:(what:string):void=>{ // console.log('小人喝',what); // } // } // xiaoren.eat() // xiaoren.drink('雪碧') //联合类型 //灵活的类型,存储不同的类型 // let judge:number|string = 100 // judge = 'zsf' // console.log('judge',judge); // judge = 250 // console.log('judge',judge); // //枚举类型 // 变量的值就必须在一定的范围内 //你们数据库性别用 char(2) varchara(2) enum bit //男 女 enum Sex{ man = '男', woman = '女' } let yousex:Sex = Sex.man console.log('you sex is',yousex); ``` ### ArkUI界面开发 #### 第一个 ```typescript @Entry //标记当前组件为入口组件,是一个页面的起点 @Component //标记是一个自定义组件,是一个可以复用的UI单元 struct test1 { build() { //组件描述ui的方法,内部使用声明式语法进行描述 //子组件 一个build 只能有一个根节点 Column(){ Text('石油大学饭一般').fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(Color.Green) }.width('100%').height('100%') } } ``` 新建模拟器 ![image-20260608112525106](images/image-20260608112525106.png) 如果你的 ![image-20260608112627538](images/image-20260608112627538.png) ``` https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-emulator-faqs#section214296171615 ``` #### 界面2 ```ts @Entry //标记当前组件为入口组件,是一个页面的起点 @Component //标记是一个自定义组件,是一个可以复用的UI单元 struct test1 { //当这个值被改变的时候 会自动出发ui界面的改变 @State message:string = '石油大学的饭好吃的很' build() { //组件描述ui的方法,内部使用声明式语法进行描述 //子组件 一个build 只能有一个根节点 Column(){ Text(this.message).fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(Color.Green) Button('点我改变').onClick(()=>{ this.message = '石油大学的🍚一点都不好吃' }) }.width('100%').height('100%') } } ``` #### 界面3 ![image-20260608142454010](images/image-20260608142454010.png) ```ts @Entry @Component struct test2 { build() { Column(){ //build下面只能有一个根节点 Text('小说介绍').backgroundColor(Color.Gray).width('100%') .fontSize(20).height(40) .fontWeight(900) //100-900之间的数字 加粗700 默认是400 Row(){ Text('都市').height(30).width('25%').backgroundColor(Color.Yellow) Text('情感').height(30).width('25%').backgroundColor(Color.Blue) Text('神话').height(30).width('25%').backgroundColor(Color.Brown) Text('白领').height(30).width('25%').backgroundColor(Color.Orange) } }.width('100%') .backgroundColor(Color.Pink) } } ``` #### 界面4 ![image-20260608143548790](images/image-20260608143548790.png) ```ts @Entry @Component struct test3 { build() { Column(){ Text('石油大学的饭太好了').height(40).fontSize(24) .fontWeight(FontWeight.Bold) .width('100%') Text('石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。') .lineHeight(24).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(2) Row(){ Text('置顶 ').fontColor('#0275fe') Text('凤凰网 ').fontColor('#a1a1a1') Text('9876评论 ').fontColor('#b1a1a2') }.width('100%').margin({top:5,left:5}) }.width('100%') } } ``` #### 界面5 ![image-20260608144421906](images/image-20260608144421906.png) ```ts @Entry @Component struct test3 { build() { Column(){ Image('https://news-bos.cdn.bcebos.com/mvideo/log-news.png') .width('50%').margin({bottom:10}) Text('石油大学的饭太好了').height(40).fontSize(24) .fontWeight(FontWeight.Bold) .width('100%') Text('石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。石油大学的饭在一个风和日丽的下午,我们哦度吃了,非常好吃。') .lineHeight(24).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(2) Row(){ Text('置顶 ').fontColor('#0275fe') Text('凤凰网 ').fontColor('#a1a1a1') Text('9876评论 ').fontColor('#b1a1a2') }.width('100%').margin({top:5,left:5}) Row(){ Image($r('app.media.11')) .width('100%').height('15%') }.margin({top:10}) }.width('100%') } } ``` #### 图片 使用图片有三种方式: 1:网络图片 ``` Image('https://news-bos.cdn.bcebos.com/mvideo/log-news.png') .width('50%').margin({bottom:10}) ``` 2:本地文件夹图片 不推荐,因为不能够跨设备 ``` Image('images/1.jpg') .width('50%').margin({bottom:10}) ``` 3:本地media图片 图片必须放在resources/base/media目录下,这是推荐的方式,能够支持跨设备 ``` Image($r('app.media.11')) .width('100%').height('15%') ``` 4:svg 矢量图,可以任意放大缩小,不失真,还可以更改颜色。 来源 ``` https://developer.huawei.com/consumer/cn/design/harmonyos-icon/ https://www.iconfont.cn/ ``` #### 登录 image-20260608150859147 ```ts @Entry @Component struct Index { build() { Column({space:15}){ Image($r('app.media.huawei')).width(60) TextInput({ placeholder:'请输入用户名' }) TextInput({ placeholder:'请输入密码' }) Button('登录').width('100%') Row({space:20}){ Image($r('app.media.zfb_tab_me')).width(20) .fillColor(Color.Red) Text('前往注册') Image($r('app.media.ic_more')).width(20) .fillColor(Color.Green) Text('忘记密码') } }.padding(20) } } ``` #### 边距: margin: 外边距 padding: 内边距 image-20260608151506551 ```ts @Entry @Component struct Index { build() { Column({space:15}){ Image($r('app.media.huawei')).width(60) Text('石油医院挂号系统').fontSize(28).fontWeight(900) TextInput({ placeholder:'请输入用户名' }) TextInput({ placeholder:'请输入密码' }) Button('登录').width('100%') Row(){ Image($r('app.media.zfb_tab_me')).width(20) .fillColor(Color.Red).margin({right:10}) Text('前往注册') .margin({right:50}) Image($r('app.media.ic_more')).width(20) .fillColor(Color.Green) Text('忘记密码').margin({left:10}) } }.padding(20) } } ``` #### 线性布局 ![image-20260608155757736](images/image-20260608155757736.png) ``` @Entry @Component struct test4 { build() { //线性布局就能够自动适配适应你设备的大小 Column(){ Text().width(200).height(100).backgroundColor(Color.Pink).border({width:2}) Text().width(200).height(100).backgroundColor(Color.Yellow).border({width:2}) Text().width(200).height(100).backgroundColor(Color.Green).border({width:2}) Text().width(200).height(100).backgroundColor(Color.Orange).border({width:2}) }.width('100%') .height('100%') // .alignItems(HorizontalAlign.End) //最右边 // .alignItems(HorizontalAlign.Start) //最左边 .alignItems(HorizontalAlign.Center) //最左边 // .justifyContent(FlexAlign.SpaceAround) //间隙环绕 1 1 1 1 // .justifyContent(FlexAlign.SpaceBetween) //间隙环绕 0.5 1 1 1 0.5 // .justifyContent(FlexAlign.Start) // 主轴 用了线性布局将来是可以跨设备的 .justifyContent(FlexAlign.End) // 底部 .justifyContent(FlexAlign.Center) // 中间 } } ``` ![image-20260608160008128](images/image-20260608160008128.png) ``` @Entry @Component struct test4 { build() { //线性布局就能够自动适配适应你设备的大小 Row(){ Text().width(60).height(100).backgroundColor(Color.Pink).border({width:2}) Text().width(60).height(100).backgroundColor(Color.Yellow).border({width:2}) Text().width(60).height(100).backgroundColor(Color.Green).border({width:2}) Text().width(60).height(100).backgroundColor(Color.Orange).border({width:2}) }.width('100%') .height('100%') .justifyContent(FlexAlign.SpaceAround) .alignItems(VerticalAlign.Center) } } ``` ##### 个人中心 ![image-20260609085556659](images/image-20260609085556659.png) ```ts @Entry @Component struct my { build() { //上下两部分 //上面 左边箭头 个人中心 右边 更多的图标 //下面 个人的详细信息 挂号信息 Column(){ Row(){ Image($r('app.media.ic_arrow_left')).width(30) Text('个人中心') Image($r('app.media.ic_more')).width(24) }.width('100%') .backgroundColor('#ccc') .height(40) .justifyContent(FlexAlign.SpaceBetween) .padding({left:10,right:10}) Column(){ Text('将来你的个人信息').margin({top:10}) }.backgroundColor(Color.White).width('100%').height('100%') }.width('100%') .height('100%') .backgroundColor('#ccc') .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM]) } } ``` #### 自适应伸缩布局 image-20260609085807375 设置layoutWeight属性的子元素与兄弟元素会按照权重进行分配主轴的剩余空间。 ![image-20260609090547734](images/image-20260609090547734.png) ```ts @Entry @Component struct test5 { build() { Column(){ Row(){ Text('左侧').backgroundColor(Color.Gray).height(40).layoutWeight(1) Text('右侧').backgroundColor(Color.Brown).height(40).width(80) }.width(300) .height(40).backgroundColor('#fff') Row(){ Text('左侧').backgroundColor(Color.Gray).height(40).layoutWeight(1) Text('中间').backgroundColor(Color.Orange).height(40).layoutWeight(2) Text('右侧').backgroundColor(Color.Brown).height(40).layoutWeight(3) }.width(300) .height(40).backgroundColor('#fff').margin({top:20}) }.width('100%') .height('100%') } } ``` #### 弹性布局 ![image-20260609091321326](images/image-20260609091321326.png) ```ts @Entry @Component struct test6 { build() { Column(){ Text('专业选择').fontSize(30).fontWeight(700) Flex({wrap:FlexWrap.Wrap}){ //弹性布局 Text('石油').margin(5).padding(10).backgroundColor('#f1f1f1') Text('管道').margin(5).padding(10).backgroundColor('#f1f1f1') Text('爆破').margin(5).padding(10).backgroundColor('#f1f1f1') Text('化工').margin(5).padding(10).backgroundColor('#f1f1f1') Text('物理').margin(5).padding(10).backgroundColor('#f1f1f1') Text('信息科学').margin(5).padding(10).backgroundColor('#f1f1f1') Text('化工').margin(5).padding(10).backgroundColor('#f1f1f1') Text('物理').margin(5).padding(10).backgroundColor('#f1f1f1') Text('信息科学').margin(5).padding(10).backgroundColor('#f1f1f1') } }.width('100%').height('100%') } } ``` #### 绝对定位 可以实现层叠效果,对整个手机屏幕的x,y轴而言。 右上角数字 ![image-20260609093628669](images/image-20260609093628669.png) ```ts @Entry @Component struct test7 { build() { Column(){ Image($r('app.media.bz_msg')).width(50) Text('8').fontColor(Color.Red).fontWeight(FontWeight.Bold) .fontSize(30) .position({ x:195, y: -15 }) }.width('100%').height('100%') } } ``` #### 层叠布局 比绝对定位要方便,根据当前容器元素的位置而言。 ![image-20260609094213853](images/image-20260609094213853.png) ``` @Entry @Component struct test8 { build() { Column(){ Stack({ alignContent: Alignment.Center }){ Text('老大').backgroundColor(Color.Yellow).width(250).height(250) Text('老二').backgroundColor(Color.Blue).width(150).height(150) Text('老三').backgroundColor(Color.Green).width(50).height(50) } }.width('100%').height('100%') } } ``` ![image-20260609094504533](images/image-20260609094504533.png) ``` @Entry @Component struct test10 { build() { Column(){ Stack({ alignContent:Alignment.TopEnd }){ Image($r('app.media.bz_msg')).width(50) Text('8').fontColor(Color.Red).fontWeight(FontWeight.Bold) .fontSize(30) .margin({top:-12,right:-8}) } }.width('100%').height('100%') } } ``` ### 事件 #### 计数器 ![image-20260609095503287](images/image-20260609095503287.png) ``` @Entry @Component struct test11 { @State count:number = 0 build() { Row(){ Button("-").onClick(()=>{ this.count-- }) Text(this.count.toString()).margin(10) Button("+").onClick(()=>{ this.count++ }) } } } ``` #### 购物车 ![image-20260609102224694](images/image-20260609102224694.png) ``` @Entry @Component struct carts { @State count:number = 1 @State price:number = 21.40 build() { Column(){ //产品 购物车的内容 Row({space:5}){ Image($r('app.media.product1')).width(100).borderRadius(8) //文字 Column({space: 6}){ Text('冲销量100W好吃饭').lineHeight(20).fontSize(14) Text('一折折扣商品').lineHeight(20).fontSize(12).fontColor('#7f7f7f') Row({space:30}){ Text('¥'+this.price) Row({space:10}){ Text('-').width(22).height(22).textAlign(TextAlign.Center) .border({width:1,color:'#e1e1e1',radius: {topRight:5,bottomRight:5}}) .onClick(()=>{ this.count-- }) Text(this.count.toString()) Text('+').width(22).height(22).textAlign(TextAlign.Center) .fontWeight(700) .border({width:1,color:'#e1e1e1',radius: {topRight:5,bottomRight:5}}) .onClick(()=>{ this.count++ }) } } }.alignItems(HorizontalAlign.Start) .width('80%').margin({left:10}) }.width('100%').height(100) //结算 Row(){ Column(){ Text(`已选择${this.count}件商品`) Text('合计:'+(this.count*this.price).toFixed(2)) } }.width('100%').height(700) }.width('100%').height('100%') .backgroundColor('#f3f3f3') } } ``` ### 新闻列表 ![image-20260609103123821](images/image-20260609103123821.png) ``` @Entry @Component struct test12 { @State news:string[] = [ '石油大学的饭吃出来金砖', '石油大学的饭吃出来金条', '石油大学的饭特表好吃', '今天天气真正好', '黄蓉来到桃花岛', '石油大学的饭吃出来金砖' ] build() { Column(){ ForEach(this.news,(item:string,index:number)=>{ Text(index+item).fontSize(24).fontWeight(700) .padding(8) .width('100%') }) }.width('100%').height('100%') } } ``` #### 新闻列表2 数据是从后端传过来的,前后段的交互数据总是json。10年前总是xml。 不管你后端是Java,Python,.Net。Go还是任何其他语言(把数据封装成对象)-----后端需要把数据打成json给前端 ------数据交互的载体就是JSON-------------- 前端:不管你是vue,react,html,鸿蒙还是大屏终端。拿到的就是JSON。(拿到JSON后,可能需要转换为自己的对象) 不管是百度地图,短信接口,身份证验证三要素。DeepSeek大模型交互的数据接口都是JSON。 #### JSON 就是个文本。 ``` { 'username':'zsf', 'password':'000000', 'sex':'female' } [ { 'username':'zsf', 'password':'000000', 'sex':'female' }, { 'username':'zsf', 'password':'000000', 'sex':'female' }, { 'username':'zsf', 'password':'000000', 'sex':'female' } ] ``` ![image-20260609143833291](images/image-20260609143833291.png) ``` //将来数据可能来自于后台 //接口和对象 interface News{ title:string, createTime:string } @Entry @Component struct test13 { @State message:string = '我不测测' //声明一个包含了所有数据的载体 @State news:News[]=[ { title:'石油大学于明天食堂全部免费开发,随便吃', createTime:'2026-06-09 12:21:21' },{ title:'石油大学于明天商店全部免费开发,随便吃', createTime:'2026-06-09 12:21:21' },{ title:'石油大学于明天教室全部免费开发,随便吃', createTime:'2026-06-09 12:21:21' } ] build() { Column(){ Column(){ ForEach(this.news,(item:News,index:number)=>{ Text(item.title) .width('100%').fontSize(15).fontColor('#303030') .lineHeight(20) Text(item.createTime).fontSize(12) .fontColor(Color.Gray) .margin({top:10}) }) }.margin({left:5}) }.width('100%').height('100%') } } ``` ### 药品管理 #### 多列的布局方式 ![image-20260609145101465](images/image-20260609145101465.png) ``` @Entry @Component struct medic { build() { Column(){ //标题 Row(){ Text('便民药品').fontSize(20) .fontWeight(FontWeight.Bold) } //商品列表要有一个循环,而且能够向下拖动 List(){ //列表组件 能够向上向下拖动 ForEach(Array.from({length:10}),()=>{ ListItem(){ Column(){ Text('hello').height(40).width('100%') .backgroundColor('#ccc').borderRadius(12).padding(6) }.width('90%').margin({top:5}) } }) }.listDirection(Axis.Vertical) .lanes(2) //2列 .width('100%') .padding(8) .layoutWeight(1) }.width('100%') .height('100%') .backgroundColor('#ffffff') } } ``` #### 便民药品1 image-20260609151704351 ``` @Entry @Component struct medic { build() { Column(){ //标题 Row(){ Text('便民药品').fontSize(20) .fontWeight(FontWeight.Bold) } //商品列表要有一个循环,而且能够向下拖动 List(){ //列表组件 能够向上向下拖动 ForEach(Array.from({length:10}),()=>{ ListItem(){ Column(){ Image($r('app.media.medic01')) .width('100%') .borderRadius(8) //商品信息 Column(){ //第一行 商品名称 Row({space:10}){ Text('布洛芬胶囊').fontSize(12).fontWeight(300) .maxLines(1) Text('¥36.78').fontSize(10).fontColor('#ff4d4f') .fontWeight(FontWeight.Bold) } //第一行 评论数量+购物车 Row({space:10}){ Text('3421条评论').fontSize(12) .fontColor('#999') Image($r('app.media.gouwu')).width(20).height(20) .fillColor('#333') } } }.width('90%').margin({top:5}) } }) }.listDirection(Axis.Vertical) .lanes(2) //2列 .width('100%') .padding(8) .layoutWeight(1) }.width('100%') .height('100%') .backgroundColor('#ffffff') } } ``` #### 便民药品2 image-20260609152929191 ```ts //定义药品的接口 interface Medic{ id:number, image: ResourceStr, goodname:string, price: number, comments:number } @Entry @Component struct medic { //声明药品的数据 @State medics: Medic[] = [ { id: 1, image: $r('app.media.medic01'), goodname: '阿莫西林胶囊', price: 28.5, comments: 2345 }, { id: 2, image: $r('app.media.medic02'), goodname: '布洛芬缓释胶囊', price: 15.8, comments: 1890 }, { id: 3, image: $r('app.media.medic03'), goodname: '维生素C片', price: 32.0, comments: 3200 }, { id: 4, image: $r('app.media.medic04'), goodname: '创可贴', price: 9.9, comments: 1567 }, { id: 5, image: $r('app.media.medic05'), goodname: '板蓝根颗粒', price: 18.5, comments: 2789 }, { id: 6, image: $r('app.media.medic06'), goodname: '感冒灵颗粒', price: 22.0, comments: 3456 }, { id: 7, image: $r('app.media.medic07'), goodname: '头孢克肟片', price: 35.0, comments: 1234 }, { id: 8, image: $r('app.media.medic08'), goodname: '止咳糖浆', price: 26.5, comments: 2100 }, { id: 9, image: $r('app.media.medic09'), goodname: '蒙脱石散', price: 19.8, comments: 1678 }, { id: 10, image: $r('app.media.medic10'), goodname: '健胃消食片', price: 12.5, comments: 2987 }, { id: 11, image: $r('app.media.medic11'), goodname: '氯雷他定片', price: 24.0, comments: 1432 }, { id: 12, image: $r('app.media.medic12'), goodname: '红霉素软膏', price: 8.5, comments: 3541 }, { id: 13, image: $r('app.media.medic13'), goodname: '云南白药气雾剂', price: 68.0, comments: 987 }, { id: 14, image: $r('app.media.medic14'), goodname: '藿香正气水', price: 15.0, comments: 2654 }, { id: 15, image: $r('app.media.medic15'), goodname: '六味地黄丸', price: 45.0, comments: 1876 }, { id: 16, image: $r('app.media.medic16'), goodname: '复方丹参滴丸', price: 38.5, comments: 1543 }, { id: 17, image: $r('app.media.medic17'), goodname: '葡萄糖酸钙口服液', price: 42.0, comments: 2234 }, { id: 18, image: $r('app.media.medic18'), goodname: '维生素B族片', price: 55.0, comments: 1765 }, { id: 19, image: $r('app.media.medic19'), goodname: '伊曲康唑胶囊', price: 88.0, comments: 654 }, { id: 20, image: $r('app.media.medic20'), goodname: '奥美拉唑肠溶片', price: 36.0, comments: 2456 }, { id: 21, image: $r('app.media.medic21'), goodname: '硝酸甘油片', price: 29.0, comments: 1321 }, { id: 22, image: $r('app.media.medic22'), goodname: '安眠药片', price: 56.0, comments: 998 }, { id: 23, image: $r('app.media.medic23'), goodname: '小儿氨酚黄那敏颗粒', price: 23.5, comments: 2876 }, { id: 24, image: $r('app.media.medic24'), goodname: '开塞露', price: 6.5, comments: 4123 }, { id: 25, image: $r('app.media.medic25'), goodname: '眼药水', price: 19.0, comments: 3450 }, { id: 26, image: $r('app.media.medic26'), goodname: '钙尔奇D片', price: 128.0, comments: 789 } ]; build() { Column(){ //标题 Row(){ Text('便民药品').fontSize(20) .fontWeight(FontWeight.Bold) } //商品列表要有一个循环,而且能够向下拖动 List(){ //列表组件 能够向上向下拖动 //循环的事上面的那个数组 ForEach(this.medics,(medic:Medic)=>{//ForEach(Array.from({length:10}),()=>{ ListItem(){ Column(){ Image(medic.image) .width('100%') .borderRadius(8) //商品信息 Column(){ //第一行 商品名称 Row({space:10}){ Text(medic.goodname).fontSize(16).fontWeight(300) .maxLines(1) Text(`¥${medic.price}`).fontSize(10).fontColor('#ff4d4f') .fontWeight(FontWeight.Bold) } //第一行 评论数量+购物车 Row({space:10}){ Text(`${medic.comments}条评论`).fontSize(12) .fontColor('#999') Image($r('app.media.gouwu')).width(20).height(20) .fillColor('#333') } } }.width('90%').margin({top:5}) } }) }.listDirection(Axis.Vertical) .lanes(2) //2列 .width('100%') .padding(8) .layoutWeight(1) }.width('100%') .height('100%') .backgroundColor('#ffffff') } } ``` ## 数据库 装 ``` Mysql装解压缩的版本,已经放在群里。 安装步骤: http://blog.csdn.net/Dailyblue/article/details/125027446 数据库链接的终端。用它能很方便的操作数据库。 通过网盘分享的文件:Navicat170.rar 链接: https://pan.baidu.com/s/1acruVrVXrVPElKVcWmAPrg?pwd=ytw8 提取码: ytw8 复制这段内容后打开百度网盘手机App,操作更方便哦 破解要看readme.txt文件 ``` ![image-20260609154309920](images/image-20260609154309920.png) ![image-20260609154536662](images/image-20260609154536662.png) ```mysql #不要求你们能写出sql #但是要求差不多能看懂,能猜懂 drop database syhis; #删除数据库 #创建数据库 create database syhis; #使用数据库 use syhis; #创建数据表 create table medic( id int auto_increment primary key COMMENT '自增id', goodname varchar(20) comment '药品名称', price NUMERIC(8,2) comment '价格', comments int comment '评论数量' )engine=innodb charset=utf8; #数据库的引擎为innodb 编码字符集为utf8 #查询 select * from medic; #增加 insert into medic values (null,'敌敌畏',888.88,2000); #删除数据库的数据 delete from medic; ``` **数据表的最基本的操作** ```sql #################################################################### #没有重新创建库,使用的还是原来的数据库 #从新创建一张表,重新进行测试 create table test( tid int auto_increment primary key, tname varchar(20), tsex char(2) )engine=innodb charset=utf8; #查询 select * from test; #插入 insert into test values (null,'毛含笑','女'); insert into test values (null,'郑超','女'); insert into test values (null,'张余','男'); insert into test values (null,'李宗伟','女'); #删除 delete from test; #一条不剩 ``` ## 科室 ### 数据表 ```sql #如果你已经存在syhis数据库,那么你就不用再重新创建这个数据库了 use syhis; #建立一个科室的表 create table dept( deptid int auto_increment primary key, #科室的id deptname varchar(20) #科室的名称 )engine=innodb charset=utf8; #引擎和编码字符集 #查询 select * from dept; truncate dept; #截断表让所有id重头开始 #增加 #增加25个不同科室 insert into dept values (1,'内科'); insert into dept values (2,'外科'); insert into dept values (3,'妇产科'); insert into dept values (4,'儿科'); insert into dept values (5,'眼科'); insert into dept values (6,'耳鼻喉科'); insert into dept values (7,'口腔科'); insert into dept values (8,'皮肤科'); insert into dept values (9,'急诊科'); insert into dept values (10,'中医科'); insert into dept values (11,'骨科'); insert into dept values (12,'神经内科'); insert into dept values (13,'神经外科'); insert into dept values (14,'心血管内科'); insert into dept values (15,'呼吸内科'); insert into dept values (16,'消化内科'); insert into dept values (17,'肾内科'); insert into dept values (18,'内分泌科'); insert into dept values (19,'肿瘤科'); insert into dept values (20,'康复科'); insert into dept values (21,'麻醉科'); insert into dept values (22,'放射科'); insert into dept values (23,'超声科'); insert into dept values (24,'检验科'); insert into dept values (25,'病理科'); ``` ### 后端 ## Idea的安装 **激活**: 运行JetBrains+2025+最新全家桶激活\jetbra-pojie\win2021-2025\scripts\install.vbs文件。双击就行。 然后等待几秒到几十秒,直到出现以下弹框,则表示脚本执行成功。 ![img](https://pic3.zhimg.com/v2-eb6f666436259b9e756078023b87fbaa_1440w.jpg) **3. 打开IDE,输入激活码** 打开你要激活的 IDE。输入对应软件的激活码。 **注意:** 记得初次安装打开 IDE,不要选中任何区域,需要选中不指定区域。 ![img](https://pica.zhimg.com/v2-096ddf350075ef12b4a330003cd7b7ae_1440w.jpg) ``` ZCB571FZHV-eyJsaWNlbnNlSWQiOiJaQ0I1NzFGWkhWIiwibGljZW5zZWVOYW1lIjoiZnV6emVzIGFsbHkiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQREIiLCJmYWxsYmFja0RhdGUiOiIyMDIzLTA3LTAxIiwicGFpZFVwVG8iOiIyMDIzLTA3LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBTSSIsImZhbGxiYWNrRGF0ZSI6IjIwMjMtMDctMDEiLCJwYWlkVXBUbyI6IjIwMjMtMDctMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFBDIiwiZmFsbGJhY2tEYXRlIjoiMjAyMy0wNy0wMSIsInBhaWRVcFRvIjoiMjAyMy0wNy0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQQ1dNUCIsImZhbGxiYWNrRGF0ZSI6IjIwMjMtMDctMDEiLCJwYWlkVXBUbyI6IjIwMjMtMDctMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFBTIiwiZmFsbGJhY2tEYXRlIjoiMjAyMy0wNy0wMSIsInBhaWRVcFRvIjoiMjAyMy0wNy0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQUkIiLCJmYWxsYmFja0RhdGUiOiIyMDIzLTA3LTAxIiwicGFpZFVwVG8iOiIyMDIzLTA3LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IklJIiwiZmFsbGJhY2tEYXRlIjoiMjAyMy0wNy0wMSIsInBhaWRVcFRvIjoiMjAyMy0wNy0wMSIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUEdPIiwiZmFsbGJhY2tEYXRlIjoiMjAyMy0wNy0wMSIsInBhaWRVcFRvIjoiMjAyMy0wNy0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQU1ciLCJmYWxsYmFja0RhdGUiOiIyMDIzLTA3LTAxIiwicGFpZFVwVG8iOiIyMDIzLTA3LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBXUyIsImZhbGxiYWNrRGF0ZSI6IjIwMjMtMDctMDEiLCJwYWlkVXBUbyI6IjIwMjMtMDctMDEiLCJleHRlbmRlZCI6dHJ1ZX1dLCJtZXRhZGF0YSI6IjAxMjAyMjA3MDFQU0FOMDAwMDA1IiwiaGFzaCI6IlRSSUFMOi01OTQ5ODgxMjIiLCJncmFjZVBlcmlvZERheXMiOjcsImF1dG9Qcm9sb25nYXRlZCI6ZmFsc2UsImlzQXV0b1Byb2xvbmdhdGVkIjpmYWxzZX0=-JNpWl3tvfBw9nYALTrBlJzoryrKHhqmiBxP5IljC6Hlgmb6YlOH8vPngzoyLYa+cGDMVj6fipEpm+BEqIA7oAoBYSu1ZPdzkHAa94apJg+CUQwuw+EJaATdKTANuKYTBsay6WsnrUh8vbIaJpGz19z+uOAc4xRP+gtuyjiwkNECZ6Y9qD+Dx3Gm5xXI3UvKqjPYIhXk23n1pjlxFIUmhD7BumdxF8JHmJJhd/K5FaXQU/K9pMp70GfmSS2KJgxm6SXfslWs/bF5GTY3i1GA6ez05ZyJwsmJMZ1v6W7GWrWNHDLK7i7aXhOLdK9u+pCz+2FpKmadRznpSmixDzj37ig==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD ``` 点击激活。如果激活码不能用,在网上再找一个。 **效果验证** 进入软件查看,已经激活到 2099-12-31 了。 ![img](https://pic1.zhimg.com/v2-9360abb49d2f2647765f9883433b4cae_1440w.jpg) 先安装jdk, 再安装idea。 设置maven. (所有公共的资源部的一个仓库),默认idea里面带maven, 但是指向地址是国外。 你拿我的压缩包,解压到D:\tools\apache-maven-3.6.0-bin\apache-maven-3.6.0目录下。 ![image-20260610102752876](images/image-20260610102752876.png) ### Maven 设置idea的两个地方: 地方1: 设置 ![image-20260610102841088](images/image-20260610102841088.png) 地方2: 为新项目设置 image-20260610102923938 ![image-20260610103001194](images/image-20260610103001194.png) idea的maven配置就好了。 ### Idea建立工程 文件----新建---项目 image-20260610140631532 空项目---输入你的项目名称 image-20260610140720194 项目右键---新建---模块 image-20260610140826835 **最关键的一部,一定要选择为maven** image-20260610140927123 image-20260610141003854 代开pom.xml进行修改 image-20260610141056573 pom.xml ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 3.2.8 com.han demo 0.0.1-SNAPSHOT demo demo 17 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin ``` 选择屏幕右边的m的字样,maven,然后进行刷新,就是从网上下载资源包。 ![image-20260610141342901](images/image-20260610141342901.png) **运行** 点击屏幕中间绿色的箭头,没有报错,程序就是ok的。 ![image-20260610141431539](images/image-20260610141431539.png) **后续继续开发,请查看科室中的具体细节。** ## 问题: 1:卸载idea后,重新安装。 import setting from PyCharm skip setting(请你一定选择这个) 2:请你下载jdk17.不用删除过去的jdk版本,是可以并行的。选择下发的17就好了。 ![image-20260610154738106](images/image-20260610154738106.png) 3: 一定要设置maven。 先去做设置去。 SpringBoot+MyBiats的开发结构。 #### 1:pom.xml ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 3.2.8 com.han syhis 0.0.1-SNAPSHOT syhis syhis 17 org.springframework.boot spring-boot-starter-web mysql mysql-connector-java 8.0.33 com.baomidou mybatis-plus-spring-boot3-starter 3.5.12 com.baomidou mybatis-plus-generator 3.5.3.1 org.freemarker freemarker 2.3.30 org.projectlombok lombok org.springframework.boot spring-boot-maven-plugin ``` #### 2:生成代码 我们用mybatis-plus官方提供的生成器,生成代码。 ```java package com.han.syhis; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.Collections; import java.util.Scanner; public class CodeGenerator { private static String packagename= "com.han"; //比如当前目录为com.han.server就只写com.han private static String dbname= "syhis"; public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { String projectPath = System.getProperty("user.dir"); String modelName = scanner("模块名"); // 定义XML文件的输出路径 - 这是核心修改 String xmlOutputDir = projectPath + "/"+modelName+"/src/main/resources/mappers"; FastAutoGenerator.create( "jdbc:mysql://localhost:3306/"+dbname+"?useUnicode=true&useSSL=false&characterEncoding=utf8", "root", "ilovelaohan") .globalConfig(builder -> builder .outputDir(projectPath + "/"+modelName+"/src/main/java") // Java文件输出目录 .author("laohan") .disableOpenDir() ) .packageConfig(builder -> builder .parent(packagename) .moduleName(modelName) // 设置XML文件的路径映射 - 关键配置 .pathInfo(Collections.singletonMap( OutputFile.xml, // 指定XML文件类型 xmlOutputDir // 指定XML文件输出目录 )) ) .strategyConfig(builder -> builder .addInclude(scanner("表名,多个英文逗号分割").split(",")) .entityBuilder() .enableLombok() .naming(NamingStrategy.underline_to_camel) .columnNaming(NamingStrategy.underline_to_camel) .mapperBuilder() // 新增mapper配置 .enableMapperAnnotation() // 启用@Mapper注解 .controllerBuilder() .enableRestStyle() ) .templateEngine(new FreemarkerTemplateEngine()) .execute(); } } ``` ![image-20260610142640247](images/image-20260610142640247.png) #### 3: application.yml 把resoruce/application.properties改名 application.yml image-20260610142758121 ```yml server: port: 8080 #服务的端口号 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/syhis?useUnicode=true&characterEncoding=utf8 username: root password: ilovelaohan #spring链接数据库的信息 ``` #### 4: 启动扫描包 在启动类上加一个@MapperScan("com.han.syhis.mapper") ![image-20260610143244477](images/image-20260610143244477.png) #### 5: 先测试一下 #### 6:写控制器类 ```java package com.han.syhis.controller; import com.han.syhis.entity.Dept; import com.han.syhis.service.IDeptService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; import java.util.List; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-10 */ @RestController public class DeptController { @Resource private IDeptService deptService; @GetMapping("/dept/findAll") @CrossOrigin //跨域 public List findAll(){ List list = deptService.list(); return list; } @GetMapping("/dept/findById") @CrossOrigin //跨域 public Dept findById(Integer deptid){ return deptService.getById(deptid); } @GetMapping("/dept/deleteById") @CrossOrigin //跨域 public boolean deleteById(Integer deptid){ return deptService.removeById(deptid); } @PostMapping("/dept/save") @CrossOrigin //跨域 public boolean save(Dept dept){ return deptService.save(dept); } @PostMapping("/dept/updateById") @CrossOrigin //跨域 public boolean updateById(Dept dept){ return deptService.updateById(dept); } } ``` ``` http://localhost:8080/dept/findAll ``` ![image-20260610143739092](images/image-20260610143739092.png) ## 接口文档: 能够直接通过接口文档,进行程序的测试,看代码是否是好的。 前端-----交互----后端的一个文档。 正式软件开发中,是一定需要这样一个文档的。 能够自动生成接口文档的工具 swagger。 ### 1: pom.xml ``` org.springdoc springdoc-openapi-starter-webmvc-ui 2.6.0 ``` 添加完一定同步一下maven ### 2: application.yml ```xml server: port: 8080 #服务的端口号 #spring链接数据库的信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/syhis?useUnicode=true&characterEncoding=utf8 username: root password: ilovelaohan #swagger的配置 springdoc: api-docs: path: /v3/api-docs swagger-ui: path: /swagger-ui.html ``` 其实swagger已经就可以使用了。 ``` http://localhost:8080/swagger-ui/index.html ``` ![image-20260610161030666](images/image-20260610161030666.png) ### 鸿蒙 #### 科室的鸿蒙代码 image-20260611084924936 ```ts @Entry @Component struct dept { build() { List(){ ForEach(Array.from({length:20}),()=>{ ListItem(){ //list里面必须而且只能放 ListItem组件 Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.SpaceBetween}){ Text('科室名称') Image($r('app.media.right')).width(30) }.border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) }.padding(10) }) }.width('100%').layoutWeight(1) //高度自适应 } } ``` #### Axios远程请求 1:安装axios ``` 方法1: ohpm install @ohos/axios 方法2: 根目录下oh-package.json5中添加依赖 "dependencies": { "@ohos/axios":"^2.27" } 屏幕的右上方有一个SyncNow,点击,然后就装好了。跟我们昨天Java里面的maven一样的。 ``` 2: 配置权限 在entry/src/main/module.json5中申请权限 ```json { "module": { "name": "entry", "type": "entry", "description": "$string:module_desc", "mainElement": "EntryAbility", "requestPermissions": [ { "name": "ohos.permission.INTERNET" } ] } } ``` 3:请求 image-20260611092514078 ```tsx import axios,{AxiosResponse} from "@ohos/axios"; //导入远程的请求类 import { get } from "../util/axios"; //导入自定义的包装类 interface Dept{ deptid:number, deptname:string } @Entry @Component struct dept { @State deptList:Array = [] //鸿蒙内部的生命周期中的钩子函数,不是我们来调用的 //而是当前鸿蒙页面,在加载的时候会自动调用 //希望页面一加载就能调用能干的事,写在下面的代码中 aboutToAppear(): void { //原生的axios的代码是比较麻烦的 // axios.get,null>('http://localhost:8080/dept/findAll') // .then((response:AxiosResponse)=>{ // console.log('取到的后台数据',JSON.stringify(response)); // }) //准备用包装类axios.js来发起请求 get('/dept/findAll',{},(content:Array)=>{ this.deptList = content; console.log('所有部门',this.deptList); }) } build() { List(){ ForEach(this.deptList,(item:Dept)=> {//ForEach(Array.from({length:20}),()=>{ ListItem(){ //list里面必须而且只能放 ListItem组件 Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.SpaceBetween}){ Text(item.deptname) Image($r('app.media.right')).width(30) }.border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) }.padding(10) }) }.width('100%').layoutWeight(1) //高度自适应 } } ``` ## 医生模块 ### 鸿蒙 在新建立一个目录utils目录,里面放得是axios.js和Entity.ts Entity.ts ``` export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string } ``` image-20260611102524283 ```tsx import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict} from "../util/Entity"; @Entry @Component struct empllist { @State dateList:Array = [] aboutToAppear(): void { this.getDates() } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text('孙重阳专家号').fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) } }) }.width('100%').layoutWeight(1) } } } ``` ### 数据库 ``` #把dept重新生成一下。 #如果你已经存在syhis数据库,那么你就不用再重新创建这个数据库了 use syhis; truncate dept; #截断表让所有id重头开始 #增加 #增加25个不同科室 insert into dept values (1,'内科'); insert into dept values (2,'外科'); insert into dept values (3,'妇产科'); insert into dept values (4,'儿科'); insert into dept values (5,'眼科'); insert into dept values (6,'耳鼻喉科'); insert into dept values (7,'口腔科'); insert into dept values (8,'皮肤科'); insert into dept values (9,'急诊科'); insert into dept values (10,'中医科'); insert into dept values (11,'骨科'); insert into dept values (12,'神经内科'); insert into dept values (13,'神经外科'); insert into dept values (14,'心血管内科'); insert into dept values (15,'呼吸内科'); insert into dept values (16,'消化内科'); insert into dept values (17,'肾内科'); insert into dept values (18,'内分泌科'); insert into dept values (19,'肿瘤科'); insert into dept values (20,'康复科'); insert into dept values (21,'麻醉科'); insert into dept values (22,'放射科'); insert into dept values (23,'超声科'); insert into dept values (24,'检验科'); insert into dept values (25,'病理科'); select * from dept; ``` 医生的级别表 ```mysql #级别表 create table emplevel( levelid int auto_increment primary key, #主键 levelname varchar(20),#级别名称 money decimal(8,2) #挂号费 )engine=innodb charset=utf8; insert into emplevel values (1,'主任医师',20.0); insert into emplevel values (2,'副主任医师',12.0); insert into emplevel values (3,'医师',8.0); select * from emplevel; ``` 医生表 ```mysql #创建一个员工表 #医生名字 级别 级别对象的挂号费 应该属于那个科室 create table emp( empid int auto_increment primary key, empname varchar(20),#医生的名字 deptid int, #这个就是你科室的id,如果1代表内科 得参照dept表里面的数据 levelid int #这个就是你级别的id. 如果1代表主任医师 得参考emplevel里面的数据 )engine=innodb charset=utf8; truncate emp; insert into emp values (1,'郭一星',1,1); insert into emp values (2,'郭二星',1,2); insert into emp values (3,'郭三星',1,3); insert into emp values (4,'郭四星',1,1); insert into emp values (5,'郭无星',1,2); select * from emp; #数据表的两表联查 #查出表里面的所有数据 select * from emp; #带条件查询 select * from emp where empid=2; #查询员工编号是2的元素 #带条件查询 select * from emp where deptid=1; #查询员工是部门1的所有人 #带条件查询 select * from emp where levelid=3; #查询员工是级别1的人 #带条件查询 select * from emp where empname='郭三星'; #查询员工名称是郭三星的人 #两表联查 两张表联合在一起执行写法如下 # select * from a表,b表 where a表.字段=b表.字段 select * from emp,emplevel where emp.levelid=emplevel.levelid ``` ```mysql truncate emp; -- 内科 (deptid=1) insert into emp(empname, deptid, levelid) values ('王建国',1,1),('张志强',1,2),('李淑芬',1,3),('陈文华',1,2),('刘桂兰',1,3),('赵德明',1,1); -- 外科 (deptid=2) insert into emp(empname, deptid, levelid) values ('孙永强',2,1),('周丽娟',2,2),('吴志远',2,3),('郑晓东',2,2),('黄桂香',2,3),('宋德福',2,1),('郭志强',2,2); -- 妇产科 (deptid=3) insert into emp(empname, deptid, levelid) values ('王秀英',3,1),('李淑华',3,2),('张桂珍',3,3),('刘淑芳',3,2),('陈玉兰',3,3),('赵秀荣',3,1),('孙桂芝',3,2),('周淑芹',3,3); -- 儿科 (deptid=4) insert into emp(empname, deptid, levelid) values ('刘文斌',4,1),('陈秀梅',4,2),('王淑珍',4,3),('张志远',4,2),('李桂芝',4,3),('赵淑华',4,1),('孙晓东',4,2); -- 眼科 (deptid=5) insert into emp(empname, deptid, levelid) values ('陈志强',5,1),('王淑芳',5,2),('李德明',5,3),('张秀英',5,2),('刘志远',5,3),('赵桂兰',5,1),('孙淑珍',5,2); -- 耳鼻喉科 (deptid=6) insert into emp(empname, deptid, levelid) values ('王德福',6,1),('李淑贤',6,2),('张志强',6,3),('刘桂珍',6,2),('陈秀兰',6,3),('赵德明',6,1),('孙秀英',6,2),('周桂香',6,3); -- 口腔科 (deptid=7) insert into emp(empname, deptid, levelid) values ('张永强',7,1),('王淑兰',7,2),('李秀英',7,3),('刘德明',7,2),('陈桂芝',7,3),('赵秀芳',7,1),('孙淑芬',7,2),('周淑珍',7,3); -- 皮肤科 (deptid=8) insert into emp(empname, deptid, levelid) values ('王志远',8,1),('李淑芹',8,2),('张秀兰',8,3),('刘桂芳',8,2),('陈淑珍',8,3),('赵秀梅',8,1),('孙淑芹',8,2); -- 急诊科 (deptid=9) insert into emp(empname, deptid, levelid) values ('刘德明',9,1),('陈秀英',9,2),('王志强',9,3),('李桂兰',9,2),('张淑芳',9,3),('赵德才',9,1),('孙秀兰',9,2),('周淑芬',9,3); -- 中医科 (deptid=10) insert into emp(empname, deptid, levelid) values ('张德胜',10,1),('王淑惠',10,2),('李秀芝',10,3),('刘桂英',10,2),('陈淑华',10,3),('赵秀芹',10,1),('孙淑兰',10,2),('周淑梅',10,3); -- 骨科 (deptid=11) insert into emp(empname, deptid, levelid) values ('王德才',11,1),('李淑英',11,2),('张志强',11,3),('刘秀芬',11,2),('陈桂兰',11,3),('赵淑英',11,1),('孙秀荣',11,2); -- 神经内科 (deptid=12) insert into emp(empname, deptid, levelid) values ('陈志远',12,1),('王淑芹',12,2),('李秀珍',12,3),('张桂英',12,2),('刘淑兰',12,3),('赵秀芬',12,1),('孙淑梅',12,2),('周秀英',12,3); -- 神经外科 (deptid=13) insert into emp(empname, deptid, levelid) values ('张德明',13,1),('李淑珍',13,2),('王秀兰',13,3),('刘桂香',13,2),('陈淑芬',13,3),('赵秀珍',13,1),('孙淑华',13,2),('周淑萍',13,3); -- 心血管内科 (deptid=14) insert into emp(empname, deptid, levelid) values ('李志强',14,1),('张淑芬',14,2),('王秀珍',14,3),('刘桂兰',14,2),('陈淑英',14,3),('赵秀芬',14,1),('孙淑珍',14,2),('周淑芳',14,3); -- 呼吸内科 (deptid=15) insert into emp(empname, deptid, levelid) values ('王德胜',15,1),('李秀芳',15,2),('张淑珍',15,3),('刘桂芝',15,2),('陈秀兰',15,3),('赵淑芬',15,1),('孙秀珍',15,2),('周淑桂',15,3); -- 消化内科 (deptid=16) insert into emp(empname, deptid, levelid) values ('刘德胜',16,1),('陈淑兰',16,2),('王秀芳',16,3),('李桂珍',16,2),('张淑芬',16,3),('赵秀兰',16,1),('孙秀芳',16,2),('周淑芹',16,3); -- 肾内科 (deptid=17) insert into emp(empname, deptid, levelid) values ('张永福',17,1),('王淑芬',17,2),('李秀兰',17,3),('刘桂珍',17,2),('陈淑梅',17,3),('赵秀英',17,1),('孙秀芬',17,2),('周淑珍',17,3); -- 内分泌科 (deptid=18) insert into emp(empname, deptid, levelid) values ('陈德明',18,1),('王秀芝',18,2),('李淑芳',18,3),('张桂兰',18,2),('刘秀珍',18,3),('赵淑芳',18,1),('孙淑芬',18,2),('周淑芹',18,3); -- 肿瘤科 (deptid=19) insert into emp(empname, deptid, levelid) values ('王志明',19,1),('李秀芬',19,2),('张淑兰',19,3),('刘桂芳',19,2),('陈秀珍',19,3),('赵淑芬',19,1),('孙淑兰',19,2),('周淑珍',19,3); -- 康复科 (deptid=20) insert into emp(empname, deptid, levelid) values ('刘永强',20,1),('陈淑芳',20,2),('王秀芬',20,3),('李桂香',20,2),('张淑芹',20,3),('赵秀芬',20,1),('孙淑英',20,2),('周淑兰',20,3); -- 麻醉科 (deptid=21) insert into emp(empname, deptid, levelid) values ('王德明',21,1),('李淑芬',21,2),('张秀英',21,3),('刘桂兰',21,2),('陈淑珍',21,3),('赵秀芝',21,1),('孙淑芳',21,2),('周淑芹',21,3); -- 放射科 (deptid=22) insert into emp(empname, deptid, levelid) values ('张德祥',22,1),('王淑琴',22,2),('李秀芬',22,3),('刘桂珍',22,2),('陈淑英',22,3),('赵秀兰',22,1),('孙淑珍',22,2),('周淑芬',22,3); -- 超声科 (deptid=23) insert into emp(empname, deptid, levelid) values ('李德胜',23,1),('张淑芹',23,2),('王秀英',23,3),('刘桂芬',23,2),('陈淑芳',23,3),('赵秀珍',23,1),('孙淑兰',23,2),('周淑芬',23,3); -- 检验科 (deptid=24) insert into emp(empname, deptid, levelid) values ('陈德胜',24,1),('王淑珍',24,2),('李秀英',24,3),('张桂珍',24,2),('刘淑芬',24,3),('赵秀兰',24,1),('孙秀英',24,2),('周淑珍',24,3); -- 病理科 (deptid=25) insert into emp(empname, deptid, levelid) values ('刘德胜',25,1),('陈淑英',25,2),('王秀芬',25,3),('李桂英',25,2),('张淑珍',25,3),('赵秀芳',25,1),('孙淑芬',25,2),('周淑兰',25,3); ``` ```mysql #测试上面的代码 select * from emp; #查询部门1的人 select * from emp where deptid=1; #两表联查 select * from emp,emplevel where emp.levelid=emplevel.levelid #两表联查查询部门1 select * from emp,emplevel where emp.levelid=emplevel.levelid and deptid=3 ``` ### Java后端 ```java @Mapper public interface EmpMapper extends BaseMapper { @Select("select * from emp,emplevel where emp.levelid=emplevel.levelid and deptid=#{deptid}") public List findByDeptid(@Param("deptid") Integer deptid); } ``` ```java public class Emp implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "empid", type = IdType.AUTO) private Integer empid; private String empname; private Integer deptid; private Integer levelid; //新加入的两个字段 private String levelname; private BigDecimal money; } ``` ```java public interface IEmpService extends IService { public List findByDeptid(@Param("deptid") Integer deptid); } ``` ```java @Service public class EmpServiceImpl extends ServiceImpl implements IEmpService { public List findByDeptid(@Param("deptid") Integer deptid) { return baseMapper.findByDeptid(deptid); } } ``` ```java @RestController public class EmpController { @Resource private IEmpService empService; @GetMapping("/emp/findByDeptid") @CrossOrigin public List findAll(@RequestParam("deptid") Integer deptid){ return empService.findByDeptid(deptid); } } ``` 这个代码访问的时候,是必须要加deptid,如果没有deptid就报错。 ``` http://localhost:8080/emp/findByDeptid?deptid=1 ``` ### 重新修改医生模块鸿蒙 utils/Entity.ts ```json export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string } export interface Emp{ empid: number, empname: string, deptid: number, levelid: number, levelname: string, money: number } ``` image-20260611151956027 ```tsx import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict,Emp} from "../util/Entity"; import {get} from "../util/axios"; @Entry @Component struct empllist { @State dateList:Array = [] @State empList:Array = [] aboutToAppear(): void { this.getDates() //在此地准备发起远程请求 get('/emp/findByDeptid?deptid=1',{},(content:Array)=>{ this.empList = content; console.log('获取当前部门下的员工',this.empList) }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(this.empList,(item:Emp)=>{//ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text(item.empname+' '+item.levelname).fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) } }) }.width('100%').layoutWeight(1) } } } ``` ## 鸿蒙的导航 resources/profile/main_page.json ```json { "src": [ "pages/Index", "pages/dept", "pages/emplist" ] } ``` ``` import { router } from "@kit.ArkUI"; onClick(()=>{ console.log('被按下了'+item.deptid); router.pushUrl({ url: 'pages/emplist' }) }) ``` 这就完成了页面的导航。 ``` import axios,{AxiosResponse} from "@ohos/axios"; //导入远程的请求类 import { get } from "../util/axios"; //导入自定义的包装类 import {Dept} from "../util/Entity"; import { router } from "@kit.ArkUI"; @Entry @Component struct dept { @State deptList:Array = [] //鸿蒙内部的生命周期中的钩子函数,不是我们来调用的 //而是当前鸿蒙页面,在加载的时候会自动调用 //希望页面一加载就能调用能干的事,写在下面的代码中 aboutToAppear(): void { //原生的axios的代码是比较麻烦的 // axios.get,null>('http://localhost:8080/dept/findAll') // .then((response:AxiosResponse)=>{ // console.log('取到的后台数据',JSON.stringify(response)); // }) //准备用包装类axios.js来发起请求 get('/dept/findAll',{},(content:Array)=>{ this.deptList = content; console.log('所有部门',this.deptList); }) } build() { List(){ ForEach(this.deptList,(item:Dept)=> {//ForEach(Array.from({length:20}),()=>{ ListItem(){ //list里面必须而且只能放 ListItem组件 Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.SpaceBetween}){ Text(item.deptname) Image($r('app.media.right')).width(30) }.border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ console.log('被按下了'+item.deptid); router.pushUrl({ url: 'pages/emplist', params:{deptid:item.deptid} }) }) }.padding(10) }) }.width('100%').layoutWeight(1) //高度自适应 } } ``` **实现页面数据传参** ```tsx import axios,{AxiosResponse} from "@ohos/axios"; //导入远程的请求类 import { get } from "../util/axios"; //导入自定义的包装类 import {Dept} from "../util/Entity"; import { router } from "@kit.ArkUI"; @Entry @Component struct dept { @State deptList:Array = [] //鸿蒙内部的生命周期中的钩子函数,不是我们来调用的 //而是当前鸿蒙页面,在加载的时候会自动调用 //希望页面一加载就能调用能干的事,写在下面的代码中 aboutToAppear(): void { //原生的axios的代码是比较麻烦的 // axios.get,null>('http://localhost:8080/dept/findAll') // .then((response:AxiosResponse)=>{ // console.log('取到的后台数据',JSON.stringify(response)); // }) //准备用包装类axios.js来发起请求 get('/dept/findAll',{},(content:Array)=>{ this.deptList = content; console.log('所有部门',this.deptList); }) } build() { List(){ ForEach(this.deptList,(item:Dept)=> {//ForEach(Array.from({length:20}),()=>{ ListItem(){ //list里面必须而且只能放 ListItem组件 Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.SpaceBetween}){ Text(item.deptname) Image($r('app.media.right')).width(30) }.border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ console.log('被按下了'+item.deptid); router.pushUrl({ url: 'pages/emplist', params:{deptid:item.deptid} }) }) }.padding(10) }) }.width('100%').layoutWeight(1) //高度自适应 } } ``` ```tsx import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict,Emp} from "../util/Entity"; import {get} from "../util/axios"; import { router } from "@kit.ArkUI"; @Entry @Component struct empllist { @State dateList:Array = [] @State empList:Array = [] @State deptid:string = "1" aboutToAppear(): void { //准备接受前面传过来的参数 //这个对象的功能就是用来接受前面的路由给我传过来参数 const params:Record = router.getParams() as Record this.deptid = params.deptid; this.getDates() //在此地准备发起远程请求 get('/emp/findByDeptid?deptid='+this.deptid,{},(content:Array)=>{ this.empList = content; console.log('获取当前部门下的员工',this.empList) }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(this.empList,(item:Emp)=>{//ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text(item.empname+' '+item.levelname).fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) } }) }.width('100%').layoutWeight(1) } } } ``` ## 如何导入我的项目 1:下载 ``` https://gitee.com/hanweiwn/sydx ``` 克隆,下载 2:包含笔记,前端,后端代码 前端直接在你的鸿蒙的DevCode中打开直接就是ok 的 3:后端打开后。 要改application.yml 把你的数据库密码改正。 可能你的maven路径跟我的不一致,需要去看一眼。 如果启动类运行不了,看一下设置里面的jdk的版本是不是17. ## 挂号医生 ### 鸿蒙: 展示具体的医生信息,包括可以挂号的时间段。和挂号操作。 image-20260612094655784 Entity.ts ``` export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string } export interface Emp{ empid: number, empname: string, deptid: number, levelid: number, levelname: string, money: number } export const timebetween:Array=[ '08.00-09.00', '09.00-10.00', '10.00-11.00', '11.00-12.00', '14.00-15.00', '15.00-16.00', '17.00-17.00', '17.00-18.00' ] ``` empitem.ets ```tsx import { timebetween } from "../util/Entity"; @Entry @Component struct empitem { build() { Column(){ //包含了医生的头像和详细信息 Row(){ Column(){//左边的列 Image($r('app.media.doctor')).width(70) Text('关注').fontColor('#ffffff') .border({color:'#ffffff',width:1}) .margin({top:8}) }.width(80).margin({left:20}) Column(){//右边的列 Text('吴玉林主任医师').fontSize(24).fontColor('#ffffff') .fontWeight(700) }.layoutWeight(1) }.height(140).backgroundColor('#575ea9').width('100%') //这个医生那天可以挂号的日期范围 Row(){ Text('门诊就医').fontSize(24).fontWeight(700) Text('需要医院').fontSize(20).fontColor('#666666') }.width('100%').margin({top:20,left:20}) //具体的日期框,依然是五个 List({space:15}){ ForEach(Array.from({length:5}),()=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text('星期六') Text('6/12') }.borderWidth(3).height(80).borderRadius(7) .layoutWeight(1) } }) }.listDirection(Axis.Horizontal) .height(100).margin({top:20}) //挂号的时间段 和 可以挂号的数量 Text('预约时间').height(50).width('90%') .border({color:'#cccccc',width:{bottom:1}}) List({space:15}){ ForEach(timebetween,(item:string)=>{ ListItem(){ Text(item).textAlign(TextAlign.Center) .width('100%').fontWeight(700) }.height(50) .border({color:'#cccccc',width:{bottom:1}}) .width('100%').margin({left:20}) }) }.height(300) //医生挂号的费用 Row(){ Text('主任医师').fontSize(24).fontWeight(700) Text('¥50.00').fontColor('#ff0000').fontSize(24).fontWeight(700) }.justifyContent(FlexAlign.SpaceBetween) .width('90%') }.width('100%').height('100%') } } ``` 在医生列表页面按下医生,进入医生详情页面 1:main_page.json ``` { "src": [ "pages/Index", "pages/dept", "pages/emplist", "pages/empitem" ] } ``` 2: emplist.ets ```tsx import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict,Emp} from "../util/Entity"; import {get} from "../util/axios"; import { router } from "@kit.ArkUI"; //这里是新加的代码 @Entry @Component struct empllist { @State dateList:Array = [] @State empList:Array = [] @State deptid:string = "1" aboutToAppear(): void { //准备接受前面传过来的参数 //这个对象的功能就是用来接受前面的路由给我传过来参数 const params:Record = router.getParams() as Record this.deptid = params.deptid; this.getDates() //在此地准备发起远程请求 get('/emp/findByDeptid?deptid='+this.deptid,{},(content:Array)=>{ this.empList = content; console.log('获取当前部门下的员工',this.empList) }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(this.empList,(item:Emp)=>{//ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text(item.empname+' '+item.levelname).fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ //这里是新加的代码 console.log('测试,去跳转医生详情页面'); //跳转到医生挂号的页面 router.pushUrl({ url: 'pages/empitem' }) }) //这里是新加的代码 } }) }.width('100%').layoutWeight(1) } } } ``` 数据库 后端 ## 个人中心 显示自己的过去的曾经的挂号信息 鸿蒙: image-20260612102120388 ```tsx @Entry @Component struct my { build() { //上下两部分 //上面 左边箭头 个人中心 右边 更多的图标 //下面 个人的详细信息 挂号信息 Column(){ Row(){ Image($r('app.media.ic_arrow_left')).width(30) Text('个人中心') Image($r('app.media.ic_more')).width(24) }.width('100%') .backgroundColor('#ccc') .height(40) .justifyContent(FlexAlign.SpaceBetween) .padding({left:10,right:10}) Column(){ List({space:15}){ ForEach(Array.from({length:3}),()=>{ ListItem(){ Column(){ Grid(){ GridItem(){Text('就诊人:')}.align(Alignment.Start) GridItem(){Text('严秋婷')}.align(Alignment.Start) GridItem(){Text('科室:')}.align(Alignment.Start) GridItem(){Text('眼科')}.align(Alignment.Start) GridItem(){Text('年龄:')}.align(Alignment.Start) GridItem(){Text('21')}.align(Alignment.Start) GridItem(){Text('医生:')}.align(Alignment.Start) GridItem(){Text('刘德华')}.align(Alignment.Start) GridItem(){Text('性别:')}.align(Alignment.Start) GridItem(){Text('女')}.align(Alignment.Start) GridItem(){Text('挂号费:')}.align(Alignment.Start) GridItem(){Text('¥60.00')}.align(Alignment.Start) GridItem(){Text('时间')}.align(Alignment.Start) GridItem(){Text('2026-09-09')}.align(Alignment.Start) GridItem(){Text('时间段')}.align(Alignment.Start) GridItem(){Text('8.00-9.00')}.align(Alignment.Start) }.rowsTemplate('1fr 1fr 1fr 1fr') .columnsTemplate('2fr 3fr 2fr 3fr') .width('90%').height(120) .margin({top:5,left:10}) } } .border({color:'#ccc',width:{bottom:1}}) }) } }.backgroundColor(Color.White).width('100%').height('100%') .backgroundColor('#eeeeee') }.width('100%') .height('100%') .backgroundColor('#ccc') .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM]) } } ``` 数据库 后端 ## 鸿蒙技术点 ### Tab导航 最简单的 image-20260612141214994 ``` @Entry @Component struct tab { build() { Tabs({barPosition:BarPosition.End}){//不写属性,默认出现在顶端 TabContent(){Text('商品内容')}.tabBar("商品") TabContent(){Text('购物车内容')}.tabBar("购物车") TabContent(){Text('AI')}.tabBar("AI") TabContent(){Text('消息内容')}.tabBar("消息") TabContent(){Text('我的信息')}.tabBar("我的") } } } ``` 第二步 ``` @Entry @Component struct tab { @Builder mybuilder(title:string){ //相当于一个自定义组件 Text(title) } build() { Tabs({barPosition:BarPosition.End}){//不写属性,默认出现在顶端 TabContent(){Text('商品内容')}.tabBar(this.mybuilder('挂号')) TabContent(){Text('购物车内容')}.tabBar(this.mybuilder('药品')) TabContent(){Text('AI')}.tabBar("AI") TabContent(){Text('消息内容')}.tabBar(this.mybuilder('购物车')) TabContent(){Text('我的信息')}.tabBar(this.mybuilder('我的')) } } } ``` 第三步 image-20260612151832727 ``` @Entry @Component struct tab { @State index:number = 0 @Builder mybuilder(index:number,title:string,img:ResourceStr){ //相当于一个自定义组件 Column(){ if(title=="AI"){ Image(img).width(40) }else{ Image(img).width(30) .fillColor(this.index==index?Color.Black:Color.Pink) Text(title) } } } build() { Tabs({barPosition:BarPosition.End}){//不写属性,默认出现在顶端 TabContent(){Text('商品内容')}.tabBar(this.mybuilder(0,'挂号',$r('app.media.guahao'))) TabContent(){Text('购物车内容')}.tabBar(this.mybuilder(1,'药品',$r('app.media.mdeic'))) TabContent(){Text('AI')}.tabBar(this.mybuilder(2,'AI',$r('app.media.AI'))) TabContent(){Text('消息内容')}.tabBar(this.mybuilder(3,'购物车',$r('app.media.gouwu'))) TabContent(){Text('我的信息')}.tabBar(this.mybuilder(4,'我的',$r('app.media.wode'))) }.onSelected((index:number)=>{ console.log('你选中了',index); this.index = index; }) } } ``` ### 整合整个页面 image-20260612152844105 ``` 需要把你的每个在导航栏中出现的组件前面都要写成 export 进行导出。改了4个文件。 my.ets export struct my medic.ets export struct medic dept.ets export struct dept carts.ets export struct carts ``` ``` import { my } from "../pages/my"; import { carts } from "../pages/carts"; import { medic } from "../pages/medic"; import { dept } from "../pages/dept"; @Entry @Component struct tab { @State index:number = 0 @Builder mybuilder(index:number,title:string,img:ResourceStr){ //相当于一个自定义组件 Column(){ if(title=="AI"){ Image(img).width(40) }else{ Image(img).width(30) .fillColor(this.index==index?Color.Black:Color.Pink) Text(title) } } } build() { Tabs({barPosition:BarPosition.End}){//不写属性,默认出现在顶端 TabContent(){dept()}.tabBar(this.mybuilder(0,'挂号',$r('app.media.guahao'))) TabContent(){medic()}.tabBar(this.mybuilder(1,'药品',$r('app.media.mdeic'))) TabContent(){Text('AI')}.tabBar(this.mybuilder(2,'AI',$r('app.media.AI'))) TabContent(){carts()}.tabBar(this.mybuilder(3,'购物车',$r('app.media.gouwu'))) TabContent(){my()}.tabBar(this.mybuilder(4,'我的',$r('app.media.wode'))) }.onSelected((index:number)=>{ console.log('你选中了',index); this.index = index; }) } } ``` 这个代码还有一个bug,等待下周解决,因为代码要完全组件化开发。 ### 自定义组件 组件:框架直接提供的就是系统组件,我们自己写的,想通用的就是自定义组件。 ``` @Entry //一个模块的入口 @Component //就是一个组件 struct Hello{ //往往正式项目,只有一个ets文件有@Entry。其他的文件,都是组件,它只有@Component //@Component不能单独,独立显示,必须依赖于入口 } ``` 第一个定义组件 ``` @Entry @Component struct testComp1 { build() { Column(){ Text('我是入口') Text('我是入口') Text('我是入口') Comp1() Comp1() Comp1() } } } //自定义组件 // 跟你的入口文件张的一抹一眼,就是没有@Entry @Component struct Comp1{ build() { Column(){ Text('我是一个组件') Text('我是一个组件') Text('我是一个组件') } } } ``` image-20260612154412957 自定义头中尾 ``` @Entry @Component struct testComp1 { build() { Column(){ MyHead() MyMain() MyFoot() } } } //自定义组件 // 跟你的入口文件张的一抹一眼,就是没有@Entry @Component struct MyHead{ build() { Row(){ Text('我是头部').height(50).backgroundColor(Color.Brown).width('100%') } } } @Component struct MyMain{ build() { Column(){ Text('我是中间的部分') Text('我是中间的部分') Text('我是中间的部分') }.backgroundColor('#ccc').width('100%') .layoutWeight(1) } } @Component struct MyFoot{ build() { Row(){ Text('我是低部').height(50).backgroundColor(Color.Green).width('100%') } } } ``` ### 组件间的传参 A组件的参数,如何传递给B组件,如何再传递给C组件。 @Prop @Link @Provide @Consume ``` @Prop @Link 只能父子组件直接传参,直接是直接,不能隔代。必须是父子父子直接嵌套,中间不能隔其他组件。而且必须手工传参。 这个代码写起来笔记麻烦。因为要手工。子组件不能改父组件的值。 参数,我想只存在于父子之间。不想参数的范围太广。 @Prop是单向的,@Link是双向的。 都不能跨代。 @Privde @Consume 跨多层传参(爷爷---》儿子----》孙子----》曾孙),无需逐层传递,支持任意层级。 中间组件完全不用管。 后者自动注入。 爷爷---》儿子----》孙子----》曾孙 放 取 写法简单,方便,但是参数的范围大。子组件可以改父组件的值。 ``` @Prop image-20260615090454668 ```tsx @Entry @Component struct FatherComp { //父组件定义一个变量 @State fcar:string = '劳斯莱斯' build() { Column(){ Text('孩他爹有的车'+this.fcar) Button('他爹的按钮').onClick(()=>{ //改变自己的车的值 this.fcar = '兰博基尼' //父组件的值改变,同时也会改变子组件的值 }) SonComp({ scar:this.fcar //把父组件的值给了子组件 }) //手工传入,子组件才会有这个参数 }.backgroundColor(Color.Orange) } } @Component struct SonComp{ //子组件来接受父组件传递的参数 @Prop scar:string='' //@Prop就是接受父组件传过来的参数 //只要加上@Prop就可以得到父组件的参数 build() { Column(){ Text('我是子组件,父类的车是'+this.scar) Button('他娃的按钮').onClick(()=>{ this.scar = '三轮车' //更改子组件的参数 }) }.backgroundColor(Color.Pink) } } ``` @Link prop只能实现单向同步,只能父传子,但是@Link可以实现双向同步。父--子---父 使用步骤: 将父组件的状态属性传递给子组件 子组件通过@Link修饰就可以了 ``` 把上面的代码//子组件来接受父组件传递的参数 @Link scar:string 只改这一句话,就已经能实现双向传参了。 ``` image-20260615091632078 ```tsx interface Person{ name:string, age:number } @Entry @Component struct FatherComp { @State count:number = 0; @State person:Person = { name:'潘凤', age : 18 } build() { Column(){ Text('父组件'+this.count) Text(JSON.stringify(this.person)) Button('add').onClick(()=>{ this.count++ }) SonComp({ count:this.count, person:this.person }) //手工传入,子组件才会有这个参数 }.backgroundColor(Color.Orange) } } @Component struct SonComp{ //子组件来接受父组件传递的参数 @Link count:number @Link person:Person //只要加上@Prop就可以得到父组件的参数 build() { Column(){ Text('我是子组件'+this.count) Text(JSON.stringify(this.person)) Button('--').onClick(()=>{ this.count-- }) }.backgroundColor(Color.Pink) } } ``` @Provider @Consume 两个一般会成对出现,可以跨级。@Provider向后传参,@Consume从上一级,上几级接受参数。 跨级双向同步。 ``` @Entry @Component struct F { @Provide count:number = 0; build() { Column(){ Text('父组件'+this.count) Button('父组件按钮').onClick(()=>{ this.count++ }) C() }.backgroundColor(Color.Pink) } } @Component struct C{ @Consume count:number; //父组件不用额外传参,子组件不用额外接受参数 build() { Column(){ Text('子组件'+this.count) Button('子组件按钮').onClick(()=>{ this.count++ }) S() }.backgroundColor(Color.Yellow) } } @Component struct S{ @Consume count:number; //父组件不用额外传参,子组件不用额外接受参数 build() { Column(){ Text('孙组件'+this.count) Button('孙组件按钮').onClick(()=>{ this.count++ }) }.backgroundColor(Color.Green) } } ``` ## 生命周期 理解就好了。概念。 组件也页面在创建显示销毁的整个过程,会自动执行一系列的【生命周期钩子函数】,系统会自动调用的函数。 他能自动执行一些代码 ``` 仅仅@Entry修饰页面组件 onPageShow: 页面每次显示的时候都会触发 onPageHide: 页面每次隐藏触发。 OnBackPress:点击返回按钮的时候触发。 @Entry和@Component都有的: aboutToAppear: 创建组件后就执行,可以修改状态变量 aboutToDispear: 组件实例销户前执行,不允许修改状态变量 ``` ```tsx @Entry @Component struct testlife { aboutToAppear(): void { //查询后端数据 console.log('aboutToAppear method be called'); } aboutToDisappear(): void { //想销毁数据的时候 console.log('aboutToDisappear method be called'); } onPageShow(): void { //对页面数据进行渲染的 console.log('onPageShow method be called'); } onPageHide(): void { //保存会话,然后断开,后续再用 console.log('onPageHide method be called'); } onBackPress(): boolean | void { //你按下上一步按钮的 console.log('onBackPress method be called'); } build() { Column(){ Button("你看我不见") } } } ``` ## 登录链接 按下登录按钮就跳转到了具体的tab页面。 ``` index.ets文件 import { router } from "@kit.ArkUI"; Button('登录').width('100%').onClick(()=>{ console.log('登录'); router.pushUrl({ url:'pages/tab' }) }) main_page.json { "src": [ "pages/Index", "pages/dept", "pages/emplist", "pages/empitem", "pages/tab" ] } ``` ## 注册 给患者使用的。关于医生管理,科室管理,挂号管理,时间段管理,这些都是后台。本次项目中就不体现了。 ### 鸿蒙静态界面 image-20260615143031009 ```tsx @Entry @Component struct register { build() { Column(){ Text('注册新用户').fontSize(30).fontWeight(700) Text('石油医院挂号系统不会泄露您的隐私信息') .fontColor('#ff9188e').margin({top:10}) //网上见到的,往往是你输入完手机号之后,对方会给你发一个验证密码 //你验证码输入正确,才代表这个手机号是你的 //取回密码,也是通过手机发验证码来验证的 //如果通过代码,给对方手机发验证码 //阿里云市场 TextInput({placeholder:'请输入手机号'}).height(50) .margin({top:30}) TextInput({placeholder:'请输入密码'}).height(50) .margin({top:30}) .type(InputType.Password) TextInput({placeholder:'请输入姓名'}).height(50) .margin({top:30}) TextInput({placeholder:'请输入身份证号'}).height(50) .margin({top:30}) Row({space:20}){ Radio({value:'男',group:'sexgroup'}).checked(true) Text('男').fontSize(20).margin({right:20}) Radio({value:'女',group:'sexgroup'}) Text('女').fontSize(20).margin({right:20}) }.height(50).width('100%') .margin({top:20}).justifyContent(FlexAlign.Center) Button('注册') .width('85%').height(60).margin({top:30}) .shadow({radius:10,color:'#000000'}) }.width('100%') .alignItems(HorizontalAlign.Center) .padding({top:30}) } } ``` 界面已经能够显示出来了,但是我将来得把这个页面上的数据,收集,封装,传递给后端,进行注册。 收集页面数据 在你的utils/Entity.ts文件中新加入如下代码 ```json //封装了你将来要获取的页面数据 export interface Userinfo { userid?: number, //加?代表的意思是这个值可以有,也可以没有 如果不加?那么这个值必须存在 phone?:string, password?:string, username?:string, idcard?:string, gender?:string } ``` 我们就可以在鸿蒙的前端页面中使用这个数据了 ```tsx import {Userinfo} from "../util/Entity"; @Entry @Component struct register { @State userinfo: Userinfo = { gender:'男' } build() { Column(){ Text('注册新用户').fontSize(30).fontWeight(700) Text('石油医院挂号系统不会泄露您的隐私信息') .fontColor('#ff9188e').margin({top:10}) //网上见到的,往往是你输入完手机号之后,对方会给你发一个验证密码 //你验证码输入正确,才代表这个手机号是你的 //取回密码,也是通过手机发验证码来验证的 //如果通过代码,给对方手机发验证码 //阿里云市场 TextInput({placeholder:'请输入手机号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.phone = value }) TextInput({placeholder:'请输入密码'}).height(50) .margin({top:30}) .type(InputType.Password) .onChange((value) => { this.userinfo.password = value }) TextInput({placeholder:'请输入姓名'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.username = value }) TextInput({placeholder:'请输入身份证号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.idcard = value }) Row({space:20}){ Radio({value:'男',group:'sexgroup'}).checked(true) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '男' } }) Text('男').fontSize(20).margin({right:20}) Radio({value:'女',group:'sexgroup'}) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '女' } }) Text('女').fontSize(20).margin({right:20}) }.height(50).width('100%') .margin({top:20}).justifyContent(FlexAlign.Center) Button('注册') .width('85%').height(60).margin({top:30}) .shadow({radius:10,color:'#000000'}) .onClick(()=>{ console.log(JSON.stringify(this.userinfo)); }) }.width('100%') .alignItems(HorizontalAlign.Center) .padding({top:30}) } } ``` ### 数据表 ```mysql create table if not EXISTS userinfo ( userid int auto_increment primary key, #主键id phone VARCHAR(20), #电话 password varchar(32), #密码 username varchar(30), #用户的真实姓名 idcard varchar(20), #身份证号 gender char(2) #性别 )engine=innodb charset=utf8; ``` ### 后端部分 先利用CodeGenerate生成关于userinfo的所有代码。 ```java package com.han.syhis.controller; import com.han.syhis.entity.Userinfo; import com.han.syhis.service.IUserinfoService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-15 */ @RestController public class UserinfoController { @Resource private IUserinfoService userinfoService; //注册 @PostMapping("/register") @CrossOrigin public boolean register(@RequestBody Userinfo info){ return userinfoService.save(info); } } ``` ### 鸿蒙调用后端 image-20260615153103836 ```typescript import {Userinfo} from "../util/Entity"; import {post} from "../util/axios"; @Entry @Component struct register { @State userinfo: Userinfo = { gender:'男' } build() { Column(){ Text('注册新用户').fontSize(30).fontWeight(700) Text('石油医院挂号系统不会泄露您的隐私信息') .fontColor('#ff9188e').margin({top:10}) //网上见到的,往往是你输入完手机号之后,对方会给你发一个验证密码 //你验证码输入正确,才代表这个手机号是你的 //取回密码,也是通过手机发验证码来验证的 //如果通过代码,给对方手机发验证码 //阿里云市场 TextInput({placeholder:'请输入手机号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.phone = value }) TextInput({placeholder:'请输入密码'}).height(50) .margin({top:30}) .type(InputType.Password) .onChange((value) => { this.userinfo.password = value }) TextInput({placeholder:'请输入姓名'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.username = value }) TextInput({placeholder:'请输入身份证号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.idcard = value }) Row({space:20}){ Radio({value:'男',group:'sexgroup'}).checked(true) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '男' } }) Text('男').fontSize(20).margin({right:20}) Radio({value:'女',group:'sexgroup'}) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '女' } }) Text('女').fontSize(20).margin({right:20}) }.height(50).width('100%') .margin({top:20}).justifyContent(FlexAlign.Center) Button('注册') .width('85%').height(60).margin({top:30}) .shadow({radius:10,color:'#000000'}) .onClick(()=>{ console.log(JSON.stringify(this.userinfo)); post('/register',this.userinfo,(content:Boolean)=>{ console.log('注册成功'); }) }) }.width('100%') .alignItems(HorizontalAlign.Center) .padding({top:30}) } } ``` ### 密码加密 现在的密码是明文。111.数据库泄漏后,所有人都会知道你们的密码。 希望哪怕是数据库管理员,能够看到你的密文,但是根据你的密文,也推不出来你的明文密码。 加密:使用一些不可逆的加密算法,算出来的一个密文。 比如: 密码111111 密文:给你的密码按照位数*10%3取余数 8976532 可逆的。 ​ 不可逆:密码给你,你无法通过密码的密文推出明文密码。 #### 测试加密 1:在java项目中的pom.xml中新加入一个坐标,导入新的加密工具包。 ```xml cn.hutool hutool-all 5.8.16 ``` 2:可以在任何一个普通的java类中做测试了,新建立一个最普通的Java类。 ```java package com.han.syhis; import cn.hutool.crypto.digest.DigestUtil; public class Test { public static void main(String[] args) { String password = "123456"; // 待加密的密码 for (int i = 0; i < 10; i++) { String 密文 = DigestUtil.md5Hex(password); System.out.println("密文:" + 密文); } } } ``` 怎么用 ``` 明文:123456 密文:e10adc3949ba59abbe56e057f20f883e 数据存储的应该是:密文 用户记住的密码:明文 注册部分: 用户输入的事123456 存储在数据库中是e10adc3949ba59abbe56e057f20f883e 用户怎么登录: 与用户输入的密码123456,你的系统要把123456再一次进行加密,看加密后的密文和数据库存储的密文是否相等。 ``` #### 在项目注册中使用加密 更改的事后端的项目中UserinfoController的代码 ```java package com.han.syhis.controller; import cn.hutool.crypto.digest.DigestUtil; import com.han.syhis.entity.Userinfo; import com.han.syhis.service.IUserinfoService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-15 */ @RestController public class UserinfoController { @Resource private IUserinfoService userinfoService; //注册 @PostMapping("/register") @CrossOrigin public boolean register(@RequestBody Userinfo info){ //这个是昨天的注册,直接明文存储了密码 // return userinfoService.save(info); //下面是今天进行加密的代码 String password = info.getPassword();//取出用户输入的密码 password = DigestUtil.md5Hex( password);//对用户输入的密码进行加密 info.setPassword(password);//把这个加密后的password再放入对象 return userinfoService.save(info); } } ``` 继续访问你昨天的注册代码,那么发现数据库中密码已经存储为密文。 修改鸿蒙的界面 1:resources/profile/main_page.json ``` { "src": [ "pages/Index", "pages/dept", "pages/emplist", "pages/empitem", "pages/tab", "pages/register" ] } ``` 2: 修改注册页面 ```typescript import {Userinfo} from "../util/Entity"; import {post} from "../util/axios"; import { router } from "@kit.ArkUI"; @Entry @Component struct register { @State userinfo: Userinfo = { gender:'男' } build() { Column(){ Text('注册新用户').fontSize(30).fontWeight(700) Text('石油医院挂号系统不会泄露您的隐私信息') .fontColor('#ff9188e').margin({top:10}) //网上见到的,往往是你输入完手机号之后,对方会给你发一个验证密码 //你验证码输入正确,才代表这个手机号是你的 //取回密码,也是通过手机发验证码来验证的 //如果通过代码,给对方手机发验证码 //阿里云市场 TextInput({placeholder:'请输入手机号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.phone = value }) TextInput({placeholder:'请输入密码'}).height(50) .margin({top:30}) .type(InputType.Password) .onChange((value) => { this.userinfo.password = value }) TextInput({placeholder:'请输入姓名'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.username = value }) TextInput({placeholder:'请输入身份证号'}).height(50) .margin({top:30}) .onChange((value) => { this.userinfo.idcard = value }) Row({space:20}){ Radio({value:'男',group:'sexgroup'}).checked(true) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '男' } }) Text('男').fontSize(20).margin({right:20}) Radio({value:'女',group:'sexgroup'}) .onChange((isChecked:boolean) => { if(isChecked){ this.userinfo.gender = '女' } }) Text('女').fontSize(20).margin({right:20}) }.height(50).width('100%') .margin({top:20}).justifyContent(FlexAlign.Center) Button('注册') .width('85%').height(60).margin({top:30}) .shadow({radius:10,color:'#000000'}) .onClick(()=>{ console.log(JSON.stringify(this.userinfo)); const uiContext = this.getUIContext(); post('/register',this.userinfo,(content:Boolean)=>{ console.log('注册成功'); uiContext.getPromptAction().showToast({ message:'注册用户成功', duration:2000 //2秒钟之后,弹框自动消失 }) //延时两秒后自动跳转到登录页面 setTimeout(()=>{ console.log('准备跳转页面'); router.pushUrl({ url: 'pages/Index' }) },2000) }) }) }.width('100%') .alignItems(HorizontalAlign.Center) .padding({top:30}) } } ``` ## 登录 ### 后端 项目下Controller/UserinfoController这个类 ```java package com.han.syhis.controller; import cn.hutool.crypto.digest.DigestUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.han.syhis.entity.Userinfo; import com.han.syhis.service.IUserinfoService; import jakarta.annotation.Resource; import org.apache.catalina.User; import org.springframework.web.bind.annotation.*; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-15 */ @RestController public class UserinfoController { @Resource private IUserinfoService userinfoService; //登录 这段代码是新增加 @PostMapping("/login") public Userinfo login(@RequestBody Userinfo info){//存储的事手机号和密码 //需要把用户输入的密码进行加密,才能和数据库做对比 String password = info.getPassword();//取出用户输入的密码 password = DigestUtil.md5Hex( password);//对用户输入的密码进行加密 info.setPassword(password);//把这个加密后的password再放入对象 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("phone",info.getPhone()); //手机号和密码和数据库中的完全一致,合法用户 wrapper.eq("password",info.getPassword()); Userinfo newUserinfo = userinfoService.getOne(wrapper); return newUserinfo==null?new Userinfo():newUserinfo; } //注册 @PostMapping("/register") @CrossOrigin public boolean register(@RequestBody Userinfo info){ //这个是昨天的注册,直接明文存储了密码 // return userinfoService.save(info); //下面是今天进行加密的代码 String password = info.getPassword();//取出用户输入的密码 password = DigestUtil.md5Hex( password);//对用户输入的密码进行加密 info.setPassword(password);//把这个加密后的password再放入对象 return userinfoService.save(info); } } ``` ### 鸿蒙 image-20260616095339192 修改的事index.ets文件 ```typescript import { router } from "@kit.ArkUI"; import { post } from "../util/axios"; import {Userinfo} from "../util/Entity"; @Entry @Component struct Index { @State userinfo:Userinfo = { phone:'', password:'', } build() { Column({space:15}){ Image($r('app.media.huawei')).width(60) Text('石油医院挂号系统').fontSize(28).fontWeight(900) .margin({bottom:80}) TextInput({ placeholder:'请输入手机号' }).onChange((value)=>{ this.userinfo.phone = value }) TextInput({ placeholder:'请输入密码' }).onChange((value)=>{ this.userinfo.password = value }).type(InputType.Password) Button('登录').width('100%').onClick(()=>{ console.log('开始登录'); //1:用户名密码不正确 提示 //2:用户密码正确 跳转页面 const uiContext = this.getUIContext(); post('login',this.userinfo,(content:Userinfo)=>{ if(content.userid!=null){ console.log('登陆成功'); uiContext.getPromptAction().showToast({ message: '登录成功,即将跳转到首页', duration: 2000 }) setTimeout(()=>{ router.pushUrl({ url:'pages/tab' }) },2000) }else{ console.log('登录失败'); uiContext.showAlertDialog({ message: '用户名或密码错误,请重新输入' }) } }) }) Row(){ Image($r('app.media.zfb_tab_me')).width(20) .fillColor(Color.Red).margin({right:10}) Text('前往注册') .margin({right:50}) .onClick(()=>{ router.pushUrl({ url:'pages/register' }) }) Image($r('app.media.ic_more')).width(20) .fillColor(Color.Green) Text('忘记密码').margin({left:10}) } }.padding(20) .width('100%').height('100%') .alignItems(HorizontalAlign.Center) //最左边 .justifyContent(FlexAlign.Center) // 中间 } } ``` ### @AppStorage @Provider @Consumer @Prop @Link 都能够实现传参。跨组件传参。 @AppStorage是全局的UI状态管理中心。简单来说就是一个单例对象,他在项目中维系了一个实例。能够实现跨多个文件,多也页面,多个组件中共享变量的问题。一般用它来做存储的登录信息。放在内存中全局共享。可以和PersisterentStrorage进行配合,能够完成持久化。帮祝你完成记住用户名和密码的功能。 #### 1:存储信息 ``` AppStorage.seteOrCreate(key,value) 能够把数据存储起来,全局性质的,供你今后在其他地方使用 ``` 在Index.ets文件中,登录成功之后的代码里面加了这么一句话 ```typescript AppStorage.setOrCreate('userinfo',content); ``` 在整个项目的生命周期中,都可以取到这个存储起来元素。 #### 2:取信息 在你的项目的任何地方,的任何代码里。都可以直接获取存储的元素。 ```typescript @StrorageLink('userinfo') userinfo:Userinfo = {} as Userinfo ``` 我们就获取到,是那个登录的用户信息。 在my.ets文件中进行测试 ```typescript import {Userinfo} from "../util/Entity"; @Entry @Component export struct my { @StorageLink("userinfo") userinfo:Userinfo = {} as Userinfo aboutToAppear(): void { console.log('当前登录的用户是:'+this.userinfo.userid+" "+this.userinfo.phone); } build() { //上下两部分 //上面 左边箭头 个人中心 右边 更多的图标 //下面 个人的详细信息 挂号信息 Column(){ Row(){ Image($r('app.media.ic_arrow_left')).width(30) Text('个人中心') Image($r('app.media.ic_more')).width(24) }.width('100%') .backgroundColor('#ccc') .height(40) .justifyContent(FlexAlign.SpaceBetween) .padding({left:10,right:10}) Column(){ List({space:15}){ ForEach(Array.from({length:3}),()=>{ ListItem(){ Column(){ Grid(){ GridItem(){Text('就诊人:')}.align(Alignment.Start) GridItem(){Text('严秋婷')}.align(Alignment.Start) GridItem(){Text('科室:')}.align(Alignment.Start) GridItem(){Text('眼科')}.align(Alignment.Start) GridItem(){Text('年龄:')}.align(Alignment.Start) GridItem(){Text('21')}.align(Alignment.Start) GridItem(){Text('医生:')}.align(Alignment.Start) GridItem(){Text('刘德华')}.align(Alignment.Start) GridItem(){Text('性别:')}.align(Alignment.Start) GridItem(){Text('女')}.align(Alignment.Start) GridItem(){Text('挂号费:')}.align(Alignment.Start) GridItem(){Text('¥60.00')}.align(Alignment.Start) GridItem(){Text('时间')}.align(Alignment.Start) GridItem(){Text('2026-09-09')}.align(Alignment.Start) GridItem(){Text('时间段')}.align(Alignment.Start) GridItem(){Text('8.00-9.00')}.align(Alignment.Start) }.rowsTemplate('1fr 1fr 1fr 1fr') .columnsTemplate('2fr 3fr 2fr 3fr') .width('90%').height(120) .margin({top:5,left:10}) } } .border({color:'#ccc',width:{bottom:1}}) }) } }.backgroundColor(Color.White).width('100%').height('100%') .backgroundColor('#eeeeee') }.width('100%') .height('100%') .backgroundColor('#ccc') .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM]) } } ``` ## 导航栈 image-20260616140618953 1:api画了一个横线,代表就是API过时了。 2:router.putshUrl 跳转,整个页面进行跳转,而不是组件间进行跳转 3:HarmonyOS Next版本中,推荐使用导航栈,而不是router.putshUrl ### 基础的导航栈讲解 AStack.ets ```typescript import { BStack } from "./BStack"; import { CStack } from "./CStack"; export interface PageParam{ deptid?:number } @Entry @Component struct AStack { //在A点击去B,在B里面点击去C //router.pushUrl还需要把页面写在main_page.json中。 //而导航栈不用这个操作,而是全部写在程序的入口页面 //创建一个导航栈,并通过@Provider提供给子组件使用 //1:声明导航栈 @Provide("pathStack") pageStack:NavPathStack = new NavPathStack(); build() { //使用Navigation作为根容器 //2:把页面所有内容放入导航容器 Navigation(this.pageStack){ Column(){//如果你要采用导航栈,根节点不能是Colun,而必须是导航栈作为根节点 Text('这是A页面') Button('我要去B').onClick(()=>{ //把B页面压入导航栈 //声明一个对象作为参数传递给下一个页面 const params:PageParam = {deptid:1} //第二个参数就是你要传递的参数 this.pageStack.pushPathByName('BStack',params) }) } }.mode(NavigationMode.Stack) .navDestination(this.PageMap) } //3:构造所有页面导航栈的规则 @Builder PageMap(name:string){ if(name=='BStack'){ BStack(); }else if (name=='CStack') { CStack() } } } ``` BStack.ets ```typescript import { PageParam } from "./AStack"; @Component export struct BStack { //在A点击去B,在B里面点击去C //router.pushUrl还需要把页面写在main_page.json中。 //而导航栈不用这个操作,而是全部写在程序的入口页面 //接收导航栈 @Consume("pathStack") pathStack:NavPathStack; //这个值是从父组件来的 aboutToAppear(): void { //准备接受参数 const param = this.pathStack.getParamByName("BStack")[0] as PageParam console.log('接收到参数'+param.deptid); } build() { NavDestination(){ Column(){ Text('这是B页面') Button('我要去C').onClick(()=>{ this.pathStack.pushPathByName('CStack',null); }) }.width('100%') }.hideTitleBar( true) } } ``` CStack.ets ```typescript @Component export struct CStack { //在A点击去B,在B里面点击去C //router.pushUrl还需要把页面写在main_page.json中。 //而导航栈不用这个操作,而是全部写在程序的入口页面 build() { NavDestination(){ Column(){ Text('这是C页面') } } } } ``` ### 部门,医生, 医生详情改成导航栈 我需要改三个页面 dept.ets emplist.ets empitem.ets文件。 1:dept.ets ``` 1:声明导航栈 2:把跟节点容器放入Navigateion跟容器 3:声明导航规则 ``` ```typescript import axios,{AxiosResponse} from "@ohos/axios"; //导入远程的请求类 import { get } from "../util/axios"; //导入自定义的包装类 import {Dept} from "../util/Entity"; import { router } from "@kit.ArkUI"; import {emplist} from "./emplist"; import {empitem} from "./empitem"; export interface PageParams{ deptid?:number, doctorid?:number } @Component export struct dept { //声明创建一个导航栈 @Provide("pathStack") pageStack:NavPathStack = new NavPathStack(); @State deptList:Array = [] aboutToAppear(): void { //准备用包装类axios.js来发起请求 get('/dept/findAll',{},(content:Array)=>{ this.deptList = content; console.log('所有部门',this.deptList); }) } build() { Navigation(this.pageStack){ List(){ ForEach(this.deptList,(item:Dept)=> {//ForEach(Array.from({length:20}),()=>{ ListItem(){ //list里面必须而且只能放 ListItem组件 Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.SpaceBetween}){ Text(item.deptname) Image($r('app.media.right')).width(30) }.border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ console.log('被按下了'+item.deptid); //把后面注释掉的代码改成用导航栈进行跳转和传参 const params:PageParams = {deptid:item.deptid} this.pageStack.pushPathByName("emplist",params) // router.pushUrl({ // url: 'pages/emplist', // params:{deptid:item.deptid} // }) }) }.padding(10) }) }.width('100%').layoutWeight(1) //高度自适应 }.mode(NavigationMode.Stack) .navDestination(this.PageMap) } @Builder PageMap(name:string){ if(name=="emplist"){ emplist() }else if(name=="empitem"){ empitem() } } } ``` 2:emplist.ets ``` 1: 接收导航栈 2:把跟节点容器放入Navigateion跟容器 3:得接收父容器传入过来的参数 ``` ```typescript import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict,Emp} from "../util/Entity"; import {get} from "../util/axios"; import { router } from "@kit.ArkUI"; import { PageParams } from "./dept"; @Component export struct emplist { @Consume("pathStack") pageStack:NavPathStack @State dateList:Array = [] @State empList:Array = [] @State deptid:number = 1 aboutToAppear(): void { //准备接受前面传过来的参数 //这个对象的功能就是用来接受前面的路由给我传过来参数 //这总取参数的方式是router的方式去参数已经过时了 // const params:Record = router.getParams() as Record const params = this.pageStack.getParamByName("emplist")[0] as PageParams this.deptid = params.deptid??1; this.getDates() //在此地准备发起远程请求 get('/emp/findByDeptid?deptid='+this.deptid,{},(content:Array)=>{ this.empList = content; console.log('获取当前部门下的员工',this.empList) }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { NavDestination(){ Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(this.empList,(item:Emp)=>{//ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text(item.empname+' '+item.levelname).fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ console.log('测试,去跳转医生详情页面'); //跳转到医生挂号的页面 this.pageStack.pushPathByName("empitem",null) // router.pushUrl({ // url: 'pages/empitem' // }) }) } }) }.width('100%').layoutWeight(1) } }.hideTitleBar(true) } } ``` 3: empitem ```typescript import { timebetween } from "../util/Entity"; @Component export struct empitem { @Consume("pathStack") pageStack:NavPathStack build() { NavDestination(){ Column(){ //包含了医生的头像和详细信息 Row(){ Column(){//左边的列 Image($r('app.media.doctor')).width(70) Text('关注').fontColor('#ffffff') .border({color:'#ffffff',width:1}) .margin({top:8}) }.width(80).margin({left:20}) Column(){//右边的列 Text('吴玉林主任医师').fontSize(24).fontColor('#ffffff') .fontWeight(700) }.layoutWeight(1) }.height(140).backgroundColor('#575ea9').width('100%') //这个医生那天可以挂号的日期范围 Row(){ Text('门诊就医').fontSize(24).fontWeight(700) Text('需要医院').fontSize(20).fontColor('#666666') }.width('100%').margin({top:20,left:20}) //具体的日期框,依然是五个 List({space:15}){ ForEach(Array.from({length:5}),()=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text('星期六') Text('6/12') }.borderWidth(3).height(80).borderRadius(7) .layoutWeight(1) } }) }.listDirection(Axis.Horizontal) .height(100).margin({top:20}) //挂号的时间段 和 可以挂号的数量 Text('预约时间').height(50).width('90%') .border({color:'#cccccc',width:{bottom:1}}) List({space:15}){ ForEach(timebetween,(item:string)=>{ ListItem(){ Text(item).textAlign(TextAlign.Center) .width('100%').fontWeight(700) }.height(50) .border({color:'#cccccc',width:{bottom:1}}) .width('100%').margin({left:20}) }) }.height(300) //医生挂号的费用 Row(){ Text('主任医师').fontSize(24).fontWeight(700) Text('¥50.00').fontColor('#ff0000').fontSize(24).fontWeight(700) }.justifyContent(FlexAlign.SpaceBetween) .width('90%') }.width('100%').height('100%') }.hideTitleBar(true) } } ``` ## 医生 image-20260617092959849 ### 后端 EmpMapper.java ```java package com.han.syhis.mapper; import com.han.syhis.entity.Emp; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** *

* Mapper 接口 *

* * @author laohan * @since 2026-06-11 */ @Mapper public interface EmpMapper extends BaseMapper { @Select("select * from emp,emplevel where emp.levelid=emplevel.levelid and deptid=#{deptid}") public List findByDeptid(@Param("deptid") Integer deptid); @Select("select * from emp,emplevel where emp.levelid=emplevel.levelid and empid=#{empid}") public Emp findByempid(@Param("empid") Integer empid); } ``` EmpController.java ```java package com.han.syhis.controller; import com.han.syhis.entity.Emp; import com.han.syhis.mapper.EmpMapper; import com.han.syhis.service.IEmpService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; import java.util.List; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-11 */ @RestController public class EmpController { @Resource private IEmpService empService; @GetMapping("/emp/findByDeptid") @CrossOrigin public List findAll(@RequestParam("deptid") Integer deptid){ return empService.findByDeptid(deptid); } @Resource private EmpMapper empMapper; @GetMapping("/emp/findByempid") @CrossOrigin public Emp empid(@RequestParam("empid") Integer empid){ return empMapper.findByempid(empid); } } ``` ``` http://localhost:8080/emp/findByempid?empid=2 测试地址 ``` ### 前端 entity.ts ``` //封装了你将来要获取的页面数据 export interface Checkin { checkinid?:number, deptid?:number, //#号挂在那个部门下 empid?:number, //#号挂在那个医生下 userid?:number, //#谁挂的号 money?:number, //#挂号费 checkdate?:string, checktime?:string } export interface Userinfo { userid?: number, //加?代表的意思是这个值可以有,也可以没有 如果不加?那么这个值必须存在 phone?:string, password?:string, username?:string, idcard?:string, gender?:string } export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string, alldate:string } export interface Emp{ empid?: number, empname?: string, deptid?: number, levelid?: number, levelname?: string, money?: number } export const timebetween:Array=[ '08.00-09.00', '09.00-10.00', '10.00-11.00', '11.00-12.00', '14.00-15.00', '15.00-16.00', '16.00-17.00', '17.00-18.00' ] ``` emplist.ets ``` import dayjs, { Dayjs } from "@hview/dayjs"; import {weekDict,Emp} from "../util/Entity"; import {get} from "../util/axios"; import { router } from "@kit.ArkUI"; import { PageParams } from "./dept"; @Component export struct emplist { @Consume("pathStack") pageStack:NavPathStack @State dateList:Array = [] @State empList:Array = [] @State deptid:number = 1 aboutToAppear(): void { //准备接受前面传过来的参数 const params = this.pageStack.getParamByName("emplist")[0] as PageParams this.deptid = params.deptid??1; this.getDates() //在此地准备发起远程请求 get('/emp/findByDeptid?deptid='+this.deptid,{},(content:Array)=>{ this.empList = content; console.log('获取当前部门下的员工',this.empList) }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D'), alldate: date.format("YYYY-MM-DD") }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { NavDestination(){ Column(){ //写详细的日切列表 List(){ //循环的事日期 ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.width(60).height(60) .border({style:BorderStyle.Solid,width:{bottom:3}}) } }) }.listDirection(Axis.Horizontal) .height(80) List(){//专家号列表 ForEach(this.empList,(item:Emp)=>{//ForEach(Array.from({length:20}),()=>{ ListItem(){ Flex({alignItems:ItemAlign.Center}){ Image($r('app.media.emp')).width(60) Text(item.empname+' '+item.levelname).fontSize(18).fontWeight(700) .margin({left:30}) }.margin({left:15,bottom:15}) .border({color:'#cccccc',style:BorderStyle.Solid,width:{bottom:1}}) .onClick(()=>{ console.log('测试,去跳转医生详情页面'); //跳转到医生挂号的页面 const params:PageParams = {empid:item.empid} this.pageStack.pushPathByName("empitem",params) }) } }) }.width('100%').layoutWeight(1) } }.hideTitleBar(true) } } ``` empitem.ets ```typescript import { timebetween , weekDict,Emp, Checkin,Userinfo } from "../util/Entity"; import dayjs, { Dayjs } from "@hview/dayjs"; import { PageParams } from "./dept"; import {get, post} from "../util/axios"; @Component export struct empitem { @Consume("pathStack") pageStack:NavPathStack @State dateList:Array = [] @State selected:number = 0;//你选中了那个日期 @State empid:number = 1 @State emp:Emp = {} @State checkin:Checkin = {} @StorageLink("userinfo") userinfo:Userinfo = {} as Userinfo aboutToAppear(): void { console.log('当前登录的用户是:'+this.userinfo.userid+" "+this.userinfo.phone); this.checkin.userid = this.userinfo.userid; this.getDates() //准备接受前面传过来的参数 const params = this.pageStack.getParamByName("empitem")[0] as PageParams this.empid = params.empid??1; this.checkin.deptid = 0 //查询后端的数据了 get('/emp/findByempid',{empid:this.empid},(content:Emp)=>{ this.emp = content console.log(JSON.stringify(this.emp)); }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D'), alldate : date.format('YYYY-MM-DD') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { NavDestination(){ //包含了医生的头像和详细信息 Row(){ Column(){//左边的列 Image($r('app.media.doctor')).width(70) Text('关注').fontColor('#ffffff') .border({color:'#ffffff',width:1}) .margin({top:8}) }.width(80).margin({left:20}) Column(){//右边的列 Text(this.emp.empname+' '+this.emp.levelname).fontSize(24).fontColor('#ffffff') .fontWeight(700) }.layoutWeight(1) }.height(140).backgroundColor('#575ea9').width('100%') //这个医生那天可以挂号的日期范围 Row(){ Text('门诊就医').fontSize(24).fontWeight(700) Text('需要医院').fontSize(20).fontColor('#666666') }.width('100%').margin({top:20,left:20}) //具体的日期框,依然是五个 List({space:15}){ ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.borderWidth(3).height(80).borderRadius(7) .borderColor(index==this.selected?'#575ea9':'transparent') .layoutWeight(1) .onClick(()=>{ this.selected = index }) } }) }.listDirection(Axis.Horizontal) .height(100).margin({top:20}) //挂号的时间段 和 可以挂号的数量 Text('预约时间').height(50).width('90%') .border({color:'#cccccc',width:{bottom:1}}) List({space:15}){ ForEach(timebetween,(item:string)=>{ ListItem(){ Text(item).textAlign(TextAlign.Center) .width('100%').fontWeight(700) .onClick(() => { AlertDialog.show({ title: '提示', message: `日期: ${this.dateList[this.selected].alldate} \n 时间段:${item} \n 确定要预约吗?`, confirm:{ value:'确定', action:()=>{ //申请页面数据的封装 提交给axios远程请求 this.checkin.checkdate = this.dateList[this.selected].alldate this.checkin.checktime = item this.checkin.empid = this.empid this.checkin.money = this.emp.money console.log('发起预约请求了'+JSON.stringify(this.checkin)); const uicontext = this.getUIContext() post('/checkin',this.checkin,(content:boolean)=>{ console.log('挂号成功'); uicontext.showAlertDialog({ message: '您已经成功挂号了,请与当前及时到院就医' }) }) } } }) }) }.height(50) .border({color:'#cccccc',width:{bottom:1}}) .width('100%').margin({left:20}) }) }.height(250) //医生挂号的费用 Row(){ Text(this.emp.levelname).fontSize(24).fontWeight(700) Text('¥'+this.emp.money).fontColor('#ff0000').fontSize(24).fontWeight(700) }.justifyContent(FlexAlign.SpaceBetween) .width('90%') }.width('100%').height('100%') .hideTitleBar(true) } } ``` ## 挂号 挂号信息是要存储在表里面的。 ### 数据库 ```mysql drop table if EXISTS checkin ; #删除表,如果存在话 create table checkin( #创建表 checkinid int auto_increment primary key, deptid int, #号挂在那个部门下 empid int, #号挂在那个医生下 userid int, #谁挂的号 money DECIMAL(8,2), #挂号费 checkdate date, checktime varchar(20) )engine=innodb charset=utf8; ``` ### 后端 这个基本上就是用你生代码那一套,再加一个Controller的save方法就ok 了。 1:生代码 2:CheckinControll ```java package com.han.syhis.controller; import com.han.syhis.entity.Checkin; import com.han.syhis.service.ICheckinService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-17 */ @RestController public class CheckinController { @Resource private ICheckinService checkinService; @PostMapping("/checkin") @CrossOrigin public boolean checkin(@RequestBody Checkin checkin){ return checkinService.save(checkin); } } ``` ### 前端 image-20260617100937361 Entity.ts ```json //封装了你将来要获取的页面数据 export interface Checkin { checkinid?:number, deptid?:number, //#号挂在那个部门下 empid?:number, //#号挂在那个医生下 userid?:number, //#谁挂的号 money?:number, //#挂号费 checkdate?:string, checktime?:string } export interface Userinfo { userid?: number, //加?代表的意思是这个值可以有,也可以没有 如果不加?那么这个值必须存在 phone?:string, password?:string, username?:string, idcard?:string, gender?:string } export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string, alldate:string } export interface Emp{ empid?: number, empname?: string, deptid?: number, levelid?: number, levelname?: string, money?: number } export const timebetween:Array=[ '08.00-09.00', '09.00-10.00', '10.00-11.00', '11.00-12.00', '14.00-15.00', '15.00-16.00', '16.00-17.00', '17.00-18.00' ] ``` empitem.ets ```typescript import { timebetween , weekDict,Emp, Checkin,Userinfo } from "../util/Entity"; import dayjs, { Dayjs } from "@hview/dayjs"; import { PageParams } from "./dept"; import {get, post} from "../util/axios"; @Component export struct empitem { @Consume("pathStack") pageStack:NavPathStack @State dateList:Array = [] @State selected:number = 0;//你选中了那个日期 @State empid:number = 1 @State emp:Emp = {} @State checkin:Checkin = {} @StorageLink("userinfo") userinfo:Userinfo = {} as Userinfo aboutToAppear(): void { console.log('当前登录的用户是:'+this.userinfo.userid+" "+this.userinfo.phone); this.checkin.userid = this.userinfo.userid; this.getDates() //准备接受前面传过来的参数 const params = this.pageStack.getParamByName("empitem")[0] as PageParams this.empid = params.empid??1; this.checkin.deptid = 0 //查询后端的数据了 get('/emp/findByempid',{empid:this.empid},(content:Emp)=>{ this.emp = content console.log(JSON.stringify(this.emp)); }) } getDates(){ //还要有一个挂号的日期 //肯定是将来的日期,一般都是能挂明天的日期,能挂明天开始5天的日期 // const today = dayjs(new Date()) // console.log('today is ',today)//今天 // const tommorw = today.add(1,'day')//明天 // console.log('tommorw is ',tommorw)//今天 //得到后五天的时间 const today = dayjs(new Date()) for(let i=1;i<=5;i++){ const date = today.add(i,'day'); console.log('========'+date.day()+' '+date.format('M-D'));//星期几 this.dateList.push({ week: date.day(), date: date.format('M-D'), alldate : date.format('YYYY-MM-DD') }) } } getChineseWeek(week:number){ if(week==1){ return '星期一' }else if(week==2){ return '星期二' }else if(week==3){ return '星期三' }else if(week==4){ return '星期四' }else if(week==5){ return '星期五' }else if(week==6){ return '星期六' }else if(week==0){ return '星期日' }else{ return '星期天' } } build() { NavDestination(){ //包含了医生的头像和详细信息 Row(){ Column(){//左边的列 Image($r('app.media.doctor')).width(70) Text('关注').fontColor('#ffffff') .border({color:'#ffffff',width:1}) .margin({top:8}) }.width(80).margin({left:20}) Column(){//右边的列 Text(this.emp.empname+' '+this.emp.levelname).fontSize(24).fontColor('#ffffff') .fontWeight(700) }.layoutWeight(1) }.height(140).backgroundColor('#575ea9').width('100%') //这个医生那天可以挂号的日期范围 Row(){ Text('门诊就医').fontSize(24).fontWeight(700) Text('需要医院').fontSize(20).fontColor('#666666') }.width('100%').margin({top:20,left:20}) //具体的日期框,依然是五个 List({space:15}){ ForEach(this.dateList,(item:weekDict,index:number)=>{ ListItem(){ Flex({direction:FlexDirection.Column,alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){ Text(this.getChineseWeek(item.week)).fontSize(18) .fontWeight(700) Text(item.date) }.borderWidth(3).height(80).borderRadius(7) .borderColor(index==this.selected?'#575ea9':'transparent') .layoutWeight(1) .onClick(()=>{ this.selected = index }) } }) }.listDirection(Axis.Horizontal) .height(100).margin({top:20}) //挂号的时间段 和 可以挂号的数量 Text('预约时间').height(50).width('90%') .border({color:'#cccccc',width:{bottom:1}}) List({space:15}){ ForEach(timebetween,(item:string)=>{ ListItem(){ Text(item).textAlign(TextAlign.Center) .width('100%').fontWeight(700) .onClick(() => { AlertDialog.show({ title: '提示', message: `日期: ${this.dateList[this.selected].alldate} \n 时间段:${item} \n 确定要预约吗?`, confirm:{ value:'确定', action:()=>{ //申请页面数据的封装 提交给axios远程请求 this.checkin.checkdate = this.dateList[this.selected].alldate this.checkin.checktime = item this.checkin.empid = this.empid this.checkin.money = this.emp.money console.log('发起预约请求了'+JSON.stringify(this.checkin)); const uicontext = this.getUIContext() post('/checkin',this.checkin,(content:boolean)=>{ console.log('挂号成功'); uicontext.showAlertDialog({ message: '您已经成功挂号了,请与当前及时到院就医' }) }) } } }) }) }.height(50) .border({color:'#cccccc',width:{bottom:1}}) .width('100%').margin({left:20}) }) }.height(250) //医生挂号的费用 Row(){ Text(this.emp.levelname).fontSize(24).fontWeight(700) Text('¥'+this.emp.money).fontColor('#ff0000').fontSize(24).fontWeight(700) }.justifyContent(FlexAlign.SpaceBetween) .width('90%') }.width('100%').height('100%') .hideTitleBar(true) } } ``` ## 我的 ### 数据表查询 ```mysql #查询所有的挂号信息 select * from checkin; #查询某用户的挂号信息 select * from checkin where userid=3; #会不会写复杂SQL语句 #多表的数据的关联点一般都是主键对应另外一个表引用 #多表查询的根本点 模拟 #关联两张表的查询 select * from a,b where a.数据=b.数据 #三张表的查询 select * from a,b,c where a.数据=b.数据 and b.数据=c.数据 #四张表的查询 select * from a,b,c,d where a.数据=b.数据 and b.数据=c.数据 and c.数据=d.数据 #别名 外号 select * from checkin #上下这两条SQL其实完全一样 select * from checkin a #别名是可以到字段上去的 select userid from checkin #查了数据表中的一个字段 select userid '用户id' from checkin ########################################################## #查询某用户的挂号信息 select * from checkin where userid=3; #checkin表和Userinfo表的对应关系是userid #搞定了就诊人是谁 select a.*,b.username from checkin a,userinfo b where a.userid=b.userid and a.userid=3 #还缺医生 关联医生表 #解决了医生 select a.*,b.username,c.empname from checkin a,userinfo b ,emp c where a.userid=b.userid and a.userid=3 and a.empid=c.empid #还缺科室 #现在checkin表没有deptid的数据,但是emp表里有deptid select a.*,b.gender,b.username,c.empname,d.deptname from checkin a,userinfo b ,emp c,dept d where a.userid=b.userid and a.userid=3 and a.empid=c.empid and d.deptid=c.deptid ``` ### 后台 CheckinMapper.java ```java package com.han.syhis.mapper; import com.han.syhis.entity.Checkin; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** *

* Mapper 接口 *

* * @author laohan * @since 2026-06-17 */ @Mapper public interface CheckinMapper extends BaseMapper { @Select("select a.*,b.gender,b.username,c.empname,d.deptname from checkin a,userinfo b ,emp c,dept d\n" + " where a.userid=b.userid and a.userid=#{userid}\n" + " and a.empid=c.empid\n" + " and d.deptid=c.deptid") public List findByUserid(@Param("userid") Integer userid); } ``` Checkin.java ```java package com.han.syhis.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; import lombok.Getter; import lombok.Setter; /** *

* *

* * @author laohan * @since 2026-06-17 */ @Getter @Setter public class Checkin implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "checkinid", type = IdType.AUTO) private Integer checkinid; @Override public String toString() { return "Checkin{" + "checkinid=" + checkinid + ", deptid=" + deptid + ", empid=" + empid + ", userid=" + userid + ", money=" + money + ", checkdate=" + checkdate + ", checktime='" + checktime + '\'' + '}'; } private Integer deptid; private Integer empid; private Integer userid; private BigDecimal money; private LocalDate checkdate; private String checktime; //如下4个是新增的 private String gender; private String username; private String empname; private String deptname; } ``` CheckinControll.java ```java package com.han.syhis.controller; import com.han.syhis.entity.Checkin; import com.han.syhis.mapper.CheckinMapper; import com.han.syhis.service.ICheckinService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; import java.util.List; /** *

* 前端控制器 *

* * @author laohan * @since 2026-06-17 */ @RestController public class CheckinController { @Resource private ICheckinService checkinService; @PostMapping("/checkin") @CrossOrigin public boolean checkin(@RequestBody Checkin checkin){ System.out.println("获取到前端的数据 "+checkin); return checkinService.save(checkin); } //看我自己的挂号 @Resource private CheckinMapper checkinMapper; @GetMapping("/checkin/findByUserid") public List findByUserid(@RequestParam("userid") Integer userid){ return checkinMapper.findByUserid(userid); } } ``` ``` http://localhost:8080/checkin/findByUserid?userid=3 测试 ``` ### 前台 image-20260617150535652 Entity.ts ``` //封装了你将来要获取的页面数据 export interface Checkin { checkinid?:number, deptid?:number, //#号挂在那个部门下 empid?:number, //#号挂在那个医生下 userid?:number, //#谁挂的号 money?:number, //#挂号费 checkdate?:string, checktime?:string gender?:string //新增的字段 username?:string //新增的字段 empname?:string //新增的字段 deptname?:string //新增的字段 } export interface Userinfo { userid?: number, //加?代表的意思是这个值可以有,也可以没有 如果不加?那么这个值必须存在 phone?:string, password?:string, username?:string, idcard?:string, gender?:string } export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string, alldate:string } export interface Emp{ empid?: number, empname?: string, deptid?: number, levelid?: number, levelname?: string, money?: number } export const timebetween:Array=[ '08.00-09.00', '09.00-10.00', '10.00-11.00', '11.00-12.00', '14.00-15.00', '15.00-16.00', '16.00-17.00', '17.00-18.00' ] ``` my.ets ```typescript import { Checkin, Userinfo} from "../util/Entity"; import { get } from "../util/axios"; @Entry @Component export struct my { @StorageLink("userinfo") userinfo:Userinfo = {} as Userinfo @State checkins:Array = [] aboutToAppear(): void { console.log('当前登录的用户是:'+this.userinfo.userid+" "+this.userinfo.phone); //准备发起远程请求获取所有的我的挂号信息 get('/checkin/findByUserid',{userid:this.userinfo.userid},(content:Array)=>{ this.checkins = content; console.log(JSON.stringify(this.checkins)); }) } build() { //上下两部分 //上面 左边箭头 个人中心 右边 更多的图标 //下面 个人的详细信息 挂号信息 Column(){ Row(){ Image($r('app.media.ic_arrow_left')).width(30) Text('个人中心') Image($r('app.media.ic_more')).width(24) }.width('100%') .backgroundColor('#ccc') .height(40) .justifyContent(FlexAlign.SpaceBetween) .padding({left:10,right:10}) Column(){ List({space:15}){ ForEach(this.checkins,(item:Checkin)=>{ // ForEach(Array.from({length:3}),()=>{ ListItem(){ Column(){ Grid(){ GridItem(){Text('就诊人:')}.align(Alignment.Start) GridItem(){Text(item.username)}.align(Alignment.Start) GridItem(){Text('科室:')}.align(Alignment.Start) GridItem(){Text(item.deptname)}.align(Alignment.Start) GridItem(){Text('年龄:')}.align(Alignment.Start) GridItem(){Text('21')}.align(Alignment.Start) GridItem(){Text('医生:')}.align(Alignment.Start) GridItem(){Text(item.empname)}.align(Alignment.Start) GridItem(){Text('性别:')}.align(Alignment.Start) GridItem(){Text(item.gender)}.align(Alignment.Start) GridItem(){Text('挂号费:')}.align(Alignment.Start) GridItem(){Text('¥'+item.money)}.align(Alignment.Start) GridItem(){Text('时间')}.align(Alignment.Start) GridItem(){Text(item.checkdate)}.align(Alignment.Start) GridItem(){Text('时间段')}.align(Alignment.Start) GridItem(){Text(item.checktime)}.align(Alignment.Start) }.rowsTemplate('1fr 1fr 1fr 1fr') .columnsTemplate('2fr 3fr 2fr 3fr') .width('90%').height(120) .margin({top:5,left:10}) } } .border({color:'#ccc',width:{bottom:1}}) }) } }.backgroundColor(Color.White).width('100%').height('100%') .backgroundColor('#eeeeee') }.width('100%') .height('100%') .backgroundColor('#ccc') .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM]) } } ``` ## 药品 ``` drop table if EXISTS medic; CREATE TABLE IF NOT EXISTS medic ( id INT PRIMARY KEY, image VARCHAR(200) NOT NULL COMMENT '图片路径', goodname VARCHAR(100) NOT NULL COMMENT '药品名称', price DECIMAL(10,2) NOT NULL COMMENT '价格', comments INT DEFAULT 0 COMMENT '评论数' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='药品信息表'; INSERT INTO medic (id, image, goodname, price, comments) VALUES (1, 'http://localhost:8080/medic01.png', '阿莫西林胶囊', 28.50, 2345), (2, 'http://localhost:8080/medic02.png', '布洛芬缓释胶囊', 15.80, 1890), (3, 'http://localhost:8080/medic03.jpg', '维生素C片', 32.00, 3200), (4, 'http://localhost:8080/medic04.png', '创可贴', 9.90, 1567), (5, 'http://localhost:8080/medic05.png', '板蓝根颗粒', 18.50, 2789), (6, 'http://localhost:8080/medic06.png', '感冒灵颗粒', 22.00, 3456), (7, 'http://localhost:8080/medic07.png', '头孢克肟片', 35.00, 1234), (8, 'http://localhost:8080/medic08.png', '止咳糖浆', 26.50, 2100), (9, 'http://localhost:8080/medic09.png', '蒙脱石散', 19.80, 1678), (10, 'http://localhost:8080/medic10.png', '健胃消食片', 12.50, 2987), (11, 'http://localhost:8080/medic11.jpg', '氯雷他定片', 24.00, 1432), (12, 'http://localhost:8080/medic12.jpg', '红霉素软膏', 8.50, 3541), (13, 'http://localhost:8080/medic13.jpg', '云南白药气雾剂', 68.00, 987), (14, 'http://localhost:8080/medic14.jpg', '藿香正气水', 15.00, 2654), (15, 'http://localhost:8080/medic15.jpg', '六味地黄丸', 45.00, 1876), (16, 'http://localhost:8080/medic16.jpg', '复方丹参滴丸', 38.50, 1543), (17, 'http://localhost:8080/medic17.jpg', '葡萄糖酸钙口服液', 42.00, 2234), (18, 'http://localhost:8080/medic18.jpg', '维生素B族片', 55.00, 1765), (19, 'http://localhost:8080/medic19.jpg', '伊曲康唑胶囊', 88.00, 654), (20, 'http://localhost:8080/medic20.jpg', '奥美拉唑肠溶片', 36.00, 2456), (21, 'http://localhost:8080/medic21.jpg', '硝酸甘油片', 29.00, 1321), (22, 'http://localhost:8080/medic22.jpg', '安眠药片', 56.00, 998), (23, 'http://localhost:8080/medic23.jpg', '小儿氨酚黄那敏颗粒', 23.50, 2876), (24, 'http://localhost:8080/medic24.jpg', '开塞露', 6.50, 4123), (25, 'http://localhost:8080/medic25.jpg', '眼药水', 19.00, 3450), (26, 'http://localhost:8080/medic26.jpg', '钙尔奇D片', 128.00, 789); select * from medic; ``` 后端直接生成代码,唯一要改就是MedicController.java ```java package com.han.syhis.controller; import com.han.syhis.service.IMedicService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** *

* 药品信息表 前端控制器 *

* * @author laohan * @since 2026-06-17 */ @RestController public class MedicController { @Resource private IMedicService medicService; @GetMapping("/medic/findAll") public List findAll(){ return medicService.list(); } } ``` 你如何把媒体文件发布成互联网文件。 image-20260617152011354 把你原来的鸿蒙里面的那些图片,直接复制到后端idea的项目的resources/static/就可以了直接通过浏览器访问了。 ### 鸿蒙前端 utils/entity.ts ``` //封装了你将来要获取的页面数据 export interface Medic { id?: number, image?: string, goodname?: string, price?: number, comments?: number } export interface Checkin { checkinid?:number, deptid?:number, //#号挂在那个部门下 empid?:number, //#号挂在那个医生下 userid?:number, //#谁挂的号 money?:number, //#挂号费 checkdate?:string, checktime?:string gender?:string //新增的字段 username?:string //新增的字段 empname?:string //新增的字段 deptname?:string //新增的字段 } export interface Userinfo { userid?: number, //加?代表的意思是这个值可以有,也可以没有 如果不加?那么这个值必须存在 phone?:string, password?:string, username?:string, idcard?:string, gender?:string } export interface Dept{ deptid:number, deptname:string } export interface weekDict{ week:number, date:string, alldate:string } export interface Emp{ empid?: number, empname?: string, deptid?: number, levelid?: number, levelname?: string, money?: number } export const timebetween:Array=[ '08.00-09.00', '09.00-10.00', '10.00-11.00', '11.00-12.00', '14.00-15.00', '15.00-16.00', '16.00-17.00', '17.00-18.00' ] ``` medic.ets ```typescript import { Medic } from "../util/Entity"; import {get} from "../util/axios"; @Entry @Component export struct medic { //声明药品的数据 @State medics: Medic[] = []; aboutToAppear(): void { get('/medic/findAll',{},(content:Medic[])=>{ this.medics =content }) } build() { Column(){ //标题 Row(){ Text('便民药品').fontSize(20) .fontWeight(FontWeight.Bold) } //商品列表要有一个循环,而且能够向下拖动 List(){ //列表组件 能够向上向下拖动 //循环的事上面的那个数组 ForEach(this.medics,(medic:Medic)=>{//ForEach(Array.from({length:10}),()=>{ ListItem(){ Column(){ Image(medic.image) .width('100%') .borderRadius(8) //商品信息 Column(){ //第一行 商品名称 Row({space:10}){ Text(medic.goodname).fontSize(16).fontWeight(300) .maxLines(1) Text(`¥${medic.price}`).fontSize(10).fontColor('#ff4d4f') .fontWeight(FontWeight.Bold) } //第一行 评论数量+购物车 Row({space:10}){ Text(`${medic.comments}条评论`).fontSize(12) .fontColor('#999') Image($r('app.media.gouwu')).width(20).height(20) .fillColor('#333') } } }.width('90%').margin({top:5}) } }) }.listDirection(Axis.Vertical) .lanes(2) //2列 .width('100%') .padding(8) .layoutWeight(1) }.width('100%') .height('100%') .backgroundColor('#ffffff') } } ``` ## 大模型 LangChain 对电脑要求比较高。完整的大模型。 ollama 能够使用所有常见的大模型,都是蒸馏版。做企业内部的知识库, RagFlow等。 使用别人的在线的大模型。 能干什么? ``` 最基础的行为就是对话。 比如:行为分析,语言分析。 抓取抖音里面的所有评论(爬虫)。找出对装修有意向的客户(AI)。帮我返回一个true|false。资源给销售去联系。 比如: RagFlow 增强向量本地知识库。智能医生:本地有很大的文献资料和医学资料。这个都是保密的内部的。 让AI完全把这些都学一遍。AI能够在线问诊。律师等等。 各种MCP: Mysql-Mcp: 你过去查询数据库是要写SQL的。但是mcp后 你问AI: 自然语言--给老子找过于一年挂号量最高的医生是谁。 ai会把自然语言转成sql语言,并且执行,把结果给你 。 电脑mcp,skill,Agent: OpenClaw 给老子下载几个最新的电影,并且老电影全部删掉。 甚至整个一个项目的开发,都可以交给AI。 ``` ### Ollama ``` https://ollama.com/download ``` 蒸馏版大模型的平台。还需要大模型。安装完毕之后。就可以在控制台dos窗口进行拉取测试等。 ``` ollama list 列出已有的大模型 ``` ``` ollama pull 大模型名称 ollama pull qwen3.5:2b ``` ``` ollama run 大模型名称 运行你的大模型 ``` 这样大模型就能再dos窗口中运行了,后续就可以在你的程序中进行访问了。 ### 在线大模型 有非常多的大模型,我用的事 ``` https://cloud.siliconflow.cn/i/cQcr5n96 ``` ### 完成一个简单的医生对话 使用了部分提示词和SpringAi的公共。 ``` 令牌:sk-nemafdyayrawprqglmxqoixcuriizricpingvvrzhjtwlomt 大模型名称:Pro/zai-org/GLM-5.1 如果你使用的本地的ollama就不用令牌。 ``` #### 1:pom.xml 加入 ``` org.springframework.ai spring-ai-starter-model-openai 1.0.0 ``` #### 2:resources/application.yml ``` server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/syhis?useUnicode=true&characterEncoding=utf8 username: root password: ilovelaohan ai: openai: base-url: https://api.siliconflow.cn/ api-key: sk-nemafdyayrawprqglmxqoixcuriizricpingvvrzhjtwlomt chat: options: model: Pro/zai-org/GLM-5.1 temperature: 0.7 springdoc: api-docs: path: /v3/api-docs swagger-ui: path: /swagger-ui.html ``` temperature 0 - 1之间。 值越大,越具有创造性思维。值越小,越准确,越保守。 3:ChatClientConfig ``` package com.han.syhis; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ChatClientConfig { @Resource private OpenAiChatModel openAiChatModel; @Bean("openAiChatClient") public ChatClient openAiChatClient() { return ChatClient.builder(openAiChatModel).build(); } } ``` 4:控制器AiController ``` package com.han.syhis.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class AiController { @Resource private ChatClient openAiChatClient; @GetMapping(value="/sydx/say",produces = "text/html;charset=UTF-8") public String say(@RequestParam("what") String what){ System.out.println("准备发起ai会话"); return openAiChatClient.prompt("你是一个资深的精通各种医师的老军医") .user(what).call().content(); } @GetMapping(value = "/sydx/saystream",produces = "text/html;charset=UTF-8") public Flux saystream(@RequestParam("what") String what){ System.out.println("准备发起ai会话"); return openAiChatClient.prompt("你是一个资深的精通各种医师的老军医") .user(what).stream().content(); } } ``` ``` http://localhost:8080/sydx/say?what=肚子疼怎么办 ``` ![image-20260618095522552](images/image-20260618095522552.png) ### 鸿蒙前端 ![image-20260618101732429](images/image-20260618101732429.png) ```typescript import axios,{AxiosResponse} from "@ohos/axios"; @Entry @Component struct ai { @State result: string = '回答内容如下:' @State input:string = '' build() { Column(){ Row(){ TextInput({ placeholder: '请输入内容'}) .height(50).layoutWeight(4) .onChange((value) => { this.input = value }) Button('问').layoutWeight(1).onClick(() => { this.result = "正在思考中..." let inputok = encodeURIComponent(this.input) let url = "http://localhost:8080/sydx/say?what="+inputok axios.get,null>(url).then((response:AxiosResponse) => { let okresponse =(response.data as string).replace(/\n/g,'\n') this.result = okresponse }); }) } Scroll(){ Text(this.result).width('100%').padding(10) .textAlign(TextAlign.Start) } }.width('100%').height('100%') } } ``` 放入组件中: ![image-20260618102251615](images/image-20260618102251615.png) ai.ets ``` import axios,{AxiosResponse} from "@ohos/axios"; @Component export struct ai { @State result: string = '回答内容如下:' @State input:string = '' build() { Column(){ Row(){ TextInput({ placeholder: '请输入内容'}) .height(50).layoutWeight(4) .onChange((value) => { this.input = value }) Button('问').layoutWeight(1).onClick(() => { this.result = "正在思考中..." let inputok = encodeURIComponent(this.input) let url = "http://localhost:8080/sydx/say?what="+inputok axios.get,null>(url).then((response:AxiosResponse) => { let okresponse =(response.data as string).replace(/\n/g,'\n') this.result = okresponse }); }) } Scroll(){ Text(this.result).width('100%').padding(10) .textAlign(TextAlign.Start) } }.width('100%').height('100%') } } ``` tab.ets ```typescript import { my } from "../pages/my"; import { carts } from "../pages/carts"; import { medic } from "../pages/medic"; import { dept } from "../pages/dept"; import { ai } from "../pages/ai"; @Entry @Component struct tab { @State index:number = 0 @Builder mybuilder(index:number,title:string,img:ResourceStr){ //相当于一个自定义组件 Column(){ if(title=="AI"){ Image(img).width(40) }else{ Image(img).width(30) .fillColor(this.index==index?Color.Black:Color.Pink) Text(title) } } } build() { Tabs({barPosition:BarPosition.End}){//不写属性,默认出现在顶端 TabContent(){dept()}.tabBar(this.mybuilder(0,'挂号',$r('app.media.guahao'))) TabContent(){medic()}.tabBar(this.mybuilder(1,'药品',$r('app.media.mdeic'))) TabContent(){ai()}.tabBar(this.mybuilder(2,'AI',$r('app.media.AI'))) TabContent(){carts()}.tabBar(this.mybuilder(3,'购物车',$r('app.media.gouwu'))) TabContent(){my()}.tabBar(this.mybuilder(4,'我的',$r('app.media.wode'))) }.onSelected((index:number)=>{ console.log('你选中了',index); this.index = index; }) } } ``` 如果你要采用ollama的模型,不使用远程模型 ``` server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/syhis?useUnicode=true&characterEncoding=utf8 username: root password: ilovelaohan ai: openai: base-url: http://127.0.0.1:11434 api-key: 不写 chat: options: model: 模型名称 temperature: 0.7 springdoc: api-docs: path: /v3/api-docs swagger-ui: path: /swagger-ui.html ```