【全队项目】智能学术海报生成系统PosterGenius--图片布局生成模型LayoutPrompt(1)

🌈 个人主页:十二月的猫-CSDN博客
🔥 系列专栏: 🏀大模型实战训练营_十二月的猫的博客-CSDN博客

💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光

目录

1. 前言

2. LayoutPrompt介绍

3. LayoutPrompt · 布局序列化模块

3.1 固定化Prompt

3.2 父类序列化模块

3.3 子类序列化模块

4. 总结


1. 前言

        猫猫不知道大家有没有思考过图片布局生成模型,这算是生成模型的一个非常小的子任务了。前面带大家学习过生成模型,包括GAN、Diffusion等。这些都算是生成模型的研究子领域,利用这些子领域的知识,我们可以来研究具体的任务,例如生成图片、按照语言提示生成图片、按照布局提示生成图片等。

        同样生成布局也是生成模型中的一个具体任务,其实思路也是非常简单。生成图片这个任务中更具体的任务是生成海报、生成照片、生成动漫图片等。因为直接生成一个海报难度太大,我们就先去生成布局,然后在具体布局的约束下去具体生成完整图片。可以简单给大家看一下布局是什么:

        猫猫研究这个呢,主要还是因为创新实训和软件创新大赛两个需要。同时由于猫猫也研究过一点生成模型,因此这个学期就研究一下这个领域啦~~如果大家也对这个领域感兴趣,可以关注我们团队的专栏(布局生成模型是我们海报生成系统中的一个子模块,希望更多猫友参与到我们的开发当中哦):大模型实战训练营_十二月的猫的博客-CSDN博客

2. LayoutPrompt介绍

总的来说,为了完成LayoutPrompt模型,我们需要完成的子模块有:

  • 数据预处理模块:该模块主要就是利用基础数据预处理方法对数据集中的所有数据样本进行预处理。
  • 动态样本选择模块:从训练集中检索最相关的样本,然后作为最直接的上下文(约束信息)送给大语言模型。
  • 布局序列化模块:用于将上面所选的样本布局转化为序列表述(因为大语言模型对序列化输入有更好的效果)。序列化数据就是类似 自然语言、代码等
  • 大语言模型模块:将序列化处理后的所有样本一起送给大语言模型,让大语言模型参考的情况下给出自己的答案。
  • 大语言模型解析模块:用于将大语言模型给出的布局结果解析为标准化的输出。
  • 布局排序模块(布局评价模块):评价大语言模型生成的布局的质量分数,并做一个排序。 

具体的模型图如下:

模型运行具体流程如下:

  1. 用户输入前导信息,例如画布大小,任务类型等,数据预处理模块预处理数据库(用户根据自己的任务可以选择数据库,如海报布局数据库、手机UI设计数据库)中的所有数据。
  2. 动态样本选择模块得到处理后的数据库数据,然后根据用户输入的前导信息选择合适的example样本。
  3. 将样本+前导信息+测试样本送给大语言模型。
  4. 由大语言模型生成最终的layout,送给Rank模块。
  5. Rank模块排序后分数最高的就是最终输出。

3. LayoutPrompt · 布局序列化模块

布局序列化模块:本质就是序列化+Prompt。两者核心都是固定

序列化:将输入输出按照固定序列格式调整。

        例如输出固定如下:

  • "标题 0 0.1 0.2 0.3 0.4 | 正文 1 0.5 0.6 0.7 0.8"
  • # < html >
    # < body >
    # < div
    # style = "..." > 标题_0 < / div >
    # < div
    # style = "..." > 正文_1 < / div >
    # < / body >
    # < / html 

Prompt:根据不同任务,输入需要不同Prompt,同时序列化格式。

        例如输入固定如下:

一句话来说,布局序列化模块准备的就是模型中的这一部分:

  • 前导部分(PREAMBLE):固定Prompt,用户输入画布大小高度等。
  • 输入限制(INPUT CONSTRAINT) :会有两种表示形式1.如上图的seq形式;2.html形式。
  • 输出布局(OUTPUT LAYOUT):得到布局的坐标data后(由其他模块负责),序列化输出上图结果。

从代码角度来说分为三个部分:

  • 固定化Prompt。
  • 父类序列化模块(固定输出序列结构,输入序列留接口给子类实现)
  • 子类序列化模块(一个子类对应一个具体的任务,不同任务输入结构不一样)

从任务角度存在以下七种:

  • 元素类型任务(限制layout中的元素)
  • 元素类型,元素尺寸限制任务
  • 元素类型,元素之间位置关系
  • 元素补全(根据部分已知布局元素,生成完整布局结构)
  • 元素尺寸修正(给出类型和尺寸,模型自己修正尺寸)
  • 防止遮挡的布局生成
  • 文本描述下的布局生成

3.1 固定化Prompt

PREAMBLE = ("Please generate a layout based on the given information. ""You need to ensure that the generated layout looks realistic, with elements well aligned and avoiding unnecessary overlap.\n""Task Description: {}\n""Layout Domain: {} layout\n""Canvas Size: canvas width is {}px, canvas height is {}px"
)
# html的头部
HTML_PREFIX = """<html>
<body>
<div class="canvas" style="left: 0px; top: 0px; width: {}px; height: {}px"></div>
"""
# html的结尾
HTML_SUFFIX = """</body>
</html>"""# html的body
HTML_TEMPLATE = """<div class="{}" style="left: {}px; top: {}px; width: {}px; height: {}px"></div>
"""HTML_TEMPLATE_WITH_INDEX = """<div class="{}" style="index: {}; left: {}px; top: {}px; width: {}px; height: {}px"></div>
"""
  • 本部分主要是为了固定输入输出的一些序列化格式,同时固定前导部分
  • 用户在这里通过前端输入自己想要的画布大小高度等信息。

3.2 父类序列化模块

# 序列化的父类,后面有具体任务不同的序列化
# 所谓序列化本质就是固定输入的prompt形式,同时固定输出的一个形式。
class Serializer:def __init__(self,input_format: str,output_format: str,index2label: dict,canvas_width: int,canvas_height: int,add_index_token: bool = True,add_sep_token: bool = True,sep_token: str = "|",add_unk_token: bool = False,unk_token: str = "<unk>",):self.input_format = input_formatself.output_format = output_formatself.index2label = index2labelself.canvas_width = canvas_widthself.canvas_height = canvas_heightself.add_index_token = add_index_tokenself.add_sep_token = add_sep_tokenself.sep_token = sep_tokenself.add_unk_token = add_unk_tokenself.unk_token = unk_tokendef build_input(self, data):if self.input_format == "seq":return self._build_seq_input(data)elif self.input_format == "html":return self._build_html_input(data)else:raise ValueError(f"Unsupported input format: {self.input_format}")# check value is not nulldef _build_seq_input(self, data):raise NotImplementedErrordef _build_html_input(self, data):raise NotImplementedErrordef build_output(self, data, label_key="labels", bbox_key="discrete_gold_bboxes"):if self.output_format == "seq":return self._build_seq_output(data, label_key, bbox_key)elif self.output_format == "html":return self._build_html_output(data, label_key, bbox_key)# # 输入数据结构示例# data = {#     "labels": [0, 1],  # 标签索引#     "discrete_gold_bboxes": [  # 坐标列表(假设已离散化)#         [0.1, 0.2, 0.3, 0.4],#         [0.5, 0.6, 0.7, 0.8]#     ]# }# "标题 0 0.1 0.2 0.3 0.4 | 正文 1 0.5 0.6 0.7 0.8"def _build_seq_output(self, data, label_key, bbox_key):# 在字典中存储的标签信息,和边框信息(不是存储具体字,而是存储信息)labels = data[label_key]bboxes = data[bbox_key]tokens = []for idx in range(len(labels)):label = self.index2label[int(labels[idx])]bbox = bboxes[idx].tolist()tokens.append(label)if self.add_index_token:tokens.append(str(idx))tokens.extend(map(str, bbox)) # extend一次性添加很多值,append一次添加一个值。map(function,list):把function作用在list上。str():将其他类型转为str类型。if self.add_sep_token and idx < len(labels) - 1:tokens.append(self.sep_token) # 添加隔离符号return " ".join(tokens)# # 输出结构# < html ># < body ># < div# style = "..." > 标题_0 < / div ># < div# style = "..." > 正文_1 < / div ># < / body ># < / html >def _build_html_output(self, data, label_key, bbox_key):labels = data[label_key]bboxes = data[bbox_key]htmls = [HTML_PREFIX.format(self.canvas_width, self.canvas_height)]  # 使用 HTML_PREFIX 作为 HTML 页面的开头,并将画布宽度和高度传递进去。_TEMPLATE = HTML_TEMPLATE_WITH_INDEX if self.add_index_token else HTML_TEMPLATE # 根据 add_index_token 决定使用哪种模板:带索引的模板或普通模板。for idx in range(len(labels)):label = self.index2label[int(labels[idx])]bbox = bboxes[idx].tolist()element = [label]if self.add_index_token:element.append(str(idx))element.extend(map(str, bbox))htmls.append(_TEMPLATE.format(*element))htmls.append(HTML_SUFFIX)return "".join(htmls)
  • 主要定义两种输出方式的序列化。第一种是以seq的形式输出;第二种是以html的形式输出。
  • 输入形式的序列化(prompt+序列化)仅仅定义了模板交给子类(具体任务)来实现。
  • expand和append都是列表后追加,但expand一次追加很多个元素,append一次加一个。
  • map(function,list):将function作用在所有的list元素上。
  • 作用:1. 得到其他模块给的Layout坐标data后,将其转化为html或seq的固定序列化格式输出。2.作为父类将输入序列化交给子类实现。

3.3 子类序列化模块

        前面也说了,子类序列化模块需要根据不同的任务要求给出不同的prompt以及序列化输入。因此有多少个任务就会有多少个子类序列化模块。在这里,猫猫仅仅展示两个模块的代码,完整的代码等专栏更新结束后,会同步放在Gitee以及CSDN账号下。

任务一:限制元素类型的布局生成

class GenTypeSerializer(Serializer):task_type = "generation conditioned on given element types"constraint_type = ["Element Type Constraint: "]HTML_TEMPLATE_WITHOUT_ANK = '<div class="{}"></div>\n'HTML_TEMPLATE_WITHOUT_ANK_WITH_INDEX = '<div class="{}" style="index: {}"></div>\n'def _build_seq_input(self, data):labels = data["labels"]tokens = []for idx in range(len(labels)):label = self.index2label[int(labels[idx])]tokens.append(label)if self.add_index_token:tokens.append(str(idx))if self.add_unk_token:tokens += [self.unk_token] * 4if self.add_sep_token and idx < len(labels) - 1:tokens.append(self.sep_token)return " ".join(tokens)def _build_html_input(self, data):labels = data["labels"]htmls = [HTML_PREFIX.format(self.canvas_width, self.canvas_height)]if self.add_index_token and self.add_unk_token:_TEMPLATE = HTML_TEMPLATE_WITH_INDEXelif self.add_index_token and not self.add_unk_token:_TEMPLATE = self.HTML_TEMPLATE_WITHOUT_ANK_WITH_INDEXelif not self.add_index_token and self.add_unk_token:_TEMPLATE = HTML_TEMPLATEelse:_TEMPLATE = self.HTML_TEMPLATE_WITHOUT_ANKfor idx in range(len(labels)):label = self.index2label[int(labels[idx])]element = [label]if self.add_index_token:element.append(str(idx))if self.add_unk_token:element += [self.unk_token] * 4htmls.append(_TEMPLATE.format(*element))htmls.append(HTML_SUFFIX)return "".join(htmls)def build_input(self, data):return self.constraint_type[0] + super().build_input(data)

任务二:限制元素类型,以及元素关系的布局生成

class GenRelationSerializer(Serializer):task_type = ("generation conditioned on given element relationships\n""'A left B' means that the center coordinate of A is to the left of the center coordinate of B. ""'A right B' means that the center coordinate of A is to the right of the center coordinate of B. ""'A top B' means that the center coordinate of A is above the center coordinate of B. ""'A bottom B' means that the center coordinate of A is below the center coordinate of B. ""'A center B' means that the center coordinate of A and the center coordinate of B are very close. ""'A smaller B' means that the area of A is smaller than the ares of B. ""'A larger B' means that the area of A is larger than the ares of B. ""'A equal B' means that the area of A and the ares of B are very close. ""Here, center coordinate = (left + width / 2, top + height / 2), ""area = width * height")constraint_type = ["Element Type Constraint: ", "Element Relationship Constraint: "]HTML_TEMPLATE_WITHOUT_ANK = '<div class="{}"></div>\n'HTML_TEMPLATE_WITHOUT_ANK_WITH_INDEX = '<div class="{}" style="index: {}"></div>\n'def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.index2type = RelationTypes.index2type()def _build_seq_input(self, data):labels = data["labels"]relations = data["relations"]tokens = []for idx in range(len(labels)):label = self.index2label[int(labels[idx])]tokens.append(label)if self.add_index_token:tokens.append(str(idx))if self.add_unk_token:tokens += [self.unk_token] * 4if self.add_sep_token and idx < len(labels) - 1:tokens.append(self.sep_token)type_cons = " ".join(tokens)if len(relations) == 0:return self.constraint_type[0] + type_constokens = []for idx in range(len(relations)):label_i = relations[idx][2]index_i = relations[idx][3]if label_i != 0:tokens.append("{} {}".format(self.index2label[int(label_i)], index_i))else:tokens.append("canvas")tokens.append(self.index2type[int(relations[idx][4])])label_j = relations[idx][0]index_j = relations[idx][1]if label_j != 0:tokens.append("{} {}".format(self.index2label[int(label_j)], index_j))else:tokens.append("canvas")if self.add_sep_token and idx < len(relations) - 1:tokens.append(self.sep_token)relation_cons = " ".join(tokens)return (self.constraint_type[0]+ type_cons+ "\n"+ self.constraint_type[1]+ relation_cons)def _build_html_input(self, data):labels = data["labels"]relations = data["relations"]htmls = [HTML_PREFIX.format(self.canvas_width, self.canvas_height)]if self.add_index_token and self.add_unk_token:_TEMPLATE = HTML_TEMPLATE_WITH_INDEXelif self.add_index_token and not self.add_unk_token:_TEMPLATE = self.HTML_TEMPLATE_WITHOUT_ANK_WITH_INDEXelif not self.add_index_token and self.add_unk_token:_TEMPLATE = HTML_TEMPLATEelse:_TEMPLATE = self.HTML_TEMPLATE_WITHOUT_ANKfor idx in range(len(labels)):label = self.index2label[int(labels[idx])]element = [label]if self.add_index_token:element.append(str(idx))if self.add_unk_token:element += [self.unk_token] * 4htmls.append(_TEMPLATE.format(*element))htmls.append(HTML_SUFFIX)type_cons = "".join(htmls)if len(relations) == 0:return self.constraint_type[0] + type_constokens = []for idx in range(len(relations)):label_i = relations[idx][2]index_i = relations[idx][3]if label_i != 0:tokens.append("{} {}".format(self.index2label[int(label_i)], index_i))else:tokens.append("canvas")tokens.append(self.index2type[int(relations[idx][4])])label_j = relations[idx][0]index_j = relations[idx][1]if label_j != 0:tokens.append("{} {}".format(self.index2label[int(label_j)], index_j))else:tokens.append("canvas")if self.add_sep_token and idx < len(relations) - 1:tokens.append(self.sep_token)relation_cons = " ".join(tokens)return (self.constraint_type[0]+ type_cons+ "\n"+ self.constraint_type[1]+ relation_cons)
  • 针对此任务设计了具体的Prompt形式。
  • 输入数据序列化有两个部分:1、seq序列化;2、html序列化
  • seq序列化:用户输入限制后,需要结合Prompt序列化到INPUT_CONSTRAINT(
  • (注意:这个seq并不是完整的,仅仅是将用户的限制要求填入):
  • html序列化:根据用户输入限制生成对应的html格式(注意:这个html并不是完整的,仅仅是将用户的限制要求填入)
  • 作用:1.将用户输入的限制转化为某种序列化(seq序列化 或 html序列化)。2.结合固定Prompt包装成完整的INPUT CONSTRAINT

4. 总结

本篇文章带大家深入了解了PosterGenius项目的Layout生成部分的第一篇,后续将更新Layout系列的第二篇。欢迎大家继续支持猫猫呀!!

 【如果想学习更多深度学习文章,可以订阅一下热门专栏】

  • 《PyTorch科研加速指南:即插即用式模块开发》_十二月的猫的博客-CSDN博客
  • 《深度学习理论直觉三十讲》_十二月的猫的博客-CSDN博客
  • 《AI认知筑基三十讲》_十二月的猫的博客-CSDN博客

如果想要学习更多pyTorch/python编程的知识,大家可以点个关注并订阅,持续学习、天天进步你的点赞就是我更新的动力,如果觉得对你有帮助,辛苦友友点个赞,收个藏呀~~~

本文撰写人:十二月的猫   十二月的猫-CSDN博客

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

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

相关文章

位图的实现和拓展

一&#xff1a;位图的介绍 ①&#xff1a;需要位图的场景 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中&#xff1f; 要判断一个数是否在某一堆数中&#xff0c;我们可能会想到如下方法&#xff1a; A…

排序功法入门指南【江湖算法笔记】

话说江湖风云变幻&#xff0c;各路英雄好汉行走江湖&#xff0c;总得有个名号排行。若问“东邪西毒南帝北丐”谁强谁弱&#xff0c;总得排个座次不是&#xff1f;这排序之道&#xff0c;恰似武功秘籍&#xff0c;练好了能号令群雄&#xff0c;练岔了怕是要被笑掉大牙&#xff0…

【中间件】brpc_基础_用户态线程中断

bthread之用户态线程中断 源码 1 简介 interrupt_pthread 核心功能是 通过信号机制中断阻塞的 pthread 线程&#xff0c;以实现线程的协作式中断。 2 核心功能与设计 2.1 信号选择与注册 信号选择&#xff1a;使用 SIGURG 作为中断信号。 原因&#xff1a;SIGURG 通常用于…

Linux 的网络卡

#本机操作系统CentOS 10 #核心版本 rootbogon:/etc# uname -r 6.12.0-65.el10.x86_64 网卡能不能被捉到可以使用【dmesg|grep xx】来判断&#xff0c;有没有驱动则可以使用lsmod看看模块有没有加载核心&#xff01;最后&#xff0c;以ifconfig xxx测试看看 观察核心所捉到的网卡…

前端双工通信的几种方案详细描述

前端实现双工通信&#xff08;全双工或半双工&#xff09;的常见方案及详细实现如下&#xff1a; 一、WebSocket&#xff08;全双工&#xff09; 原理&#xff1a;基于 TCP 的持久化协议&#xff0c;客户端与服务端建立双向通信通道&#xff0c;支持实时双向数据传输。 // 客…

KUKA机器人快速启动设置

KUKA机器人在首次开机启动时&#xff0c;有时在示教器上需要进行投入运行等相关的设置。如以下相关的信息需要处理&#xff1a; 1、机器人系统开机后&#xff0c;选择T1运行模式&#xff1b;2、显示提示信息&#xff1a;“RDC 存储器和控制系统不一致什么被更换了”时&#xf…

游戏代码C

以下将结合不同编程语言的特点及游戏开发中的实际应用&#xff0c;展示多种语言的游戏代码示例&#xff08;以简单游戏为例&#xff0c;展示代码结构和逻辑差异&#xff09;。由于代码篇幅较长&#xff0c;我将分语言进行说明并引用相关来源&#xff1a; 1. C# Unity&#xff…

LangChain Agent核心解析:Zero-Shot-ReAct策略实现与实战指南

引言 在LangChain的Agent框架中&#xff0c;zero-shot-react-description 是一种预定义的Agent类型&#xff0c;它结合了Zero-Shot&#xff08;零样本学习&#xff09; 和 ReAct&#xff08;推理行动&#xff09; 策略&#xff0c;主要用于根据工具的描述动态选择和执行工具&a…

PyQt 或 PySide6 进行 GUI 开发文档与教程

一、官网文档 Qt 官方文档&#xff1a;Porting to Qt 6 | Qt 6.9Qt 维基&#xff1a;​​​​​​​Qt WikiQt for Python (PySide6) &#xff1a;​​​​​​​Qt for Python - Qt WikiPySide6 快速上手指南&#xff1a;​​​​​​​Getting Started - Qt for Python PyS…

2024年第十五届蓝桥杯省赛B组Python【 简洁易懂题解】

2024年第十五届蓝桥杯省赛B组Python题解 一、整体情况说明 2024年第十五届蓝桥杯省赛B组Python组考试共包含8道题目&#xff0c;分为结果填空题和程序设计题两类。 考试时间&#xff1a;4小时编程环境&#xff1a;Python 3.x&#xff0c;禁止使用第三方库&#xff0c;仅可使…

Go语言--语法基础4--基本数据类型--类型转换

Go 是一种强类型的语言&#xff0c;所以如果在赋值的时候两边类型不一致会报错。一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换&#xff0c;因此所有的类型转换都必须显式的声明。 强制类型转换语法 使用 type (a) 这种形式来进行强制类型转换&am…

nginx 代理时怎么更改 Remote Address 请求头

今天工作中遇到用 localhost 访问网站能访问后台 api&#xff0c;但是用本机IP地址后就拒绝访问&#xff0c;我怀疑是后台获取 Remote Address 然后设置白名单了只能 localhost 访问。 想用 nginx 更改 Remote Address server {listen 8058;server_name localhost;loca…

LeetCode刷题链表

文章目录 链表总结 常用技巧两数相加题解代码 两两交换链表中的节点题解代码 重排链表题解代码 合并k个升序链表题解代码 K个一组翻转链表题解代码 链表总结 常用技巧 画图 直观 形象 便于理解引入虚拟头节点&#xff0c;便于处理边界情况&#xff0c;方便我们对链表进行…

ESP32S3 多固件烧录方法、合并多个固件为单一固件方法

ESP32S3 多固件烧录方法、合并多个固件为单一固件方法 文章目录 ESP32S3 多固件烧录方法、合并多个固件为单一固件方法前言1、前期准备工作2、多固件烧录方法3、单固件烧录方法总结 前言 使用正点原子的ESP32S3 BOX开发板独立烧录编译生成的xxx.bin固件无法正常运行起来&#…

Webug4.0靶场通关笔记10- 第14关链接注入

目录 第14关 链接注入 1.打开靶场 2.源码分析 3.渗透实战 &#xff08;1&#xff09;方法1&#xff1a;跳转外部网页 &#xff08;2&#xff09;方法2&#xff1a;获取cookie 4.漏洞防御 本文通过《webug靶场第14关 链接注入》来进行渗透实战。 第14关 链接注入 链接注…

SpringBoot的汽车商城后台管理系统源码开发实现

概述 汽车商城后台管理系统专为汽车4S店和经销商设计&#xff0c;提供全面的汽车管理系统解决方案。 主要内容 1. 核心功能模块 系统提供以下主要功能&#xff1a; ​​销售管理​​&#xff1a;记录销售信息&#xff0c;跟踪交易进度​​客户管理​​&#xff1a;维护客户…

VBA代码解决方案第二十四讲:EXCEL中,如何删除重复数据行

《VBA代码解决方案》(版权10028096)这套教程是我最早推出的教程&#xff0c;目前已经是第三版修订了。这套教程定位于入门后的提高&#xff0c;在学习这套教程过程中&#xff0c;侧重点是要理解及掌握我的“积木编程”思想。要灵活运用教程中的实例像搭积木一样把自己喜欢的代码…

日本IT行业|salesforce开发语言占据的地位

在日本的IT行业中&#xff0c;Salesforce 开发语言处于一个较为专业但稳步增长的细分领域&#xff0c;并不是主流开发语言&#xff08;如 Java、Python、PHP&#xff09;&#xff0c;但其在某些行业和场景中地位越来越重要。 本篇以下是详细分析&#xff1a; Salesforce开发语言…

前端开发,文件在镜像服务器上不存在问题:Downloading binary from...Cannot download...

问题与处理策略 问题描述 在 Vue 项目中&#xff0c;执行 npm i 下载依赖时&#xff0c;报如下错误 Downloading binary from https://npm.taobao.org/mirrors/node-sass//v4.14.1/win32-x64-72_binding.node Cannot download "https://npm.taobao.org/mirrors/node-sa…

基于Vue2 + Element 实现任务列表管理功能的详细教程

前言&#xff1a;本文介绍的是如何从0开始搭建Vue2项目到1实现对任务添加、删除和筛选的功能&#xff0c;&#x1f517; 相关链接Vue 入门(安装与应用超详细教程) ❤ 【作者主页—&#x1f4da;阅读更多优质文章、获取更多优质源码】 目录 一 . 项目搭建 1.1 安装node.js 1.…