当前位置:   article > 正文

基于LVGL的界面管理机制_lvgl 页面管理

lvgl 页面管理

一、LVGL介绍:
lvgl是一款轻量级的嵌入式开源界面代码,广泛用于图形化的界面操作,控件丰富。

二、资源介绍:
1.lvgl资源连接,从GitHub上可以直接搜索lvgl,可以搜索到各个版本的资源连接,连接如下:https://github.com/lvgl/lvgl
我使用的不是最新的版本,7.11.zip是我挑选的历史版本
在这里插入图片描述lvgl源码工程如图所示:源码压缩包

在这里插入图片描述在分支里选择你所需要的工程包
在这里插入图片描述
找到文件夹里的HTML,会有lvgl的所有操作
在这里插入图片描述
包括移植、控件的使用等等一系列操作。关于移植后续补充,其实没啥难度,很多地方都有介绍

2、lvgl字体转换
链接如下:https://lvgl.io/tools/fontconverter
在这里插入图片描述
你本地的字库里仿宋,微软雅黑等 ttf后缀的文件
在这里插入图片描述

在这里插入图片描述
至此就完成了汉字的生成,后续使用就可以直接用了

3、lvgl图片转换
链接如下:https://lvgl.io/tools/imageconverter
在这里插入图片描述
至此图片也可以生成了,用的时候declare一下图片即可

三、界面管理机制介绍:
基于lvgl的实现,为了方便页面的管理和实现,使用了面向对象的方式进行界面的管理和实现,方便后续作为模板化生成界面,新增界面和维护界面非常方便
1、相关技术介绍
1.1、堆栈
我们界面总共有100个左右的界面,所以界面句柄的管理使用了堆栈进行页面句柄的入栈出栈操作,源码如下

/*定义数据结构*/
#define pt_stack(T,size)\
	struct{\
		T buf[size];\
		int top;\
	}
/*初始化堆栈*/	
#define pt_stack_init()\
{.top = -1}	

#define pt_stack_len(s) (sizeof((s)->buf)/sizeof((s)->buf[0]))//<计算堆栈长度
#define pt_stack_cap(s) ((s)->top + 1)//<计算堆栈容量
#define pt_stack_empty(s) ((s)->top == -1)//<判断堆栈是否为空
#define pt_stack_reset(s) ((s)->top == -1)//<复位
#define pt_stack_full(s) ((s)->top == pt_stack_len(s) - 1)//<判断队列是否满

#define pt_stack_push(s,el)\
	(!pt_stack_empty(s) && ((s)->buf[++(s)->top] = (el),1))//<入栈
#define pt_stack_pop(s)\
	(pt_stack_empty(s) ? NULL: (s)->buf[(s)->top--])//<出栈	
#define pt_stack_peek(s)\
	(pt_stack_empty(s) ? NULL : (s)->buf[(s)->top]))//<取出栈顶元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

页面数据结构定义page.h

struct _PAGE;
struct _lv_obj_t;
struct _lv_group_t;
/**
 *@brief:页面类型
 */
typedef enum{
	GUI_NONE_PAGE_TYPE = 0,//<页面无类型
	/*常驻界面-不需要删除的*/
	LOCK_SCREEN_PAGE_TYPE,//<锁屏页
	MAIN_PAGE_TYPE,//<主运行页
	MENU_PAGE_TYPE,//<菜单页
	/*动态页面,退出后注销的,不常用的页面资源*/
}PAGE_TYPE

/**
 *@brief:页面存储类型
 */
typedef enum{
	PERMANENT_PAGE_TYPE,	//<常驻页面,静态创建,不删除
	DYNAMIC_PAGE_TYPE		//<动态页面,动态创建以及删除,有页面栈进行维护
}

/**
 *@brief:页面公共回调函数
 */
typedef struct{
	int (*tp_gui_create)(struct _PAGE *self);				//<创建页面
	int (*tp_gui_show)(struct _PAGE *self);					//<显示页面
	int (*tp_gui_hidden)(struct _PAGE *self);				//<隐藏页面
	int (*tp_gui_create_timer_task)(struct _PAGE *self);	//<创建页面后初始化
	int (*tp_gui_delete_timer_task)(struct _PAGE *self);	//<删除页面前清理
	int (*tp_gui_cleanup)(struct _PAGE *self);				//<清理页面
	int (*tp_gui_delete)(struct _PAGE *self);				//<删除页面
	int (*tp_gui_dispatch_logic_key)(struct _PAGE *self,uint8_t logic_key,uint8_t *lvgl_key);//<按键转换
}PAGE_VTABLE;

/**
 *@brief:定义页面数据结构
 */
typedef struct _PAGE{
	PAGE_VTABLE v_table;			//<页面公共回调函数
	PAGE_TYPE type;					//<页面类型
	PAGE_MEMORY_TYPE mem_type;		//<页面存储类型
	uint8_t register_flag;			//<注册标记
	uint8_t init_flag;				//<初始化标记
	uint8_t minimal_permission;		//<页面最低需求权限
	uint8_t page_building;			//<1:功能开发中
	/*lvgl核心控件*/
	struct _lv_obj_t *root;			//<根容器
	struct _lv_group_t *active_group//<活跃焦点组
	/*页脚和页眉*/
	PAGE_HEADER *active_header;
	PAGE_FOOTER *active_footer;	
}PAGE; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

页面初始化相关函数page.c

/**
 *@brief:页面初始化
 */
int page_init(PAGE *self)
{
	if(self == NULL)
		return -1;
	memset(self,0,sizeof(PAGE));
	self->v_table.tp_gui_show = __default_gui_show;
	self->v_table.tp_gui_hidden = __default_gui_hidden;
	self->v_table.tp_gui_delete = __default_gui_delete;
}


int default_gui_show(PAGE *page)
{
	__default_gui_show(page);
}
/**
 *@brief:默认显示界面
 */
static int __default_gui_show(PAGE *self)
{
	if(self == NULL)
		return -1;
	/*1.统一显示和刷新头部组件*/
	if(self->active_header && self->active_header->v_table.tp_show){
		self->active_header->v_table.tp_show(self->active_header);
	}
	if(self->active_header && self->active_header->v_table.tp_flush){
		self->active_header->v_table.tp_flush(self->active_header,NULL);
	}
	/*2.统一显示和刷新底部组件*/
	if(self->active_footer && self->active_footer->v_table.tp_show){
		self->active_footer->v_table.tp_show(self->active_footer);
	}
	if(self->active_footer && self->active_footer->v_table.tp_flush){
		self->active_footer->v_table.tp_flush(self->active_footer,NULL);
	}
	/*3.统一创建定时任务*/
	if(self->v_table.tp_gui_create_timer_task && self->init_flag){
		self->v_table.tp_gui_create_timer_task(self);
	}
	/*4.创建焦点组*/
	if(self->init_flag && self->root){
		lv_obj_set_hidden(self->root,false);
		lv_scr_load(self->root);
		if(self->active_group){
			lvgl_task_register_group(self->active_group);
		}
	}
	return 0;
}
/**
 *@brief:默认界面隐藏
 */
static int __default_gui_hidden(PAGE *self)
{
	int ret = -1;
	if(self == NULL)
		return -2;
	/*1.统一控制header组件*/
	if(self->active_header && self->active_header->v_table.tp_hidden){
		self->active_header->v_table.tp_hidden(self->active_header);
	}
	/*2.统一控制footer组件*/
	if(self->active_footer && self->active_footer->v_table.tp_hidden){
		self->active_footer->v_table.tp_hidden(self->active_footer);
	}	
	/*3.统一清除定时任务*/
	if(self->v_table.tp_gui_delete_timer_task){
		self->v_table.tp_gui_delete_timer_task(self);
	}
	/*4.清除焦点组*/
	lvgl_task_clear_group();
	
	if(self->root){
		lv_obj_set_hidden(self->root,true);
		ret = 0;
	}

}

/**
 *@brief:默认界面删除
 */
static int __default_gui_delete(PAGE *self)
{
	int ret = -1;
	if(self == NULL)
		return -2;
	/*1.统一控制header组件*/
	if(self->active_header && self->active_header->v_table.tp_delete_timer_task){
		self->active_header->v_table.tp_delete_timer_task(self->active_header);
	}
	/*2.统一控制footer组件*/
	if(self->active_footer && self->active_footer->v_table.tp_delete_timer_task){
		self->active_footer->v_table.tp_delete_timer_task(self->active_footer);
	}	
	/*3.统一清除定时任务*/
	if(self->v_table.tp_gui_delete_timer_task){
		self->v_table.tp_gui_delete_timer_task(self);
	}
	/*4.清除焦点组*/
	lvgl_task_clear_group();
	
	/*5.删除界面*/
	if(self->root){
		lv_obj_del_async(self->root);
		self->root = NULL;
	}
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

页面栈结构定义page_mgr.c

typedef pt_stack(PAGE*,PAGE_MGR_STACK_SZIE) LV_STACK; //<定义页面栈结构
static LV_STACK page_stack = pt_stack_init();			//<页面栈-实体
static uint8_t __page_mgr_init_flag = 0;			//<页面管理器-初始化标志
static int __page_size = 0;							//<页面数量,总共页面的数量
static PAGE *__pages[PAGE_MGR_STACK_SIZE];			//<页面缓冲区,二级指针用于存放页面栈数据
static int __page_register(void);					//<页面注册管理

/**
 *@brief:页面注册管理初始化
 */
int page_mgr_init(void)
{
	if(__page_mgr_init_flag == 0){
		__page_size  = 0;										//< 页面管理数量0
		memset(__pages,0,PAGE_MGR_STACK_SIZE * sizeof(PAGE));	//< 初始化为所有页面栈为0
		__page_mgr_init_flag = 1;
		__page_register();//<页面注册管理
	}
	return 0;
}

/**
 *@brief:通过页面类型查找页面
 *@param:PAGE_TYPE 页面类型
 *@ret:PAGE:当前活跃页
 */
PAGE *page_mgr_lookup_by_type(PAGE_TYPE type)
{
	PAGE *page = NULL;
	for(int i = 0; i < PAGE_MGR_STACK_SIZE; i++){
		page = __pages[i];
		if((page->register_flag) == 1 && (page->type) == type){
			return page;
		}
	}
	return NULL;
}
/**
 *@brief:查找栈顶页面
 *@ret:PAGE 栈顶页面
 */
PAGE *page_mgr_lookup_active_page(void)
{
	return page_mgr_lookup_check_active_page(GUI_NONE_PAGE_TYPE);
}

/**
 *@brief:根据类型查找栈顶页面,主动调用的,在当前页面取出页面的句柄
 *@param:PAGE_TYPE 页面类型
 *@ret:返回栈顶页面
 */
PAGE *page_mgr_lookup_check_active_page(PAGE_TYPE type)
{
	PAGE **page = pt_stack_peek(&page_stack);
	if(__page_mgr_init_flag == 0)
		return NULL;
	if(*page == NULL)
		return NULL;
	if(type != GUI_NONE_PAGE_TYPE){
		if((*page)->type != type){
			return NULL;
		}
	}
	return *page;
}
/**
 *@brief:打开一个新页面
 *@ret:返回码
 */
int page_mgr_open_new(PAGE_TYPE type)
{
	int ret = -1;
	PAGE *page = page_mgr_lookup_by_type(type);
	if(page == NULL){
		return -1;
	}
	PAGE **cur_page = NULL;
	cur_page = pt_stack_peek(&page_stack);					//<取当前栈顶页面
	if(cur_page){
		if((*cur_page)->type == type){						//<表示栈顶页面就是当前页面,可以直接不打开,页面去重
			return -2;
		}
	}
	if(page->init_flag == 0){								//<如果页面不存在,创建一个新的页面
		if(page->v_table.tp_gui_create){
			ret = page->v_table.tp_gui_create(page);		//<创建一个新的页面
		}
	}else{													//<不需要动态创建
		ret = 0;
	}
	if(ret == 0){											//表示可以显示
		page->init_flag = 1;
		if(cur_page && (*cur_page)->v_table.tp_gui_hidden){	//<当前栈顶旧页面隐藏
			ret = (*cur_page)->v_table.tp_gui_hidden(*cur_page);
		}
		pt_stack_push(&page_stack,page);					//<新页面入栈
		
		if(page->v_table.tp_gui_show){						//<新界面显示
			ret = page->v_table.tp_gui_show(page);
		}
	}
	return ret;
}
/**
 *@brief:返回上一个界面
 *@ret:状态返回值
 */
int page_mgr_back_open(void)
{
	int ret = 0;
	PAGE **cur_page = NULL;
	PAGE **pre_page = NULL;
	if(__page_mgr_init_flag == 0){//<如果没有页面初始化,直接返回
		return -1;
	}
	taskENTER_CRITICAL();
	/*取出来一个栈页面*/
	cur_page = pt_stack_pop(&page_stack);
	if(cur_page == NULL){
		ret = -1;
		goto clean;
	}
	/*取出当前栈顶元素*/
	pre_page = pt_stack_peek(&page_stack);
	if(pre_page == NULL){
		ret = -2;
		goto clean;
	}
	/*判断当前取出来的页面是常驻的还是动态的,静态的就隐藏,动态的就删除*/
	if((*cur_page)->mem_type == PERMANENT_PAGE_TYPE){//<如果是常驻界面隐藏掉
		if((*cur_page)->v_table.tp_gui_hidden){//<如果有隐藏回调,直接隐藏
			(*cur_page)->v_table.tp_gui_hidden(*cur_page);
		}
	}else{//<动态页面直接删除掉
		if((*cur_page)->v_table.tp_gui_cleanup){
			(*cur_page)->v_table.tp_gui_cleanup(*cur_page);
		}
		if((*cur_page)->v_table.tp_gui_delete){
			(*cur_page)->v_table.tp_gui_delete(*cur_page);
		}
		(*cur_page)->init_flag = 0;//<删除了页面
	}
	if((*pre_page)->v_table.tp_gui_show){
		(*pre_page)->v_table.tp_gui_show(*pre_page);
	}
clean:	
	taskEXIT_CRITICAL();
	return ret;
}


/**
 *@brief:跳转到指定类型的页面,从页面管理栈中查找
 *@param:type页面类型
 *@ret: 0成功 其他:失败
 */
int page_mgr_back_to_target_page(PAGE_TYPE type)
{
	int ret = 0;
	PAGE *page = NULL;
	PAGE **pr_page = NULL;//<寻找页面栈指针
	char page_exist_flag = 0;//<页面类型是否在页面栈中
	if(__page_mgr_init_flag == 0){
		return -1;
	}
	taskENTER_CRITICAL();
	/*1.遍历页面栈中是否存在当前类型的页面*/
	for(uint8_t i = 0; i <= page_stack.top; ++i){
		if(page_stack.buf[i]->type == type){
			page_exist_flag = 1;
			break;
		}
	}
	if(page_exist_flag == 0){//<表示页面不存在.创建一个新页面
		page_mgr_open_new(type);
		ret = 0;
		goto clean;
	}
	/*2.遍历页面栈,清除相关的页面资源直至到指定页面类型栈位置*/
	while((pr_page = pt_stack_pop(&page_stack)) != NULL){
		page = *pr_page;
		if(page->type == type){
			ret = 0;
			break;
		}
		/*2.1 隐藏常驻界面元素*/
		if(page->mem_type == PERMANENT_PAGE_TYPE){
			if(page->v_table.tp_gui_hidden){
				page->v_table.tp_gui_hidden(page);
			}
		}else{
		/*2.2 删除动态界面元素*/
			if(page->v_table.tp_gui_cleanup){
				page->v_table.tp_gui_cleanup(page);
			}
			if(page->v_table.tp_gui_delete){
				page->v_table.tp_gui_delete(page);
			}
		}
	}
	/*3.指定类型页面入栈,显示*/
	if(ret == 0){
		pt_stack_push(&page_stack,page);//<指定页面入栈
		if(page->v_table.tp_gui_show){//<显示
			page->v_table.tp_gui_show(page);
		}
	}
clean:	
	taskEXIT_CRITICAL();
}

/**
 *@brief:跳转到主运行页
 */
int page_mgr_back_to_main_page(void)
{
	return page_mgr_back_to_target_page(MAIN_PAGE_TYPE);
}

/**
 *@brief:跳转到菜单页
 */
int page_mgr_back_to_menu_page(void)
{
	return page_mgr_back_to_target_page(MENU_PAGE_TYPE);
}

/**
 *@brief:跳转到锁屏页
 */
int page_mgr_back_to_lockscreen_page(void)
{
	return page_mgr_back_to_target_page(LOCK_SCREEN_PAGE_TYPE);
}


/**
 *@brief:页面管理分发key
 *@param:
	-#1:logic_key 物理key
	-#2:lvgl_key lvgl key
 */
int page_mgr_dispatch_logic_key(uint8_t logic_key,uint8_t *lvgl_key)
{
	int ret = -1;
	PAGE *page = page_mgr_lookup_active_page();//<取栈顶页面
	if(page == NULL){
		return -2;
	}
	if(page->init_flag && page->v_table.tp_gui_dispatch_logic_key){
		ret = page->v_table.tp_gui_dispatch_logic_key(page,logic_key,lvgl_key);
	}
	return ret;
}

#include "gui_lock_screen_page.h"
//<一系列页面头文件
/**
 *
 *
 */
static int __page_register(void)
{
	/*屏保界面*/
	static LOCK_SCREEN_PAGE __lock_screen_page;
	lock_screen_page_init(&__lock_screen_page);
	__pages[__page_size++] = (PAGE *)&__lock_screen_page;
    // 主运行界面
    static MAIN_PAGE __main_page;
    main_page_init(&__main_page);
    __pages[__page_size++] = (PAGE*)&__main_page;
    
    // 主运行界面
    static MENU_PAGE __menu_page;
    menu_page_init(&__menu_page);
    __pages[__page_size++] = (PAGE*)&__menu_page;	
    
    // 系统查询-组网信息界面          
    DEVICE_RECORD_PAGE *__device_record_page = MALLOC(sizeof(DEVICE_RECORD_PAGE));
    device_record_page_init(__device_record_page);
    __pages[__page_size++] = (PAGE*)__device_record_page;
    
    // 系统查询-接口信息界面              
    INTERFACE_INFO_PAGE *__interface_info_page = MALLOC(sizeof(INTERFACE_INFO_PAGE));
    interface_info_page_init(__interface_info_page);
    __pages[__page_size++] = (PAGE*)__interface_info_page;
    
    // 系统查询-联动程序-菜单界面       
    PROGRAM_MENU_PAGE *__program_menu_page = MALLOC(sizeof(PROGRAM_MENU_PAGE));
    program_menu_page_init(__program_menu_page);
    __pages[__page_size++] = (PAGE*)__program_menu_page;

	//<新增页面即可
}

未完待续...

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/493345
推荐阅读
相关标签
  

闽ICP备14008679号