大模型应用开发初探 : 手搓一个简易Agent - EdisonZhou - 博客园

mikel阅读(141)

来源: 大模型应用开发初探 : 手搓一个简易Agent – EdisonZhou – 博客园

大家好,我是Edison。

今天是中秋节前最后一个工作日,加油挺住,马上就放假了!

近期我一直在学习和了解LLM的相关知识,听到大家都在谈论AI Agent,说它是接下来几年大模型应用开发的新范式,那么什么是AI Agent,如何快速开发一个AI Agent呢?

AI Agent:可以帮你执行任务的助手

学术界和工业界对术语“AI Agent”提出了各种定义。其中,OpenAI将AI Agent定义为“以大语言模型为大脑驱动的系统,具备自主理解、感知、规划、记忆和使用工具的能力,能够自动化执行完成复杂任务的系统。”

说人话就是:大多数时候你给它一个最终你想要达成的目标,它能直接交付结果,过程你啥都不用管

NOTE:如果说人和动物的区别是人会使用各种工具,那么Agent和大模型的区别亦然。

我们可以把Agent与LLM形象地比作生物体与其大脑,Agent有手有脚,可以自己干活自己执行,而LLM呢,就是它的大脑。比如,如果你使用LLM大模型,它可能只能给你输出一份食谱,告诉你需要哪些食材和步骤来制作。但如果你使用Agent,它可能就是不仅提供食谱和步骤,还会根据你的需求,帮你选择合适的食材甚至自动下单购买,监控烹饪过程,确保食物口感,最终为你呈上一份佳肴。

AI Agent如何工作?

AI Agent的架构是其智能行为的基础,它通常包括感知、规划、记忆、工具使用和行动等关键组件,这些组件协同工作以实现高效的智能行为。

AI Agent的工作流程其实就是一个连续的循环过程

它从感知环境开始,经过信息处理、规划和决策,然后执行行动。最后,根据执行结果和环境反馈进行调整,以优化未来的行动和决策。

通过这种结构化和层次化的方式,AI Agent能够有效地处理信息,做出决策,并在复杂环境中执行任务。

如何开发AI Agent?

目前业界开发AI Agent主要有两种模式:

一种是基于Python或C#等编程语言,结合LangChain或Semantic Kernel等大模型应用开发框架,集成某个大模型API 和 企业内部的业务API能力,来完成具体领域的Agent。

另一种是基于Coze、Dify、AutoGen等Agent开发管理平台,拖过拖拉拽的方式快速生成一个Agent,与其说是开发,不如说是Workflow一样的配置。当然,也需要给这些平台注册封装好的企业内部API平台提供的能力供配置好的Agent去实现工具调用。

使用Semantic Kernel开发AI Agent

这里我们快速使用Semantic Kernel开发一个简易的WorkOrder Agent(MES工单助手),重点关注如何给LLM添加Function Calling能力,直观了解Agent规划任务 和 执行任务 的效果,而至于其他更加具体的,等待后续了解深入后再交流,这里我们就先来个感性认识即可。

以终为始,先看效果吧:

(1)没有实现Function Calling的效果,它就只是个Chatbot

(2)实现了Function Calling的效果,它就可以称为Agent

可以看到,我的需求其实包含两个步骤:第一步是更新工单的Quantity,第二步是查询更新后的工单信息。而这两个步骤我们假设其实都是需要去调用MES WorkOrderService API才能获得的,而这就需要我们给LLM加入Function Calling的能力,当然LLM自己得知道如何规划执行的步骤,哪个步骤先执行,哪个后执行。

示例代码的结构如下所示:

关键部分代码:

(1)Shared

OpenAiConfiguration.cs

复制代码
public class OpenAiConfiguration
{
    public string Provider { get; set; }
    public string ModelId { get; set; }
    public string EndPoint { get; set; }
    public string ApiKey { get; set; }

    public OpenAiConfiguration(string modelId, string endPoint, string apiKey)
    {
        Provider = ConfigConstants.LLMProviders.OpenAI; // Default OpenAI-Compatible LLM API Provider
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }

    public OpenAiConfiguration(string provider, string modelId, string endPoint, string apiKey)
    {
        Provider = provider;
        ModelId = modelId;
        EndPoint = endPoint;
        ApiKey = apiKey;
    }
}
复制代码

CustomLLMApiHandler.cs

复制代码
public class CustomLLMApiHandler : HttpClientHandler
{
    private readonly string _openAiProvider;
    private readonly string _openAiBaseAddress;

    public CustomLLMApiHandler(string openAiProvider, string openAiBaseAddress)
    {
        _openAiProvider = openAiProvider;
        _openAiBaseAddress = openAiBaseAddress;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        UriBuilder uriBuilder;
        Uri uri = new Uri(_openAiBaseAddress);
        switch (request.RequestUri?.LocalPath)
        {
            case "/v1/chat/completions":
                switch (_openAiProvider)
                {
                    case ConfigConstants.LLMProviders.ZhiPuAI:
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.ZhiPuAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                    default: // Default: OpenAI-Compatible API Providers
                        uriBuilder = new UriBuilder(request.RequestUri)
                        {
                            Scheme = "https",
                            Host = uri.Host,
                            Path = ConfigConstants.LLMApiPaths.OpenAIChatCompletions,
                        };
                        request.RequestUri = uriBuilder.Uri;
                        break;
                }
                break;
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}
复制代码

(2)WorkOrderService

这里我直接模拟的API的逻辑,你可以使用HttpClient去实际调用某个API。

复制代码
public class WorkOrderService
{
    private static List<WorkOrder> workOrders = new List<WorkOrder>
            {
                new WorkOrder { WorkOrderName = "9050100", ProductName = "A5E900100", ProductVersion = "001 / AB", Quantity = 100, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050101", ProductName = "A5E900101", ProductVersion = "001 / AB", Quantity = 200, Status = "Ready" },
                new WorkOrder { WorkOrderName = "9050102", ProductName = "A5E900102", ProductVersion = "001 / AB", Quantity = 300, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050103", ProductName = "A5E900103", ProductVersion = "001 / AB", Quantity = 400, Status = "InProcess" },
                new WorkOrder { WorkOrderName = "9050104", ProductName = "A5E900104", ProductVersion = "001 / AB", Quantity = 500, Status = "Completed" }
            };

    public WorkOrder GetWorkOrderInfo(string orderName)
    {
        return workOrders.Find(o => o.WorkOrderName == orderName);
    }

    public string UpdateWorkOrderStatus(string orderName, string newStatus)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";
        // Update status if it is valid
        workOrder.Status = newStatus;
        return "Operate Succeed!";
    }

    public string ReduceWorkOrderQuantity(string orderName, int newQuantity)
    {
        var workOrder = this.GetWorkOrderInfo(orderName);
        if (workOrder == null)
            return "Operate Failed : The work order is not existing!";

        // Some business checking logic like this
        if (workOrder.Status == "Completed")
            return "Operate Failed : The work order is completed, can not be reduced!";
        if (newQuantity <= 1 || newQuantity >= workOrder.Quantity)
            return "Operate Failed : The new quantity is invalid!";
        // Update quantity if it is valid
        workOrder.Quantity = newQuantity;
        return "Operate Succeed!";
    }
}
复制代码

(3)Form

appsetting.json

复制代码
{
  "LLM_API_PROVIDER": "ZhiPuAI",
  "LLM_API_MODEL": "glm-4",
  "LLM_API_BASE_URL": "https://open.bigmodel.cn",
  "LLM_API_KEY": "***********" // Update this value to yours
}
复制代码

AgentForm.cs

初始化Kernel

复制代码
 private Kernel _kernel = null;
 private OpenAIPromptExecutionSettings _settings = null;
 private IChatCompletionService _chatCompletion = null;
 private ChatHistory _chatHistory = null;

private void ChatForm_Load(object sender, EventArgs e)
    {
        var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.ZhiPu.json");
        var config = configuration.Build();
        var openAiConfiguration = new OpenAiConfiguration(
            config.GetSection("LLM_API_PROVIDER").Value,
            config.GetSection("LLM_API_MODEL").Value,
            config.GetSection("LLM_API_BASE_URL").Value,
            config.GetSection("LLM_API_KEY").Value);
        var openAiClient = new HttpClient(new CustomLlmApiHandler(openAiConfiguration.Provider, openAiConfiguration.EndPoint));
        _kernel = Kernel.CreateBuilder()
            .AddOpenAIChatCompletion(openAiConfiguration.ModelId, openAiConfiguration.ApiKey, httpClient: openAiClient)
            .Build();

        _chatCompletion = _kernel.GetRequiredService<IChatCompletionService>();

        _chatHistory = new ChatHistory();
        _chatHistory.AddSystemMessage("You are one WorkOrder Assistant.");
    }
复制代码

注册Functions

复制代码
_kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions("WorkOrderHelperPlugin",
    new List<KernelFunction>
    {
                    _kernel.CreateFunctionFromMethod((string orderName) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.GetWorkOrderInfo(orderName);
                    }, "GetWorkOrderInfo", "Get WorkOrder's Detail Information"),
                    _kernel.CreateFunctionFromMethod((string orderName, int newQuantity) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.ReduceWorkOrderQuantity(orderName, newQuantity);
                    }, "ReduceWorkOrderQuantity", "Reduce WorkOrder's Quantity to new Quantity"),
                    _kernel.CreateFunctionFromMethod((string orderName, string newStatus) =>
                    {
                        var workOrderRepository = new WorkOrderService();
                        return workOrderRepository.UpdateWorkOrderStatus(orderName, newStatus);
                    }, "UpdateWorkOrderStatus", "Update WorkOrder's Status to new Status")
     }
));
复制代码

开启自动调用Function,告诉大模型可以自行决定调用相关Functions,而且大模型会自行决定根据什么顺序来调用,这就是大模型作为Agent大脑的规划能力。当然,我们还可以通过定制化Planner来增强agent的规划能力,这个就留到后面再分享:

_settings = new OpenAIPromptExecutionSettings
{
   ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

发送用户的提示词给大模型:

复制代码
_chatHistory.AddUserMessage(tbxPrompt.Text);
        ChatMessageContent chatResponse = null;
        tbxResponse.Clear();

if (cbxUseFunctionCalling.Checked)
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, _settings, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
        }
else
{
            Task.Run(() =>
            {
                ShowProcessMessage("AI is handling your request now...");
                chatResponse = _chatCompletion.GetChatMessageContentAsync(_chatHistory, null, _kernel)
                    .GetAwaiter()
                    .GetResult();
                UpdateResponseContent(chatResponse.ToString());
                ShowProcessMessage("AI Response:");
            });
}
复制代码

小结

本文简单介绍了AI Agent的基本概念 和 工作方式,目前主要有两种开发Agent的模式,一种是高代码手搓,另一种是低代码拖拉拽。

最后,本文通过C# + Semantic Kernel + 智谱GLM-4模型 演示了如何快速开发一个简易的AI Agent,虽然它只是个Demo,但希望对你快速了解Agent有所帮助!

示例源码

本文示例:https://github.com/Coder-EdisonZhou/EDT.Agent.Demos

本文大模型:智谱 GLM-4 模型

推荐学习

Microsoft Learn, 《Semantic Kernel 学习之路

大模型应用开发初探 : 基于Coze创建Agent - EdisonZhou - 博客园

mikel阅读(191)

来源: 大模型应用开发初探 : 基于Coze创建Agent – EdisonZhou – 博客园

大家好,我是Edison。

最近学习了一门课程《AI Agent入门实战》,了解了如何在Coze平台上创建AI Agent,发现它对我们个人(C端用户)而言十分有用,分享给你一下。

Coze是什么?

Coze(扣子)是字节跳动公司开发的新一代AI应用开发平台,使用这个AI应用开发平台,无论你是否有编码基础,都可以快速搭建基于大语言模型的各类AI Bot,还可以将Bot发布到其他渠道。对于一个AI Agent而言,最重要的能力就是任务规划、调用工具、知识库 和 记忆能力,而这些能力在Coze中你都不需要关注,已经封装好了提供给你,对你而言就是透明的。

如上图所示,我们可以对我们要做的AI Agent先设立人设,然后给它注册想要调用的工具或工作流,还可以给它注册一个内部知识库(文档/图片/表格等),如果想要记忆能力甚至可以直接给它添加一个数据库供其使用,最后再通过调试模块进行测试,一个针对AI Agent的“宇宙最强IDE“也不过如此。
目前,Coze有两个版本:

(1)基础版:面向尝鲜体验的个人和企业开发者,全部功能免费使用,但有一定的限量额度,超过后不可再使用,需切换专业版后继续使用。

(2)专业版:面向对稳定性和用量有更高需求的专业开发者,支持更高团队空间容量和免费知识库容量,付费功能保障专业级 SLA,不限制调用请求频率和总量,费用按实际用量计算。

这里,我用的是基础版,主要是尝尝鲜,做了几个DEMO体验下效果,用到的模型主要是豆包的Function Call模型。未来,我们可能会主要尝试企业内部搭建的FastGPT或Dify,又或者是微软的AutoGen。

下面,主要通过我做的这几个DEMO一起来看看Coze提供的一些关键能力。

强大的工作流配置

我通过Coze创建了一个城市天气助手的Bot,使用了Coze提供的工作流能力,如下图所示,这是一个获取天气预报并解析的工作流:

可以看到,通过一个简单的工作流,我们就快速调用了大模型 和 插件(墨迹天气)的能力,而这些操作在传统的编码场景下,都需要程序员单独来处理,现在则是0代码纯配置就可以了。

基于这个工作流,我再把人设和回复的逻辑配置一下提示词,就可以完成一个Bot的创建。值得一提的是,针对你的提示词,Coze提供了一个优化的功能,可以按照最佳实践将你的提示词做一个优化,这真的是一个很实用的功能。

最后实现的效果如下图所示:

强大的图像流配置

我通过Coze创建了一个产品图背景替换助手的Bot,用到了Coze提供的另一个强大技能:图像流。这也是一个工作流,但是其用到了专门针对图像处理的处理节点,例如图像生成、背景替换、画质提升等等。这些功能对于有做社交媒体运营的朋友,应该挺有帮助的。

最后的效果如下图所示:我把原图 和 想要替换的背景描述给它,它给我输出了一张还算不错的背景替换图。

快捷的知识库应用

Coze支持不同格式的知识库,例如文本类型(如txt, pdf, doc等)、表格类型(如xls等)以及 照片类型(如png, jpg等)。

比如,我创建了一个MongoDB知识助手的Bot,就导入了一些MongoDB的体系课程的pdf文档:

最终的效果如下图所示:

又如,我创建了一个产品图查询助手的Bot,可以基于我导入的产品图资料库,让我可以快速的查找到对应的产品图。

效果如下图所示:

再如,假设我是一个在线课堂的老板,我将课程订单表(Excel)导入到知识库中,通过对人设和回复逻辑的设置,就可以实现一个快速查询的功能:

透明的记忆能力

假设我是一个在线课堂的老板,我可以用Coze创建一个在线客服,让它和客户对话,并试图引导用户留下姓名和联系方式,这就需要一个类似于数据库的记忆能力。

这样配置后,一旦客户在对话中留下联系方式,我们的Bot就会自动将其存入预先设置的数据库中:

其他能力

对于客服类Bot,语音能力是非常重要的,在Coze中可以支持语音通话,还有多种口音供选择,个人觉得这是很方便的一个支持能力。

发布到订阅号

Coze可以支持发布到多个平台,未来可能真的会有Agent Store的概念。不过,我目前最喜欢的还是可以直接发布到微信订阅号,这样大家在给我回复时,不只是有冷冰冰的自动回复,而是有情绪价值的回复,for all of you!

小结

本文简单介绍了Coze(扣子)这个AI应用开发平台的主要功能,通过我所学习实践的一些DEMO来了解一下在AI Agent开发中涉及到一些核心概念如工作流、图像流、记忆能力、知识库等等,相信会对大家在今后的AI Agent开发实践中有所帮助。

推荐学习

周文洋,《AI Agent入门实战》

Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

mikel阅读(137)

来源: Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例_log parser lizard gui:-CSDN博客

Log Parser 日志分析工具,用命令行操作,可以分析 IIS logs,event logs,active directory,log4net,file system,t-SQL

Log Parser Lizard 以可视化界面操作,使用类似SQL的语法查询

 

下载地址:

Log Parser 2.2 :
http://www.microsoft.com/en-us/download/details.aspx?id=24659

Log Parser Lizard GUI :
http://www.lizard-labs.com/log_parser_lizard_buy.aspx

 

或者两个打包好的资源下载:

LogParser2.2+LogParserLizard

 

安装完成后:

 

 

 

LogParser 查询方法:

 

编写SQL脚本保存为: SlowPage.sql

–C:\inetpub\SlowPage.sql
Select Top 20
LogRow as [Line Number],
date as [Date],
time as [Time],
c-ip as [Client-IP],
s-ip as [Server IP],
s-port as [Server Port],
cs-method as [Request Verb],
cs-uri-stem as [Request URI],
sc-bytes as [Bytes sent],
sc-status as [Status],
sc-substatus as [Sub-status],
sc-win32-status as [Win 32 Status],
time-taken as [Time Taken]
From
C:\inetpub\logs\LogFiles\W3SVC2\u_ex*.log
Order by time-taken desc

打开 LogParser 2.2 命令行工具,执行脚本:
LOGPARSER -i:IISW3C file:C:\inetpub\SlowPage.sql -o:DataGrid -q:off

 

 

Log Parser Lizard 查询方法:

打开 Log Parser Lizard 可视化界面,如下图,将sql命令复制到 查询[query] 窗口中,在点击生成[generate]。

 

 

 

 

还可以以各种图形的方式显示出来,更方便、直观 了解分析情况:

 

 

 

在Log Parser 2.2 安装路径下,有很多自带的sql语句:

 

 

若不清楚列名称,可以先全部查询所有列,再选择有用的列进行分析:

 

 

更多的使用方法,可以参考帮助文档,有很多例子:

 

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/kk185800961/article/details/43964209

推荐 | 10个好用的Web日志安全分析工具 - Bypass - 博客园

mikel阅读(227)

来源: 推荐 | 10个好用的Web日志安全分析工具 – Bypass – 博客园

经常听到有朋友问,有没有比较好用的web日志安全分析工具?
首先,我们应该清楚,日志文件不但可以帮助我们溯源,找到入侵者攻击路径,而且在平常的运维中,日志也可以反应出很多的安全攻击行为。

一款简单好用的Web日志分析工具,可以大大提升效率,目前业内日志分析工具比较多,今天推荐十个比较好用的Web日志安全分析工具。

1、360星图
一款非常好用的网站访问日志分析工具,可以有效识别Web漏洞攻击、CC攻击、恶意爬虫扫描、异常访问等行为。一键自动化分析,输出安全分析报告,支持iis/apache/nginx日志,支持自定义格式。
下载地址:https://wangzhan.qianxin.com/activity/xingtu


2、LogForensics
TSRC提供的一款日志分析工具,可从单一可疑线索作为调查起点,遍历所有可疑url(CGI)和来源IP。
相关下载地址:https://security.tencent.com/index.php/opensource/detail/15


3、GoAccess
一款可视化 Web 日志分析工具,通过Web 浏览器或者 nix 系统下的终端程序即可访问。能为系统管理员提供快速且有价值的 HTTP 统计,并以在线可视化服务器的方式呈现。
官网地址:https://www.goaccess.cc/



4、AWStats

一款功能强大的开源日志分析系统,可以图形方式生成高级Web,流媒体,ftp或邮件服务器统计信息。
官网地址:http://www.awstats.org/


5、Logstalgia
一款非常炫酷且可视化日志分析工具,可以直观的展示CC攻击和网站的日志分析,并以可视化的3D效果展示出来。
下载地址:http://www.softpedia.com/get/Internet/Servers/Server-Tools/Logstalgia.shtml



6、FinderWeb
程序员的看日志利器,支持,tail, less, grep,支持超大的文本文件,从几M到几十G的日志文件都流畅自如。
下载使用:http://www.finderweb.net/download.html


7、web-log-parser
一款开源的分析web日志工具,采用python语言开发,具有灵活的日志格式配置。
github项目地址:https://github.com/JeffXue/web-log-parser

8、ELK
开源实时日志分析的ELK平台,由ElasticSearch、Logstash和Kiabana三个开源项目组成,在企业级日志管理平台中十分常见。
下载使用:https://www.elastic.co/cn/elastic-stack

9、Splunk
一款顶级的日志分析软件,如果你经常用 grep、awk、sed、sort、uniq、tail、head 来分析日志,那么你可以很容易地过渡到Splunk。
下载地址:https://www.splunk.com/zh-hans_cn/download/splunk-enterprise.html



10、IBM QRadar
Qradar有一个免费的社区版本,功能上和商用版本差别不大,适合小规模日志和流量分析使用。
下载地址:https://developer.ibm.com/qradar/ce/

vue中子组件的methods中获取到props中的值方法(vue 学习笔记)_vue 获取props的值-CSDN博客

mikel阅读(113)

来源: vue中子组件的methods中获取到props中的值方法(vue 学习笔记)_vue 获取props的值-CSDN博客

说一下为什么会有这个需求:如果不需要处理props的值直接用,是不用这么麻烦的,
直接在页面上用{{name}}就好了。在做项目的时候由于要对props的值做处理后才能使用,当我在methods、mounted 用this.xxx,发现取出来的值是空对象,有点疑惑
于是就上网查了原来:
通过这个方法传过来的值,在子组件mounted函数中如果直接去获取的话有可能会获取不到。因为此时父组件的值还没有传过来,但是如果你是写在methods中的方法中获取值的话,此时值已经传过来了,可以直接使用this.name获取到。 如果你想要在子组件的mounted()中获取传值的话最好做个延迟或者是监听,但是又有一点,为啥我在页面中使用{{name}}没有问题呢! 这就是vue的好处它是等到这个name值有变化的时候就会更新的。所以才会看到显示。(其实先是空然后值传过来了,就会加载上去。)
https://segmentfault.com/q/1010000014590428 (原文链接)
解决方法,直接上代码了:
父组件代码:
<template>
// 将photoList 传给子组件
  <div class=”home”><photo-view :photoList=”photoList”></photo-view></div>
</template>
<script>
import PhotoView from ‘./components/PhotoView’
import axios from ‘axios’
export default {
  name: ‘Home’,
  components: {
    PhotoView
  },
  data () {
    return {
      photoList: []
    }
  },
  mounted () {
    this.getPhotoInfo()
    this.getPushText()
  },
  methods: {
    getPhotoInfo () {
    // 获取后台数据通过getPhotoInfoSucc得到photoList
      axios.get(‘./mock/photo.json’)
        .then(this.getPhotoInfoSucc)
    },
    getPhotoInfoSucc (res) {
      res = res.data
      if (res.data) {
        const data = res.data
        this.photoList = data
        console.log(data)
      }
    }
  }
}
</script>
子组件代码:
<template>
</template>
<script>
export default {
  name: ‘PhotoView’,
  props: {
    photoList: Array
  },
  data () {
    return {
      newPhotoList: [],
    }
  },
  //监听photoList的值,当它由空转变时就会触发,这时候就能取到了,拿到值后要传值到data的newPhotoList中
  watch: {
    photoList: function (newData) {
      this.newPhotoList = newData
      this.getPhotoList(this.newPhotoList)
    }
  },
  methods: {
    getPhotoList (res) {
    console.log(this.newPhotoList)
  }
}
</script>
这里是参考https://www.jianshu.com/p/cdc90de5b1cf
个人学习笔记,如有误,请指教,谢谢
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/baidu_38942289/article/details/115552354

The data property “xxx“ is already declared as a prop. Use prop default value instead.-CSDN博客

mikel阅读(104)

来源: The data property “xxx“ is already declared as a prop. Use prop default value instead.-CSDN博客

在写父组件子组件相互传递数据的时候,出现The data property “num” is already declared as a prop. Use prop default value instead.

这里意思是这个data的属性num已经在prop声明了,使用prop默认的值替代。因为num我在父组件已经创建声明后面又在子组件的data声明和初始化,造成冲突。

 

所以报这个错,然后需要把子组件的那个data函数里的num删除或者换个不重复的名字,便可以解决报错。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/caseywei/article/details/108062924

uni-app父子组件间的方法调用及传值_uniapp调用组件内部的方法-CSDN博客

mikel阅读(106)

来源: uni-app父子组件间的方法调用及传值_uniapp调用组件内部的方法-CSDN博客

uni-app父子组件间的方法调用及传值

方法调用:

一、父组件调用子组件里的方法

1. 先准备一个子组件

<template>

<view></view>

</template>

 

<script>

export default {

data(){

return {}

},

methods:{

childMethod() {  // 子组件中有一个childMethod方法

console.log(‘childMethod do…’)

}

}

}

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

2. 准备一个父组件

<template>

<view class=”content”>

<mian-index ref=”mainindex”></mian-index> //使用子组件,ref给子组件一个id标识

</view>

</template>

<script>

import mainindex from ‘@/pages/main/index/index.vue’  //引入子组件

export default {

data() {

return {

 

};

},

components:{

‘mian-index’:mainindex

},

onLoad(e) {

this.$refs.mainindex.childMethod();  //页面加载时就调用子组件里的childMethod方法

}

}

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

参考地址:https://www.cnblogs.com/cap-rq/p/11026881.html

 

二、子组件调用父组件里的方法

2.1. 准备一个父组件

<template>

<view class=”father”>

<child v-on:ToChange=”change”></child>  // 在使用子组件时,通过v-on绑定在父组件内定义好的方法

</view>

</template>

 

<script>

import child from ‘../../components/child’  //引入子组件

export default {

methods: {

change(){  // 在父组件内定义好的方法

console.log(“触发了父页面内的方法”);

},

},

components:{

child

}

}

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

2.2. 在准备一个子组件

<template>

<view @tap=”changeC()”>  // 在子组件内绑定触发子组件内定义的方法

子组件

</view>

</template>

 

<script>

export default {

methods:{

changeC(type){

this.$emit(“Tochange”);  // 通过this.$emit触发父组件内绑定的方法

console.log(“触发了子组件内的方法”);

}

}

}

</script>

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

参考地址:https://blog.csdn.net/Sport_18/article/details/102833209

 

传值:

 

三、父组件给子组件传值

3.1. 父组件:

<template>

<view>

<test :msg=”msg”></test>  // :msg=”msg”绑定传值

</view>

</template>

 

<script>

import test from “@/components/test/test.vue”  // 引入子组件

export default {

data () {

return {

msg: ‘hello’

}

},

components: {test}

}

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

3.2. 子组件:

<template>

<view>

子组件 {{msg}}  //接收后使用

</view>

</template>

 

<script>

export default {

props: [‘msg’]  //通过props接收数据

}

</script>

 

<style>

</style>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

四、子组件给父组件传值

4.1. 父组件

<template>

<view>

<test @myEvent=”getMsg”></test>

</view>

</template>

<script>

import test from “@/components/test/test.vue”   // 引入子组件

export default {

methods: {

getMsg (res) {

console.log(res)

}

},

components: {test}

}

</script>

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

4.2.子组件

<template>

<view>

<button type=”primary” @click=”sendMsg”>给父组件传值</button>

</view>

</template>

 

<script>

export default {

data () {

return {

status: ‘hello uni-app’

}

},

methods: {

sendMsg () {

this.$emit(‘myEvent’,this.status)  // 通过$emit触发事件,第二个参数就是传递的参数

}

}

}

</script>

————————————————

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

 

原文链接:https://blog.csdn.net/weixin_40816738/article/details/124774664

uni-app 页面顶部栏固定悬浮的 css 兼容方法 - DCloud问答

mikel阅读(113)

来源: uni-app 页面顶部栏固定悬浮的 css 兼容方法 – DCloud问答

uni-app 中 NavigationBar(导航栏 44px)以及 TabBar(底部选项卡 50px)组件的高度是固定的,不可修改,各小程序平台,包括同小程序平台的 iOS 和 Android 的高度也不一样。

泪雪博客

那么如果我们需要悬浮导航菜单则需要使用 position: sticky 来使其悬浮,然后对于 H5 平台加上 top:44px 的高度,这样 uni-app 页面顶部导航栏的固定悬浮就实现了兼容,具体的代码如下:

复制代码.menu {  
    position: sticky;  
    /* #ifdef H5 */  
    top: 44px;  
    /* #endif */  
    /* #ifndef H5 */  
    top: 0;  
    /* #endif */  
    z-index: 999;  
    flex: 1;  
    flex-direction: column;  
    overflow: hidden;  
    background-color: #ffffff;  
}

以上代码纯属子凡开发中简单的需要稍微注意的地方,仅供大家学习参考。

其中 position: fixed 虽然也可以悬浮固定,但是底部的元素就无法继承 top 的值了,可以简单说 fixed 是特殊版的 absolute,fixed 元素总是相对于 body 定位的。所以就需要使用 position: static,static(没有定位)是 position 的默认值,元素处于正常的文档流中,会忽略 left、top、right、bottom 和 z-index 属性。

除非注明,否则均为泪雪博客原创文章,禁止任何形式转载

本文链接:https://zhangzifan.com/uni-app-navigationbar-position-static.html

uniapp 添加顶部搜索导航_typeerror: uni.onnavigationbarsearchinputclicked i-CSDN博客

mikel阅读(106)

来源: uniapp 添加顶部搜索导航_typeerror: uni.onnavigationbarsearchinputclicked i-CSDN博客

(记得提前引入字体样式)
1.在 pages.json里面配置搜索界面(这里member为主页):
{
            “path” : “pages/member/member”,
            “style” : {
“app-plus”:{
“scrollIndicator”:”none”,//隐藏滚动条
“titleNView”:{
//搜索框配置
“searchInput”:{
“align”:”center” ,//居中
“backgroundColor”:”#f7f7f7″,
“borderRadius”:”50rpx”,
“placeholder”:”请搜索文字”,
“placeholderColor”:”red”,   //默认字体颜色
“disabled”:true    //不希望输入文本
},
//左右2个按钮
“buttons”:[
//左边图标
{
“color”:”#FF9619″,
“colorPressed”:”#BBBBBB”,
“float”:”left”,
“fontSize”:”40px”   ,//文字大小
“fontSrc”:”/static/font/icon.ttf”,
“text”:”\ue609″
},
//右边图标
{
“color”:”#000000″,
“float”:”right”,
“fontSrc”:”/static/font/icon.ttf”,
“text”:”\ue653″
}
]
}
}
}
        },
2.在主页member.vue    加入监听搜索框点击事件,当点击搜索导航就会跳转到搜索页
//监听搜索框点击事件
onNavigationBarSearchInputClicked(){
// console.log(“打开搜索页”)
//跳转到搜索页
uni.navigateTo({
url:”../search/search”
})
},
遇到问题:如果没有加入参数”disabled”:true    则不能跳转
3.搜索页布局配置(在 pages.json里面)
{
            “path” : “pages/search/search”,
            “style” : {
“app-plus”:{
“scrollIndicator”:”none”,//隐藏滚动条
“titleNView”:{
“autoBackButton”: false, //去掉返回按钮,未生效bug
//搜索框配置
“searchInput”:{
“align”:”left” ,
“backgroundColor”:”#f7f7f7″,
“borderRadius”:”50rpx”,
“placeholder”:”请搜索文字”,
“placeholderColor”:”red”,   //默认字体颜色
“disabled”:false
},
“buttons”:[
//取消按钮
{
“color”:”#000000″,
“float”:”right”,
“fontSize”:”40rpx”,
“text”:”取消”
}
]
}
}
}
        }
4.点击取消按钮,返回到上一层(在search.vue中配置)
<script>
export default{
data(){
return{
};
},
//监听原生标题导航点击事件
onNavigationBarButtonTap(e){
if(e.index==0){
uni.navigateBack({
delta:1
})
}
},
}
</script>
5.2个监听事件接口  search.vue中修改(//监听搜索框文本变换,监听点击搜索按钮事件)
<script>
export default{
data(){
return{
};
},
//监听原生标题导航点击事件
onNavigationBarButtonTap(e){
if(e.index==0){
uni.navigateBack({
delta:1
})
}
},
//监听搜索框文本变换
onNavigationBarSearchInputChanged(e) {
console.log(“监听文本搜索变化”+JSON.stringify(e.text))
},
//监听点击搜索按钮事件
onNavigationBarSearchInputConfirmed(e) {
console.log(“监听点击搜索事件”+JSON.stringify(e.text))
}
}
</script>
————————————————
                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_45200726/article/details/108616708