项目
- 毕业设计
简单记录制作毕业设计的过程
- 电赛
简单记录参加电赛的过程。
- 唛盟杯比赛详记
这个项目是一个知识产权流程管理系统,主要功能包括客户下单、付款、订单查询、订单进度消息推送、文档查阅下载等。还介绍了安装git和nodejs,安装前需要先安装nvm,这是一个管理node的工具,然后安装node和vue,最后安装mvn。
- 基于C++的职工管理系统
描述文章内容
简单记录制作毕业设计的过程
简单记录参加电赛的过程。
这个项目是一个知识产权流程管理系统,主要功能包括客户下单、付款、订单查询、订单进度消息推送、文档查阅下载等。还介绍了安装git和nodejs,安装前需要先安装nvm,这是一个管理node的工具,然后安装node和vue,最后安装mvn。
描述文章内容
1.进入OneNet创建产品和设备(具体步骤待补充)
2.复制产品和设备的详细信息(备份)
目的是为了方便使用AT指令以及代码编写。
设备名称(clientlD) : test
产品ID (username) : F1AVyfXw7l
password: version=2018-10-31&res=products%2FF1AVyfXw71%2Fdevices%
2Ftest&et=2810313321&method=md5&sign=EfJtPPxHwMX6iWWf3YksbA%3D%3D.urI:mqtts.heclouds.com
端口号:1883
订阅: $sys/F1AVyfXw7I/test/thing/property/post/reply发布: $sys/F1AVyfXw7l/test/thing/property/post
设备密钥: cjFZSWRwUGNrazdRR2d6eTJ4Tm9YNXN5bTNuMGpMTIE=access key: vclTl19loOn7mcfdtT39l1eRfVffM6FfH8U/U3HhUQc=
res:products/F1AVyfXw7l/devices/test
et:时间戳
代码部分唯一不同的是引用的库函数不同,
ESP8266使用的是
#include <ESP8266WiFi.h>
ESP32使用的是
#include <WiFi.h>
在使用时需要注意引用的库函数,如果库函数不匹配,可能会导致编译失败,这部分的代码以放在附件中。
**注意:**我提供的代码是以onejson格式上传的,而不是数据流的格式。在云平台创建产品时需要注意这点,当然代码里博主写的就是以数据流格式上传,如果你用原来博主写的,那么创建产品时就选数据流。
上面的代码只能实现数据上传,并不能实现命令控制,下面就介绍如何实现命令控制。
主要修改的地方是callback函数,当拿到topic的时候要解析拿到一段ID,具体可以看官方的资料,里面有提到命令下发的操作。
这个错误是协议格式不对,也就是用的topic不正确,通过下面的案例对这个作说明。
当我们在云平台下发命令的时候,在callback
函数里使用Serial.println(topic);
会打印订阅的topic,格式为:
$sys/584938/onenet_mqtt_dev1/cmd/request/c5a7e27e-05bb-41eb-972b-abce424ade8e
callback函数如下:
/********************************************************************************
函数:callback
功能:接收ONeNet传回的信息
返回值:无
参数:
topic是消息的主题,
payload是消息的有效载荷(数据部分),
length是payload的长度
*********************************************************************************/
void callback(char *topic, byte *payload, unsigned int length)
{
Serial.println("message rev:");
Serial.println(topic); // 打印订阅的topic
const char* lastSlash = strrchr(topic, '/');
if (lastSlash != NULL) {
const char* cmdId = lastSlash + 1; // 命令ID位于最后一个'/'之后
msgid = String(cmdId); //将命令ID复制给msgid,msgid定义为全局变量
}
Serial.println(msgid); // 打印拿到的ID
if (strstr(topic, ONENET_GET_CAM_REQUEST)) //ONENET_GET_CAM_REQUEST="$sys/{产品ID}/{设备名}/cmd/request/"
{
DynamicJsonDocument doc(100);
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.println("parse json failed");
return;
}
JsonObject setAlinkMsgObj = doc.as<JsonObject>();
serializeJsonPretty(setAlinkMsgObj, Serial);
String cam = setAlinkMsgObj["cam"].as<String>();
Serial.println("@"+cam); //取出下发的命令并在前面加@
char RESPONSE_TOPIC[100];
sprintf(RESPONSE_TOPIC, "$sys/{产品ID}/{设备名}/cmd/response/%s",msgid.c_str());
client.publish(RESPONSE_TOPIC, "OK"); // 向平台响应OK
}else {
Serial.println("GET_CAM faile!");
}
}
这份代码是根据一位博主的代码改的(博主文章链接),取出后面的ID用如下代码即可
const char* lastSlash = strrchr(topic, '/');
if (lastSlash != NULL) {
const char* cmdId = lastSlash + 1; // 命令ID位于最后一个'/'之后
msgid = String(cmdId); //将命令ID复制给msgid,msgid定义为全局变量
}
Serial.println(msgid); // 打印拿到的ID
而这个时候我们需要响应这个命令用的是
$sys/584938/onenet_mqtt_dev1/cmd/response/c5a7e27e-05bb-41eb-972b-abce424ade8e
一个是request,一个是response,所以在拿到下发命令的topic后要取出后面跟着的ID,然后再重新拼接在$sys/584938/onenet_mqtt_dev1/cmd/response/
后面,然后用这个拼接的topic发送响应数据给云平台。
这个时候就能在云平台那看到类似如下信息:
一般格式错误返回的错误码是15,原因就是格式有问题。使用如下的格式上传响应都可能有问题:
1.$sys/584938/onenet_mqtt_dev1/cmd/response/
2.$sys/584938/onenet_mqtt_dev1/cmd/response/+
3.$sys/584938/onenet_mqtt_dev1/cmd/response/+/+
以上内容仅供参考,如果你有更好的方法,请在评论区告诉我。
在B站上学习OKHTTP3,我把主要的核心代码放在附件里,分别是get和post的同步和异步请求,返回的结果和视频地址2里讲的一样。
在编写HTTPS网络请求(post)时需要上传一些数据,这些数据有一定的上传格式,下面的网址汇总了上传格式content参数
2024.8.19
方法一:
方法二:
使用第三方库Gson,在使用前需要创建一个Jsonbear,就是接收Jsons数据的格式。
总结:
在Android Studio中,如果你想要生成代码,例如getter和setter方法,可以使用快捷键Alt + Insert(Windows/Linux系统)或Command + N(Mac系统) 。这个快捷操作会引导你通过一个菜单来选择想要生成的代码类型,比如构造函数、toString方法、以及各种重写方法等。
此外,如果你需要查看某个方法的参数信息,可以使用快捷键Ctrl + P(Windows/Linux系统)或Command + P(Mac系统),这将展示出当前方法的参数列表及其类型。
STM32————电脑:串口1(PA 9 PA10),手机连接指定热点,连接成功后等待一段时间把摄像头的网址发给单片机,单片机通过串口2发给ESP8266.ESP8266再传到OneNet上。
注意:在用ESP8266最小系统通信的时,需要给板子单独供电,否则通信可能失败,原因我想可能是因为电压不够。
这个配网技术是让开发板设置热点,然后让手机连接,连接好后配置要让开发板连接的WIFI和密码,这个技术叫设备热点技术。相比于以往的一键配网技术,我个人觉得这个设备热点配网技术成功率要要一些。
缺点是这个技术不能用AT指令来实现,所以目前AT指令在不配合其他设备的情况下,通常都使用一键配网技术。
ESP32和ESP8266使用设备热点配网技术的区别
WiFi.h
,而ESP8266使用ESP8266WiFi.h
2.0.17
(目前这个版本是最新的),而ESP8266使用0.16或0.15
在使用ESP32时,请使用目前最新版的wifimanage库,而使用ESP8266的话请使用0.15或0.16版本的WIFIManager,如果ESP8266使用最新版的可能会报错。报什么错呢?完整报错如下:
d:\Program Files (x86)\arduino\libraries\WiFiManager/wm_consts_en.h:162:7: error: 'wifi_country_t' does not name a type
const wifi_country_t WM_COUNTRY_US{"US",1,11,WIFI_COUNTRY_POLICY_AUTO};
^
d:\Program Files (x86)\arduino\libraries\WiFiManager/wm_consts_en.h:163:7: error: 'wifi_country_t' does not name a type
const wifi_country_t WM_COUNTRY_CN{"CN",1,13,WIFI_COUNTRY_POLICY_AUTO};
^
d:\Program Files (x86)\arduino\libraries\WiFiManager/wm_consts_en.h:164:7: error: 'wifi_country_t' does not name a type
const wifi_country_t WM_COUNTRY_JP{"JP",1,14,WIFI_COUNTRY_POLICY_AUTO};
^
这个错误我目前没有解决,如果你解决了还请评论区或邮件(3256149770@qq.con)告诉我。
keil5中一点击下载就闪退或点击debug中的setting就闪退。参照文章
Keil5编程之warning: #223-D: function “xxx“ declared implicitly.参考链接
2024.8.12(更新).
使用三个ADC通道要注意按顺序去初始化,否则可能会出现问题,运营我还不知道.
安装路径:https://blog.csdn.net/u011715638/article/details/138670319
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
上面是配置文件的内容,下面说明一下配置时的一些注意事项:
example.com www.example.com
换成172.0.0.1,这样方便在本地测试;sudo systemctl reload nginx
1.安装Tomcat要使用哪个版本的java?
使用jdk8或更高版本,详细参考官方文档
2.如何在Ubuntu安装Tomcat?
2024.8.18
1.从入门到实际运用,请参考B站up主视频:点击链接
2.学完上面的视频后学习paddlepaddle的语音识别课程:
3.接着参考STM32cudeXM.AI的相关知识,把模型转换成onnx格式然后在但【单片机上运行:
3.1先用一个项目练练手:https://shequ.stmicroelectronics.cn/thread-632736-1-1.html
意外收获,这是一个学习论坛:https://www.waveshare.net/study/portal.php?mod=list&catid=38
2024.8.25
参考文献:
2024.8.26
-【1.基于声学的婴儿哭声识别细分市场模型】(Baby Cry Recognition Based on Acoustic) -【2.使用隐马尔可夫模型自动分割婴儿哭闹信号】(Automatic segmentation of infant cry signals using hidden Markov models)
所谓视频编码就是指通过特定的压缩技术,将某个视频格式文件转换成另一种视频格式文件的方式。
视频流传输中最重要的编解码标准有国际电联(TU-T,国际电信联盟)的H.261、H.263、H.264等,运动静止图像专家组(由ISO国际标准化组织与IEC国际电子委员会于1988年联合成立)的MPEG系列标准MPEG1、MPEG2、MPEG4 AVC等。
其中ITU-TH.264/MPEG-4 AVC是ITU-T与ISO/IEC连手合作制订的新标准。ITU-T方面称之为H.264。但ISOIEC的则将这个新标准归纳于MPEG系列,称为MPEG-4 AVC。
而H.265则被视为是ITU-TH.264/MPEG-4AVC标准的继任者,又称为高效率视频编码(High Efficiency VideoCoding,简称HEVC)
构建预览布局-使用SurfaceView或者TextureView
打开相机- Camera.open
设置参数- Camera.Parameters
设置预览数据回调- PreviewCallback
设置预览画布并启动- setPreviewTexture/startPreview
释放相机- stopPreview/release
YUV主要应用于优化彩色视频信号的传输,与RGB相比,YUV只需要占用极少的频宽(RGB需要三个独立的视频信号同时传输)。YUV中Y代表明亮度,也称灰阶值;U与V表示的则是色度(色调和饱和度)也可以记作:YCbCr。如果只有Y数据,那么表示的图像就是黑白的。
使用YUV的原因:
使用YUV格式才能极大地去除冗余信息,人眼对亮点信息更敏感,对色度敏感度不高。也就是说,可以压缩UV数据,而人眼难以发现。所以压缩算法的第一步,往往先把RGB数据转换成YUV数据。对Y少压缩一点,对UV多压缩一点,以平衡图像效果和压缩率。这也是为什么编码选择使用YUV而不是RGB。
YUV是一组数据的压缩格式,其下还有因为排列不同而衍生的不同格式名称
安卓P18
2024.8.27
方法一:
方法二:
1.安卓登录开发
2024.8.21
登录可以进去就强登录,或进去后在某个页面触发登录。
对于第一个只需要在第一个页面做登录处理即可,而第二种需要Hook AMS + APT
框架实现,具体看这篇博文:文章连接
可以使用目前比较流行的第三方库实现:
JustAuth开源组件
:JustAuth是一个整合了国内外数十家知名平台的OAuth登录的开源组件,它提供了丰富的OAuth平台支持,自定义State、自定义OAuth、自定义Http接口和自定义Scope等功能,极大地简化了开发者在第三方登录功能上的工作量。
2024.8.23
2.数据显示
2024.8.29
一、设备选型
1.空气质量模块设备选型:
以下笔记皆基于HAL库。
创建工程中如果勾选MCO表示向外部输出时钟,
输出的是哪一个引脚可以查看,如下图。
如果Application Structure设置为Basic,那么会把下面的两个文件分开放
否则项目会把两个文件放在一个文件夹里,如Core
1.初识时钟周期
4.外设时钟使能和失能
5.sys_stm32_cloc_init()函数
HAL_RCC_OscConfig()
6.SYSTEN文件夹
在文件startup_stm32f10x_hd.s中这里列出内部中断,外部中断就在内部中断之下,这里不再列出。
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
中断的抢占优先级由AICR寄存器的后三位控制,而响应优先级由IPRx寄存器的后四位控制,最终分配结果参考下面的表格
中断一般只设置一次,设置多次可能会导致中断紊乱,一般以最后一次的中断设置为准,详细说明参考手册4.4.5
中断执行的顺序:抢占优先级(先执行数值小的)————响应优先级(先执行数值小的)————自然优先级(先执行数值大的)
以下笔记皆基于标准库。
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
简而言之,gpio电平发生变化时,申请外部中断。
主要用于引脚复用功能的选择和重定义
在STM32中,AFIO主要完成两个任务:
这段代码的中断端口是GPIOB_Pin14,所以需要将相关的传感器接到PB14这个端口
void CountSensor_Init(){
//配置RCC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//AFIO、
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//EXTI、一直打开着不用配置打开时钟
//NVIC 一直打开着不用配置打开时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
//EXTI
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
//NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI15_10_IRQHandler(void){//中断函数
if(EXTI_GetITStatus(EXTI_Line14)== SET){
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
在编写代码前先看看定时器的实现原理图
此处主要参考通用计时器的结构图,下面是基于内部时钟RCC的定时器。
在 stm32f103xxx_rcc.c 文件对 RCC_APB1PeriphClockCmd 这样的介绍:
/**
* @brief Enables or disables the High Speed APB (APB2) peripheral clock.
* @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
* This parameter can be any combination of the following values:
* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA,
……
……
……+
*/
specifies the APB2 peripheral to gates its clock.即:指定APB 2外围设备到其时钟门。而这里的时钟门代码里选择了TIM2。
void Timer_Init(void){
//1.RCC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//2.选择时基单元时钟源,
TIM_InternalClockConfig(TIM2);
//3.时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分频,这里是1分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInitStructure.TIM_Period=10000-1; //自动重装值,计数达到这个值后触发中断,然后重新开始计数
TIM_TimeBaseInitStructure.TIM_Prescaler=7200 -1; //预分频,计数频率,
//假设计数为1s则自动重装值可设置为10000,预分频可设置为7200,公式为秒数=27MHz/PSC/ARR
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //重复计数数器高级定时器才有,这里设置为0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); //时基单元设置
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//4.中断输出控制
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //抢占优先级,0-3, 响应优先级0-3,一个工程只设置一次
//5.NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE);
}
void TIM2_IRQHandler(void){
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
OC:输出比较
IC:输入比较
CC:输入/输出比较
在信号通过比较后输出到CC1P,然后再输出到输出使用电路,输出使用电路的OC1可以查看引脚定义表
在上图中需要注意的一些引文缩写:
设置的函数为
配置PWM的时候可以参考下面的图,
图中的参考坐标系图红色线表示CCR的值,蓝色线表示CNT的值,黄色线表示自动重装载值即ARR。
了解了这点后我们再看看PWM的是如何计算的,如下公式
PWM频率: [ \text{Freq} = \frac{CKPSC}{(PSC + 1) \times (ARR + 1)} ] 可以看出PWM的频率等于CNT的更新频率
PWM占空比: [ \text{Duty} = \frac{CCR}{(ARR + 1)} ]
PWM分辨率: [ \text{Reso} = \frac{1}{(ARR + 1)} ]
知识产权流程管理系统
二、项目描述
该系统用于知识产权项目申报的全过程跟踪管理,其功能包括:
该系统分为客户端和管理端两个子系统。客户端对客户开放,主要功能包括:
管理端对管理人员开放,不对客户开放,负责订单管理、文档上传(批量上传)等。
(一)、用户端
个人中心模块:
1、订单查询
分为我的订单(综合查询)及商标、专利、版权、科技项目四大子类查询,订单查询按全部业务下单时间顺列。
2、订单详细
可根据订单详情按钮跳转到对应的订单详情。
订单详情页展示图:
(二)、管理端
订单管理模块:
1、订单查询
查询订单,展示列表。
2、订单详细
在订单列表中点击某个订单,进入订单详情。
3、文档上传
在订单详情页,点击文档上传,选中本地文件,上传本地文件到服务器,支持批量、单文件上传。
一、扫描一级部门的二维码
这一部分只建议参加了比赛的网友查看。本次是组队参加,一队3-5人,队长要在他们的官方网页端注册号团队(二级部门),方便后面的项目管理,然后再通过一级部门添加自己的团队,首先是扫描官方给的二维码,扫这个二维码后注册就会在一级部门下,这也是要注册在一级部门之下的办法,
二、授权登录成功后,跳转到提示页面,点击“首页”
三、进入低代码平台首页,按照提示,点击“账户明细”
四、按照提示,输入姓名,并提交
五、点击唛盟低代码平台官网,选择微信扫码登录
唛盟低代码平台官网:https://maimengcloud.com/lcode/m1/#/login
六、看到用户名称是自己的即报名成功
1.构建每个参赛团队的独立部门
2.将参赛队员拉入部门
3.给每个参赛队员设置好岗位
3.1 在一级部门,即总部下建立子部门
进入【部门管理】,点击【添加下级】
3.2输入部门信息
其它字段不填
(如果你不是第一届比赛的同学,那这个不需要关注,可能每年都不一样)
3.3保存提交
3.4把岗位挂接到部门上
【部门管理】中找到刚才建立的部门,点击【岗位】按钮弹出以下岗位选择框:
选择左边需要绑定的岗位,移动到右边,点击【提交】按钮即完成岗位绑定(如果没有显示岗位,多次刷新,或者退出重新登录,这个知识第一届遇到此问题解决的方案)
3.5把用户拉入部门
打开【用户管理】菜单,先把自己的搜索出来,如果不是通过一级部门的二维码注册的,会搜不到的哦。找到需要拉入的队友,点击【部门/岗位】
弹出以下部门岗位选择界面:
在弹出的界面中左边选中归属的部门,右边选中岗位,再【提交】即可
这部分本来也不想记了,毕竟没什么用了,可能下一届又不一样了,但想想自己记一下当作回忆也可以。
在安装前我们通过这个链接https://gitee.com/maimengcloud/mdp-lcode-ui-web
去看看这个项目的介绍,
大概浏览一下,然后往下拉根据导航直接跳到 快速开始
这里并不是说其他的不重要,只是说对于一个项目而言如果不是涉及本地启动的话不必花费时间去纠结,等你能让项目在本地启动后再回来看看也不迟,当然这也有弊端,可能会错一些细节哈。
在快速开始的地方提到:
注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+ nodejs版本v14.16.1
所以我们安装的时候要注意版本的问题尽量不要选太高的,如果你已经安装了一部分或全部,而且版本很高或很低,这样也不必太担心(大不了删掉重新安装,哈哈),下面是我的一些经验,希望帮到你。
这部分没有太多可讲的,网上有很多的教程,讲的也很好,这里需要你记住的一条命令是:
git clone <远程仓库地址> <本地存放目录>
这里举个例子,就比如这次比赛用到的这个项目,我们建一个文件夹装要下载的项目,比如是mdp文件夹,然后右键,win11选显示更多,在弹出的选项选择: git bash here
然后复制下面的内容回车就可以了。
git clone https://gitee.com/maimengcloud/mdp-lcode-ui-web.git mdp-lcode-ui-web/
但是有的时候也会因为网速的问题,导致无法下载,比如回车后报错,如下:
Cloning into ‘mdp-lcode-ui-web’…
fatal: unable to access ‘https://gitee.com/maimengcloud/mdp-lcode-ui-web.git/': Failed to connect to 127.0.0.1 port 26501 after 2017 ms: Couldn’t connect to server
简单翻译理解就是这个远程仓库无法访问,其实这也是使用让我比较烦恼的问题,这里的原因我可能没办法给你讲清楚(你有时间可以去琢磨哈),但有个软件可以分享给你,帮你解决这个问题,即 watt toolkit ,而且这个软件在微软软件商城就有。
下载后选github,然后点击一键加速。
然我们再回到git base here,再次输入刚才的命令回车,
发现刚才的代码仓库已经下载到本地了。
如果你使用上面的软件还是无法下载远程的仓库,那你需要根据报错信息在网上查考相关的信息,这里提供上面报错的可能解决方案,只是针对上面的问题哦!参照文章
网上可能称为nodejs都是一个工具,这个工具的简介还请参照官方,这里我们有做过多的了解哦。在安装node之前我们要先安装nvm。这是一个管理node的工具,这里安装的主要目的是 升高或降低node版本,简单就是想安装哪个版本(已发布且可下载的版本)的可以用这个工具来实现。
nvm是否要安装取决于你的情况,除了nvm,此次项目还需要安装工具node,vue,mvn。
方案一:为防止版本过高过低影响运行,以后可能还需要,最好是下载nvm管理node,然后也要了解升级或降低vue版本的方法,最后下载mvn;
方案二:自己不管安装还是没安装,直接安装node,vue,mvn,本次项目用到的node,vue,mvn都在package.json文件里定义写好了,运行后会根据里面去下载,似乎对项目没太大影响(因为我工具处于最高版本,或是相对低一点的版本,使用相同的命令,报错都是一个,基于这点你可以尝试。)
参照文档 关于文章的几点说明:
里面的 三、配置路径和下载源我们需要把淘宝镜像改为如下:
node_mirror: https://cdn.npmmirror.com/binaries/node/
npm_mirror: https://cdn.npmmirror.com/binaries/npm/
这是因为淘宝的镜像换了。
接着参照文章,目的是node还要做一些配置,主要node的环境变量设置,还有目录node_cache
和目录node_global
的一些设置。从文章的下图开始看,关于node的下载就不看了。
注意在设置目录node_cache
和目录node_global
时,
npm config set prefix “<自己的文件目录>\node_global” npm config set cache “<自己的文件目录>\node_cache”
这两个路径时要记得把它改成自己安装的路径:
首先你要先安装好node,然后用node的npm工具安装vue。
使用命令查看可安装版本:
要查看的vue版本是1.x和2.x的
npm view vue-cli versions --json
要查看的vue版本是3.x的
npm view @vue/cli versions --json
安装vue指定的版本:
安装的vue版本是1.x和2.x的
npm install -g @vue/cli@版本号
yarn global add @vue/cli@版本号
要查看的vue版本是3.x的
npm install -g vue-cli@版本号
yarn global add vue-cli@版本号
第一次安装要去配环境变量。
卸载已经安装的Vue Cli
卸载 3.x 或 4.x 版本的Vue Cli
npm uninstall -g @vue/cli
yarn global remove @vue/cli
卸载 1.x 或 2.x 版本的Vue Cli
npm uninstall vue-cli -g
yarn global remove vue-cli
重复情况一的步骤,然后打开cmd,输入:
vue -V
显示安装的版本则说明安装完成。
直接参照这个文章就好了。
参照文章,同样的里面涉及到镜像文件的设置要注意换掉:
node_mirror: https://cdn.npmmirror.com/binaries/node/
npm_mirror: https://cdn.npmmirror.com/binaries/npm/
除此之外在设置目录node_cache
和目录node_global
这两个路径时要记得把它改成自己安装的路径:
npm config set prefix “<自己的文件目录>\node_global” npm config set cache “<自己的文件目录>\node_cache”
直接参照这个文章就好了。
至此工具就就安装完了,接着我们就要去启动项目了。
本次使用的系统是win11和code应用程序,如果你用的不是这个系统和应用程序情况可能不一样哈。
这里我使用的是code应用程序(你如果用的是别的应用程序另说咯),点击文件夹用code打开后,根据项目文档执行命令:
npm install
结果…………………………报错啦(我就知道没这么简单)!!!错误如下:
上面的报错意思是:在尝试安装项目依赖时,npm遇到了依赖冲突问题。具体来说,babel-loader需要webpack的版本在2、3或4之间,但是你的项目中已经安装了webpack的5.90.1版本。这导致了peer依赖冲突,因为babel-loader期望的webpack版本与实际安装的版本不匹配,
解决办法如下:
1.查看自己的node版本;
```
node -v
```
2.修改"package.json",在"devDependencies"中增加"node": “^21.6.1"依赖;
```
"node":"^21.6.1",
```
3.npm install -s node-sass@4.14.1;
4.npm install –save –legacy-peer-deps;
结果……………………报错啦!!!!!
修改"package.json",原来的改为"node": "^13.14.0"依赖;
再次运行结果没报错。
5.npm install –registry=https://cdn.npmmirror.com
接着我们运行如下代码 `npm install --registry=https://cdn.npmmirror.com`
6.npm run dev
运行npm run dev,结果还是报错了……
这里的错误说vue-template-compiler的版本为2.7.16,那我们就去"package.json"找到vue,
原来的vue版本:
把上面的修改为`"vue":"^2.7.16",`
这时候要重新运行 `npm install --save --legacy-peer-deps`,然后再次运行 `npm run dev`;
结果还是报错,如下:
大概意思是没有找到ajv这个包,那么我们在"package.json"里的 **"dependencies"** 添加`"ajv": "^8.12.0",`
然后重复`npm install --save --legacy-peer-deps`, `npm run dev`操作就可以了。
显示如下:
我们再看看页面,页面如下:
到这里说明没问题了。
从上面我们可以了解到一点,自己安装的版本似乎并不会影响项目的运行,所以选择方案二来进行安装似乎更简单,但从往长远的看方案一更更好,他可以随时换版本,更为方便。
……
添加头文件:
#pragma once //防止头文件重复包含
#include <iostream>
using namespace std;
class WorkerManager
{
public:
WorkerManager();
~WorkerManager();
};
添加源文件:
#include "WorkerManager.h"
WorkerManager::WorkerManager()
{
}
WorkerManager::~WorkerManager()
{
}
class WorkerManager
{
public:
WorkerManager();
void Show_Menu();
~WorkerManager();
};
void WorkerManager::Show_Menu()
{
cout << "*************************" << endl;
cout << "***欢迎使用职工管理系统******" << endl;
cout << "***** 0-退出管理程序 *****" << endl;
cout << "***** 1-增加职工信息 *****" << endl;
cout << "***** 2-显示职工信息 *****" << endl;
cout << "***** 3-删除职工信息 *****" << endl;
cout << "***** 4-修改职工信息 *****" << endl;
cout << "***** 5-查找职工信息 *****" << endl;
cout << "***** 6-按照编号排序 *****" << endl;
cout << "***** 7-清空所有文档 *****" << endl;
cout << "*************************" << endl;
}
#include <iostream>
using namespace std;
#include "workerManager.h"
int main()
{
WorkerManager wm;
wm.Show_Menu();
system("pause");
return 0;
}
class WorkerManager
{
public:
WorkerManager();
void Show_Menu();
void ExitSystem();
~WorkerManager();
};
void WorkerManager::ExitSystem()
{
cout << "欢迎下次使用" << endl;
system("pause");
exit(0);
}
int main()
{
WorkerManager wm;
int choice = 0;
while (true)
{
wm.Show_Menu(); //显示菜单
cout << "请输入您的选择: " << endl;
cin >> choice;
switch (choice)
{
case 0: //退出系统
wm.ExitSystem();
break;
case 1: //增加职工
break;
case 2: //显示职工
break;
case 3: //删除职工
break;
case 4: //修改职工
break;
case 5: //查找职工
break;
case 6: //按照编号排序
break;
case 7: //清空文件
break;
default:
cout << "输入有误,请重新输入" << endl;
system("pause");
system("cls"); //清屏操作
break;
}
}
system("pause");
return 0;
}
将三个职工抽象为一个类,利用多态管理不同职工; 职工属性行为: 职工行为:
#pragma once
#include <iostream>
using namespace std;
#include <string>
//职工抽象类
class Worker
{
public:
//显示个人性息
virtual void showInfo() = 0;
//获取岗位个人性息
virtual string GetDeptName() = 0;
//职工编号
int m_Id;
//职工姓名
string m_Name;
//部门编号
int m_DeptId;
};
#pragma once
#include <iostream>
using namespace std;
#include <string>
#include "worker.h"
//职工类
class Employee:public Worker
{。
public:
Employee(int id,string name,int did);
//显示个人性息
void showInfo();
//获取岗位个人性息
string GetDeptName();
};
#include "employee.h"//不同岗位此处不同
Employee::Employee(int id, string name, int did)
{
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = did;
}
//显示个人性息
void Employee::showInfo()
{
cout << "职工编号:" << this->m_Id
<< "\t职工姓名:" << this->m_Name
<< "\t岗位:" << this->GetDeptName()
<< "\t岗位职责:完成经理交给的任务" << endl;
}
//获取岗位个人性息
string Employee::GetDeptName()
{
return string("员工");//不同岗位此处不同
}
Worker* worker = new Employee(20, "张三", 3);
worker->showInfo();
delete worker;
worker = new Manager(10, "李四", 2);
worker->showInfo();
delete worker;
worker = new Boss(1, "王五", 1);
worker->showInfo();
delete worker;
class WorkerManager
{
public:
//构造函数
WorkerManager();
//展示菜单
void Show_Menu();
//退出系统
void ExitSystem();
//添加职工函数
void Add_Emp();
//记录数组中有多少个职工人数
int m_EmpNum;
//职工数组指针,使用双重指针管理worker数组指针,数组类型是*Worker
Worker** m_EmpArray;
~WorkerManager();
};
void WorkerManager::Add_Emp()
{
//提示
cout << "请输入要添加的人数" << endl;
//保存输入的人数
int addNum = 0;
cin >> addNum;
if (addNum > 0)
{
//添加
//计算添加空间大小
int newSize = this->m_EmpNum + addNum;
//开辟空间,使用二级指针指向一个指针数组,这个指针数组会因为多态而指向不同职工
Worker **newSpace = new Worker *[newSize];
//如果原来有数据要复制到新空间下
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
newSpace[i] = this->m_EmpArray[i];
}
}
//添加新成员
for (int i = 0; i < addNum; i++)
{
int id;
string name;
int dSelect;
cout << "输入第"<<i+1<<"个职工的编号" << endl;
cin >> id;
cout << "输入第" << i + 1 << "个职工的姓名" << endl;
cin >> name;
cout << "选择岗位职称" << endl;
cout << "1:表示普通员工" << endl;
cout << "2:表示经理" << endl;
cout << "3:表示老板" << endl;
cin >> dSelect;
Worker* worker = NULL;
switch (dSelect)
{
case 1:
worker = new Employee(id, name, 1);
break;
case 2:
worker = new Manager(id, name, 2);
break;
case 3:
worker = new Boss(id, name, 3);
break;
default:
break;
}
//将职工信息添加到数组中
newSpace[this->m_EmpNum + i] = worker;
}
//释放堆区数据
delete[] m_EmpArray;
//更新双重指针的指向
this->m_EmpArray = newSpace;
//更新人数
this->m_EmpNum = newSize;
//文件不为空
this——>m_FileIsEmpty = false;
//成功添加后保存到文件中
Save();
//提示添加成功
cout << "添加成功,添加了"<<addNum<<"人" << endl;
system("pause");
system("cls");
}
else
{
cout << "输入错误" << endl;
}
}
int main()
{
WorkerManager wm;
int choice = 0;//用于存储用户选择
while (true)
{
wm.Show_Menu(); //显示菜单
cout << "请输入您的选择: " << endl;
cin >> choice;
switch (choice)
{
case 0: //退出系统
wm.ExitSystem();
break;
case 1: //增加职工
wm.Add_Emp();
break;
case 2: //显示职工
break;
case 3: //删除职工
break;
case 4: //修改职工
break;
case 5: //查找职工
break;
case 6: //按照编号排序
break;
case 7: //清空文件
break;
default:
cout << "输入有误,请重新输入" << endl;
system("pause");
system("cls"); //清屏操作
break;
}
}
system("pause");
return 0;
}
因程序运行结束后添加的数据会被清除,再次打开创建的数据就没有了,所以需要将数据保存到文件中,下次打开时可以读取文件中的数据。
在WorkerManager.h中添加保存数据函数Save();并在WorkerManager.cpp中实现,实现代码如下:
void WorkerManager::Save()
{
//创建写入文件对象
ofstream ofs;
//打开文件
ofs.open(FILENAME, ios::out);
//开始写入
for (int i = 0; i < this->m_EmpNum; i++)
{
ofs << this->m_EmpArray[i]->m_Id << " "
<< this->m_EmpArray[i]->m_Name << " "
<< this->m_EmpArray[i]->m_DeptId << endl;
}
//关闭文件
ofs.close();
}
接着在WorkerManager.cpp中Add_Emp()函数中添加保存数据函数,详细查看代码。
在读取文件的过程中,文件可能有一下三种情况:
在worrkerManager.h中添加一个bool类型的m_FileIsEmpty成员变量用于记录文件是否为空。
在WorkerManager.cpp中构造函数中添加代码,当文件为空时如何操作:
WorkerManager::WorkerManager()
{
//1,判断文件是否存在
ifstream ifs;
ifs.open(FILENAME, ios::in);
if (!ifs.is_open())
{
cout << "文件不存在" << endl;
//初始化属性
this->m_EmpArray = NULL;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
//文件存在,但是没有记录
char ch;
ifs >> ch;
if (ifs.eof())
{
//文件为空
cout << "文件为空" << endl;
//初始化属性
this->m_EmpArray = NULL;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
}
因为每次添加人数后文件就不是空的,所以需要在函数Add_Emp()中添加代码,当文件不为空时,将m_FileIsEmpty = false,详细查看代码。
在为文件中WorkerManager.h类添加如下成员函数:
//统计文件中的人数
int Get_EmpNUm();
//如果文件存在且有记录,读出数据并存储在数组中,初始化员工人数
void initEmp();
在WorkerManager.cpp中实现Get_EmpNUm()、initEmp()函数,实现代码如下:
int WorkerManager::Get_EmpNUm()//用于记录人数个数
{
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int did;
int empNum = 0;
while (ifs >> id && ifs >> name && ifs >> did)
{
empNum++;
}
return empNum;
}
void WorkerManager::initEmp()
{
//打开文件
ifstream ifs;
ifs.open(FILENAME, ios::in);
int id;
string name;
int dId;
int index = 0;
while (ifs >> id && ifs >> name && ifs >> dId)
{
Worker* worker = NULL;
if (dId == 1)
{
worker = new Employee(id, name, dId);
}
else if (dId == 2)
{
worker = new Manager(id, name, dId);
}
else
{
worker = new Boss(id, name, dId);
}
this->m_EmpArray[index] = worker;//将数据存储在指针数组中
index++;//记录人数
}
//关闭文件
ifs.clear();
}
此时回到构造函数中添加如下代码
//文件存在,并有记录
int num = this->Get_EmpNUm();
cout << "职工人数为:" << num<<endl;
this->m_EmpNum = num;
//开辟空间
this->m_EmpArray = new Worker * [this->m_EmpNum];
//将数据存到数组中
this->initEmp();
//测试代码,可以删除
for (int i = 0; i < this->m_EmpNum; i++)
{
cout << "职工编号" << this->m_EmpArray[i]->m_Id << " "
<< "职工姓名:" << this->m_EmpArray[i]->m_Name << " "
<< "职工职称:" << this->m_EmpArray[i]->GetDeptName() << endl;
}
在WorkerManager.h中添加如下成员函数:
//显示职工
void Show_Emp();
在WorkerManager.cpp中实现Show_Emp()函数,实现代码如下:
//判断文件是否为空
if (this->m_FileIsEmpty)
{
cout << "文件不存在或为空" << endl;
}
else
{
for (int i = 0; i < this->m_EmpNum; i++)
{
this->m_EmpArray[i]->showInfo();
}
system("pause");
system("cls");
}
在main()函数中添加如下代码:
case 2: //显示职工
wm.Show_Emp();
break;
在WorkerManager.h中添加如下成员函数:
// 删除职工
void Del_Emp();
//判断职工是否存在
int IsExist(int id);
在WorkerManager.cpp中实现Del_Emp()函数,实现代码如下:
// 删除职工
void WorkerManager::Del_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在" << endl;
}
else
{
cout << "请输入要删除的职工编号" << endl;
int id = 0;
cin >> id;
int index = this->IsExist(id);
if (index != -1 ) {
for (int i = index; i < this->m_EmpNum - 1; i++)
{
//数据前移
this->m_EmpArray[i] = this->m_EmpArray[i + 1];
}
//更新人数
this->m_EmpNum--;
//同步到文件中
this->Save();
cout << "删除成功" << endl;
system("pause");
system("cls");
}
else
{
cout << "职工编号不存在" << endl;
system("pause");
system("cls");
}
}
}
//判断职工是否存在
int WorkerManager::IsExist(int id)
{
int index = -1;
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Id == id)
{
//按照编号,找到职工
index = i;
break;
}
}
return index;
}
在WorkerManager.h中添加如下成员函数:
//修改职工
void Mod_Emp();
在WorkerManager.cpp中实现Mod_Emp()函数,实现代码如下:
//修改职工
void WorkerManager::Mod_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在" << endl;
}
else
{
cout << "请输入要修改的员工编号" << endl;
int oldId = 0;
cin >> oldId;
int ret = this->IsExist(oldId);
if (ret != -1)
{
//先清空原有数据
delete this->m_EmpArray[ret];
//提示输入,修改职工信息
int id;
string name;
int dSelect;
cout << "查到:" << oldId << "号职工,请输入职工新编号" << endl;
cin >> id;
cout << "输入职工的姓名" << endl;
cin >> name;
cout << "选择岗位职称" << endl;
cout << "1:表示普通员工" << endl;
cout << "2:表示经理" << endl;
cout << "3:表示老板" << endl;
cin >> dSelect;
Worker* worker = NULL;
switch (dSelect)
{
case 1:
worker = new Employee(id, name, 1);
break;
case 2:
worker = new Manager(id, name, 2);
break;
case 3:
worker = new Boss(id, name, 3);
break;
default:
break;
}
//将职工信息添加到数组中
this->m_EmpArray[ret] = worker;
cout << "修改成功" << endl;
//同步到文件
this->Save();
}
else
{
cout << "修改失败,员工编号不存在" << endl;
}
}
system("pause");
system("cls");
}
详细请查看代码:
int WorkerManager::IsNameExist(string name) {
bool flag = -1;
for (int i = 0; i < this->m_EmpNum; i++)
{
if (this->m_EmpArray[i]->m_Name == name)
{
flag = i;
break;
}
}
return flag;
}
void WorkerManager::Find_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在" << endl;
}
else
{
cout << "请输入查找方式" << endl;
cout << "1:按照编号查询" << endl;
cout << "2:按照姓名查询" << endl;
int select = 0;
cin >> select;
switch (select)
{
case 1:
{
//按照编号查询
int id;
cout << "请输入查找的编号:" << endl;
cin >> id;
int index = 0;
index = this->IsExist(id);
if(index != -1)
{
cout << "查询到编号为"<<id<<"的职工,信息如下:" << endl;
this->m_EmpArray[index]->showInfo();
}
else
{
cout << "查无此人!" << endl;
}
}
break;
case 2:
{
//按照姓名查找
string name;
cout << "请输出要查找的职工姓名:" << endl;
cin >> name;
int index = this->IsNameExist(name);
if (index != -1)
{
cout << "查找成功," << name << "职工的信息如下:" << endl;
this->m_EmpArray[index]->showInfo();
}
else
{
cout << "查无此人" << endl;
}
}
break;
default:
cout << "输入错误请重新输入!!" << endl;
break;
}
}
this->Cls_coutContents();
}
详细查看代码:
void WorkerManager::Sort_Emp()
{
if (this->m_FileIsEmpty)
{
cout << "文件不存在或为空" << endl;
this->Cls_coutContents();
}
else
{
//文件存在输入编号,设置升序,降序
cout << "输入数字设置排序" << endl;
cout << "1:升序" << endl;
cout << "2:降序" << endl;
int select = 0;
cin >> select;
for (int i = 0; i < this->m_EmpNum; i++)
{
//假设i下标对象中的编号就是最大的
int minOrMan = i;
for (int j = 0; j < this->m_EmpNum; j++)
{
if (select == 1)//升序
{
if (this->m_EmpArray[minOrMan]->m_Id > this->m_EmpArray[j]->m_Id)//如果是大于,把下标赋值给minOrman
{
minOrMan = j;
}
}
else
{
if (this->m_EmpArray[minOrMan]->m_Id < this->m_EmpArray[j]->m_Id)//如果是小于,把下标赋值给minOrman
{
minOrMan = j;
}
}
}
//以升序为例,当假设的值最大值minOrMan与j中对象比较完后,
// 就能得到第一轮真正最大的下标,这个是时候交换数据即可
if (minOrMan != i)
{
Worker* temp = this->m_EmpArray[i];
this->m_EmpArray[i] = this->m_EmpArray[minOrMan];
this->m_EmpArray[minOrMan] = temp;
}
}
cout << "排序成功" << endl;
//同步数据到文件
this->Save();
this->Show_Emp();
}
}
详细查看代码:
void WorkerManager::Clean_File()
{
cout << "确定清空文件" << endl;
cout << "1:确定" << endl;
cout << "2:取消" << endl;
int select = 0;
cin >> select;
if(select == 1)
{
//清空文件
ofstream ofs(FILENAME,ios::trunc);
ofs.close();
//判断存储指针对象的数组是否为空,为空则需要一一清楚堆区的每个职工
if (this->m_EmpArray != NULL)
{
for (int i = 0; i < this->m_EmpNum; i++)
{
delete this->m_EmpArray[i];
this->m_EmpArray[i] = NULL;//避免野指针
}
//释放指针数组
delete[] this->m_EmpArray;
this->m_EmpArray = NULL;
this->m_EmpNum = 0;
this->m_FileIsEmpty = true;
cout << "文件清除成功。" << endl;
}
}
this->Cls_coutContents();
}
void WorkerManager::Cls_coutContents()
{
system("pause");
system("cls");
}
以上就是一个基于C++的职工管理系统,希望对大家有所帮助。