Unity功能模块一对话系统(4)实现个性文本标签

本期我们将了解如何在TMPro中自定义我们的标签样式,并实现两种有趣的效果。

一.需求描述

1.定义<float>格式的标签,实现标签处延迟打印功能

2.定义<r=" "></r>格式的标签,实现标签区间内文本片段的注释显示功能

3.补充文本多种打印样式功能。

二.自定义标签样式

首先我们需要借助预处理器来读取到我们的目标格式的标签,并记录标签内的相关信息

基本思路:通过ITextPreprocessor接口实现预处理器的预加工函数PreprocessText(),读取到文本中的自定义标签格式并记录标签内信息。

1.实现接口方法

 public class AdvancedTextPreprocessor : ITextPreprocessor
 {
     public string PreprocessText(string text){}
 }

2.提取标签

(1)使用正则表达式来匹配<>的类html格式。

  public string PreprocessText(string text)
  {
    string processingText = text;
    string pattern = "<.*?>";//定义一个正则表达式 "<.*>" 用于匹配所有以 < 开头、> 结尾的字符串。贪婪模式,即它会匹配最远的 >
    Match match = Regex.Match(processingText, pattern);//首次匹配,查找所有富文本标签

    while (match.Success) //尝试匹配富文本标签
    {
        match = Regex.Match(processingText, pattern);//继续匹配,查找下一个标签
    }

    processingText = text;
    return processingText;
}

    //正则表达式概念
    // . 代表任意字符
    //*代表前一个字符出现0次或多次
    //+代表前一个字符出现1次或多次
    //?代表前一个字符出现0次或1次

这里我们定义了<.*?>这种文本模式,下方代码将在用户文本中查找符合模式的文本片段match。

 Match match = Regex.Match(processingText, pattern);//按照pattern模式来匹配用户文本中符合格式的文本(含<>)

3.记录标签内信息

下面我们需要记录下我们的自定义标签内的信息。

1.定义存储结构

/// <summary>
/// 打印延迟字典【读取标签<float>,遇到将延迟float秒后打印后面内容】
/// </summary>
public Dictionary<int, float> IntervalDictionary = new Dictionary<int, float>();//自定义打印标签字典

/// <summary>
/// 注释标签列表
/// </summary>
public List<RubyData> rubyList = new List<RubyData>();

这里我使用字典存储延迟标签信息,将延迟标签的位置偏移作为键,延迟长度作为值。

使用列表来存储注释数据结构。成员是Rubydata类的实例。

Rubydata类中定义了3种属性:注释自身在文本中的起止位置偏移startIndex和终止位置偏移endIndex,以及注释内容contnet。

RubyData类如下

 /// <summary>
 /// 注释数据
 /// </summary>
 public class RubyData
 {
     public RubyData(int _startIndex, string _content)
     {
         startIndex = _startIndex;
         rubyContent = _content;
     }

     public int startIndex { get; }
     public string rubyContent { get; }
     public int endIndex { get; set; }

 }

2.匹配<>标签内的文本信息

下方代码功能是去除match文本中包含的<>字符,得到标签内的信息label。

 string label = match.Value.Substring(1, match.Length - 2);

3.标签内信息的存储逻辑

(1)读取<float>格式标签内容
  //自定义延迟打印标签【规定<float>】并记录在打印间隔标签字典中
  if (float.TryParse(label, out float result))
      IntervalDictionary[match.Index - 1] = result;

此处由于标签内部就是一个float类型的数据,所以我们可以直接就爱那个标签记录在我们的打印间隔标签字典中

(2)读取<r=" "></r>格式标签内容
  //自定义注释标签【规定<r="">】录入注释列表
  //注释标签开启
   if (Regex.IsMatch(label, "^r=.+")) //^:表示字符串的开头。这个符号确保匹配的字符串必须从开头开始
      rubyList.Add(new RubyData(match.Index, label.Substring(2)));

  //注释标签终止
  else if (Regex.IsMatch(label, "/r"))
  {
      if (rubyList.Count > 0)
          rubyList[rubyList.Count - 1].endIndex = match.Index - 1;
  }

4.去除标签文本

由于我们在用户文本中含有<>的类html标签,但是最终是不需要显示的,所以我们还需要在用户文本的基础上去除我们的标签(替换为空字符串),才能得到最终的渲染文本。

 pattern = @"(<(\d+)(\.\d+)?>)|(</r>)|(<r=.*?>)";//使用 @ 前缀可以避免在字符串中使用转义字符
                                                    
 //将处理文本中符合pattern规则的字符串 替换为空字符串""
 processingText = Regex.Replace(processingText, pattern, "");

完整源码见后文。


三.实现自定义文本效果

1.延迟打印功能

现在我们已经可以存储文本中的自定义标签内的信息,我们现在来实现一下文本的自定义效果。

我们前面已经实现了打字机的效果,实际上的延时打印功能,就是在打字函数的携程循环中等待的迭代函数。

       if (selfPreprocessor.IntervalDictionary.TryGetValue(typingIndex, out float result))
           yield return new WaitForSecondsRealtime(result);
       else
           yield return new WaitForSecondsRealtime(defalutInterval);

上句代码用来判断文本中当前打印位置处会否是延迟标签所在位置。如果恰好是字典中延迟标签的位置,则返回字典中的的当前位置键对应的float值作为输入执行携程延迟函数。否则按照默认间隔执行打印延迟。

2.注释功能

我们具体会用到两个方法。

(1)生成并设置注释

void SetRubyText(RubyData data)
{
    GameObject ruby = Instantiate(Resources.Load<GameObject>("RubyText"), transform);
    ruby.GetComponent<TextMeshProUGUI>().SetText(data.rubyContent);
    ruby.GetComponent<TextMeshProUGUI>().color = textInfo.characterInfo[data.startIndex].color;
    ruby.transform.localPosition = (textInfo.characterInfo[data.startIndex].topLeft + textInfo.characterInfo[data.endIndex].topRight) / 2 - new Vector3(0, 10, 0);
}

这里我们传入一个Rubydata实例,设置预制件文本,颜色及位置。

(2)判断当前位置是否是注释的起始位

    /// <summary>
    /// 读取一个位置是否就是Ruby起始位
    /// </summary>
    /// <param name="index"></param>
    /// <param name="data"></param>
    /// <returns></returns>
    public bool TryGetRubyText(int index, out RubyData data)
    {
        data = new RubyData(0, "");
        foreach (RubyData item in rubyList)
        {

            if (item.startIndex == index)
            {
                data = item;
                return true;
            }
        }
        return false;
    }
}

每打印到一个位置,我们都需要判断当前位置是否是注释标签的起始位。如果是,将调用生成注释预制件的函数。


四.改进文本显示逻辑

现在我们去AdvancedText类中改进一下前面实现的文本显示逻辑。

增添字段
    Coroutine typingCor;//存储打字携程,易于中断

    Action OnFinish; //文本打印结束的回调委托

这里的携程字段使用来记录我们的打字机携程的,实际情况中我们会遇到 打断正在打印中文本 的需求。存储到一个携程变量中将易于管理。

增添枚举

我们使用一个枚举来决定文本以什么形式显示。(见下方)

public enum E_DisplayType 
{
    Defalut,Fading,Typing
}

Defalut模式:文本采用 整体直接显示。

Fading模式:文本采用 整体淡入显示。

Typing模式:文本采用 打字机淡入效果显示。

我们现在实现一下各种打印样式的函数。

Defalut模式显示方法
  /// <summary>
  /// 文本整体直接显示
  /// </summary>
  void DefalutDisplay(Action action = null)
  {
      for (int i = 0; i < m_characterCount; i++)
          SetSingleCharacterAlpha(i, 255);
      action?.Invoke();
  }
Fading模式显示方法
 /// <summary>
 /// 文本整体淡入显示
 /// </summary>
 void FadingDisplay(float fadeDuration,Action action=null)
 {
     for (int i = 0; i < m_characterCount; i++)
         StartCoroutine(FadeInCharacter(i, fadeDuration));
     action?.Invoke();
 }
Typing模式显示方法
 /// <summary>
 /// 文本打字机显示
 /// </summary>
 /// <returns></returns>
 IEnumerator TypingDisplay(float fadeDuration,Action action = null)
 {
     ForceMeshUpdate();
     for (int i = 0; i < m_characterCount; i++)
     {
         SetSingleCharacterAlpha(i, 0);
     }
     typingIndex = 0;
     while (typingIndex < m_characterCount)
     {

         //SetSingleCharacterAlpha(typingIndex,255);   //无淡入打字机效果
         StartCoroutine(FadeInCharacter(typingIndex, fadeDuration)); //淡入打字机效果

         if (selfPreprocessor.IntervalDictionary.TryGetValue(typingIndex, out float result))
             yield return new WaitForSecondsRealtime(result);
         else
             yield return new WaitForSecondsRealtime(defalutInterval);

         typingIndex++;
     }
     action?.Invoke();
 }
文本显示方法

我们还需要一个函数作为暴露给外部的应用接口,客户端可以选择不同的文本显示样式。

  public IEnumerator ShowText(string content, E_DisplayType displayType, float duration)
  {
      if (typingCor != null)
      {
          StopCoroutine(typingCor);
      }
      typingCor = null;
      SetText(content);
      yield return null;
      TextDisAppear();
      switch (displayType)
      {
          case E_DisplayType.Defalut:
              DefalutDisplay();
              SetAllRubyTexts();
              break;
          case E_DisplayType.Fading:
              FadingDisplay(duration);
              SetAllRubyTexts();
              break;
          case E_DisplayType.Typing:
              typingCor = StartCoroutine(TypingDisplay(duration));
              break;
          default:
              break;
      }
  }

五.功能测试

在测试脚本中我们调用AdvancedText实例的携程来显示文本。

 StartCoroutine(advancedText.ShowText(content,displayType,duration));

编辑器内选择打印样式。

运行效果

这里我是做了一个按键输入功能,通过按键显示并开始打印文本,但调用文本打印的逻辑是一致的。如果大家对UI动效有有兴趣的话我后面可以出一期关于UI的代码框架及动效功能的解决方案。


六.完整源码

AdvancedTextPreprocessor类  

using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;

namespace DialogueDemo
{
    public class AdvancedTextPreprocessor : ITextPreprocessor
    {
        /// <summary>
        /// 打印延迟字典【读取标签<float>,遇到将延迟float秒后打印后面内容】
        /// </summary>
        public Dictionary<int, float> IntervalDictionary = new Dictionary<int, float>();//自定义打印标签字典

        public List<RubyData> rubyList = new List<RubyData>();
        public string PreprocessText(string text)
        {
            IntervalDictionary.Clear();
            string processingText = text;
            string pattern = "<.*?>";//定义一个正则表达式 "<.*>" 用于匹配所有以 < 开头、> 结尾的字符串。贪婪模式,即它会匹配最远的 >
            Match match = Regex.Match(processingText, pattern);//首次匹配,查找所有富文本标签

            while (match.Success) //尝试匹配富文本标签
            {
                string label = match.Value.Substring(1, match.Length - 2);
                //自定义延迟打印标签【规定<float>】并记录在打印间隔标签字典中
                if (float.TryParse(label, out float result))
                    IntervalDictionary[match.Index - 1] = result;
                //自定义注释标签【规定<r="">】录入注释列表
                //注释标签开启
                else if (Regex.IsMatch(label, "^r=.+")) //^:表示字符串的开头。这个符号确保匹配的字符串必须从开头开始
                    rubyList.Add(new RubyData(match.Index, label.Substring(2)));

                //注释标签终止
                else if (Regex.IsMatch(label, "/r"))
                {
                    if (rubyList.Count > 0)
                        rubyList[rubyList.Count - 1].endIndex = match.Index - 1;
                }

                processingText = processingText.Remove(match.Index, match.Length);//读取此打印间隔标签后,删除此标签
                if (Regex.IsMatch(label, "^sprite.+"))  //如果标签格式是精灵,需要一个占位符
                    processingText.Insert(match.Index, "*");

                match = Regex.Match(processingText, pattern);//继续匹配,查找下一个标签
            }

            processingText = text;
            //正则表达式概念
            // . 代表任意字符
            //*代表前一个字符出现0次或多次
            //+代表前一个字符出现1次或多次
            //?代表前一个字符出现0次或1次

            pattern = @"(<(\d+)(\.\d+)?>)|(</r>)|(<r=.*?>)";//使用 @ 前缀可以避免在字符串中使用转义字符
                                                            //简单解释:本句代码实现了读取<>中的整数或小数的功能
            /* (\d +):
            \d + 是一个数字匹配模式,它匹配一个或多个数字字符。+表示前面的模式(数字)可以匹配一个或多个字符。
            () 是捕获组的标记,这样 \d + 匹配到的数字部分就会被捕获到组中,可以在后续处理中使用。

            (\.\d +)?:
            \. 匹配一个字面上的点(.)。点号是一个元字符,在正则中表示任意字符,但在这里需要加 \ 进行转义,表示字面上的点号。
            \d + 匹配一个或多个数字,表示小数点后面的部分。
            () ? 表示这个捕获组是可选的,即小数部分不是必需的。如果没有小数部分,这一部分会被忽略。*/

            //将处理文本中符合pattern规则的字符串 替换为 后面的字符串
            processingText = Regex.Replace(processingText, pattern, "");

            return processingText;
        }

        /// <summary>
        /// 读取一个位置是否就是Ruby起始位
        /// </summary>
        /// <param name="index"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool TryGetRubyText(int index, out RubyData data)
        {

            data = new RubyData(0, "");
            foreach (RubyData item in rubyList)
            {

                if (item.startIndex == index)
                {
                    data = item;
                    return true;
                }
            }
            return false;
        }
    }

    /// <summary>
    /// 注释数据
    /// </summary>
    public class RubyData
    {
        public RubyData(int _startIndex, string _content)
        {
            startIndex = _startIndex;
            rubyContent = _content;
        }

        public int startIndex { get; }
        public string rubyContent { get; }
        public int endIndex { get; set; }

    }
}

AdvancedText类

using System;
using System.Collections;
using TMPro;
using UnityEngine;

namespace DialogueDemo
{
    public class AdvancedText : TextMeshProUGUI
    {
        int typingIndex;
        float defalutInterval = 0.08f;
        Coroutine typingCor;//存储打字携程,易于中断

        Action OnFinish;
        AdvancedTextPreprocessor selfPreprocessor => (AdvancedTextPreprocessor)textPreprocessor;

        private void Init()
        {
            SetText("");
            ClearRubyText();
        }

        public AdvancedText()
        {
            textPreprocessor = new AdvancedTextPreprocessor();
        }
        public void TextDisAppear()
        {
            for (int i = 0; i < m_characterCount; i++)
                SetSingleCharacterAlpha(i, 0);
        }

        public IEnumerator ShowText(string content, E_DisplayType displayType, float duration)
        {
            if (typingCor != null)
            {
                StopCoroutine(typingCor);
            }
            typingCor = null;
            SetText(content);
            yield return null;
            TextDisAppear();
            switch (displayType)
            {
                case E_DisplayType.Defalut:
                    DefalutDisplay();
                    SetAllRubyTexts();
                    break;
                case E_DisplayType.Fading:
                    FadingDisplay(duration);
                    SetAllRubyTexts();
                    break;
                case E_DisplayType.Typing:
                    typingCor = StartCoroutine(TypingDisplay(duration));
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 直接显示
        /// </summary>
        void DefalutDisplay(Action action = null)
        {
            for (int i = 0; i < m_characterCount; i++)
                SetSingleCharacterAlpha(i, 255);
            action?.Invoke();
        }

        /// <summary>
        /// 整体淡入
        /// </summary>
        void FadingDisplay(float fadeDuration,Action action=null)
        {
            for (int i = 0; i < m_characterCount; i++)
                StartCoroutine(FadeInCharacter(i, fadeDuration));
            action?.Invoke();
        }

        /// <summary>
        /// 打字机显示
        /// </summary>
        /// <returns></returns>
        IEnumerator TypingDisplay(float fadeDuration,Action action = null)
        {
            ForceMeshUpdate();
            for (int i = 0; i < m_characterCount; i++)
            {
                SetSingleCharacterAlpha(i, 0);
            }
            typingIndex = 0;
            while (typingIndex < m_characterCount)
            {

                //SetSingleCharacterAlpha(typingIndex,255);   //无淡入打字机效果
                StartCoroutine(FadeInCharacter(typingIndex, fadeDuration)); //淡入打字机效果

                if (selfPreprocessor.IntervalDictionary.TryGetValue(typingIndex, out float result))
                    yield return new WaitForSecondsRealtime(result);
                else
                    yield return new WaitForSecondsRealtime(defalutInterval);

                typingIndex++;
            }
            action?.Invoke();
        }

        /// <summary>
        /// 设置单字符的透明度(每个字符都是由网格(含4个顶点)渲染)
        /// </summary>
        /// <param name="index"></param>
        /// <param name="newAlpha">newalpha范围为0~255</param>
        void SetSingleCharacterAlpha(int index, byte newAlpha)
        {
            TMP_CharacterInfo character = textInfo.characterInfo[index];//获取文本内容索引下的单个字符
            if (!character.isVisible)
                return;
            int matIndex = character.materialReferenceIndex;//获取字符材质索引
            int vertexIndex = character.vertexIndex;//获取字符顶点索引
            for (int i = 0; i < 4; i++)
            {
                textInfo.meshInfo[matIndex].colors32[vertexIndex + i].a = newAlpha;
            }
            UpdateVertexData();//更新顶点数据
        }

        /// <summary>
        /// 单字符淡入
        /// </summary>
        /// <param name="index"></param>
        /// <param name="duration"></param>
        /// <returns></returns>
        IEnumerator FadeInCharacter(int index, float duration)
        {
            //如果找到Ruby起始位,设置Ruby预制件
            Debug.Log(selfPreprocessor.TryGetRubyText(index, out var data1));

            if (selfPreprocessor.TryGetRubyText(index, out RubyData data))
                SetRubyText(data);

            if (duration <= 0)
                SetSingleCharacterAlpha(index, 255);
            else
            {
                float timer = 0;
                while (timer < duration)
                {
                    timer = Mathf.Min(duration, timer + Time.unscaledDeltaTime);
                    SetSingleCharacterAlpha(index, (byte)(255 * (timer / duration)));
                    yield return null;
                }
            }
        }

        void SetRubyText(RubyData data)
        {
            GameObject ruby = Instantiate(Resources.Load<GameObject>("RubyText"), transform);
            ruby.GetComponent<TextMeshProUGUI>().SetText(data.rubyContent);
            ruby.GetComponent<TextMeshProUGUI>().color = textInfo.characterInfo[data.startIndex].color;
            ruby.transform.localPosition = (textInfo.characterInfo[data.startIndex].topLeft + textInfo.characterInfo[data.endIndex].topRight) / 2 - new Vector3(0, 10, 0);
        }

        /// <summary>
        /// 清除当前的所有注释
        /// </summary>
        void ClearRubyText()
        {
            foreach (var item in GetComponentsInChildren<TextMeshProUGUI>())
            {
                if (item != this)
                    Destroy(item.gameObject);
            }
        }

        /// <summary>
        /// 用于跳过对话直接显示所有注释
        /// </summary>
        void SetAllRubyTexts()
        {
            foreach (var item in selfPreprocessor.rubyList)
            {
                SetRubyText(item);
            }
        }
    }
}

七.尾声

       在 Unity 的 TextMeshPro中,文本标签(如 <color=red>...</color>)的处理方式不同于我们在自定义 AdvancedTextPreprocessor 类中的实现。TextMeshPro 之所以能正确处理 <color=red> </color> 等标签,原因在于它内部已经预先实现了一个功能强大的解析器,用于识别和处理各种富文本标签。TMP处理这些标签的方法是通过其 TextParser 类内部的规则和机制。具体的实现位于 TextMeshPro 的源码中,这些规则和方法是在 TextMeshPro 的组件中预定义的。当你将含有富文本标签的字符串传递给 TMP_Text 组件时,TextMeshPro 会自动解析这些标签并应用对应的样式。这与 AdvancedTextPreprocessor 类不同,后者是我们自己实现的自定义处理器,它仅仅处理我们通过正则表达式解析并手动处理的标签(例如延迟打印标签、注释标签等),并不会干预 TextMeshPro 内部已经定义的标准标签处理。

本篇完

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习实战自动驾驶目标识别

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对BDD100K自动驾驶目标数据集进行训练和优化&#xff0c;该数据集包含丰富的自动驾…

广西大数据局:数聚政府、利企惠民(广西数字政府建设内容、管理机制、应用场景)

2023年数字政府评估大会上&#xff0c;广西大数据局党委书记、主任周飞发表了题为“数聚政府、利企惠民”的主旨演讲。主要介绍了广西壮族自治区“数字政府的建设内容、数字政府的管理机制以及数字政府有哪些应用场景来实现惠企利民”。 篇幅限制&#xff0c;部分内容如下&…

AI 助力游戏开发中的常用算法实现

在当今的游戏开发领域&#xff0c;人工智能&#xff08;AI&#xff09;技术的应用已经成为推动行业发展的关键力量。AI不仅能够提升游戏的智能化水平&#xff0c;还能够增强玩家的沉浸感和游戏体验。随着技术的进步&#xff0c;AI在游戏设计、开发和测试中的应用越来越广泛&…

行业商机信息付费小程序系统开发方案

行业商机信息付费小程序系统&#xff0c;主要是整合优质行业资源&#xff0c;实时更新的商机信息。在当今信息爆炸的时代&#xff0c;精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览&#xff1a;用户在工作间隙或闲暇时间&#xff0c…

LabVIEW 中 NI Vision 模块的IMAQ Create VI

IMAQ Create VI 是 LabVIEW 中 NI Vision 模块&#xff08;NI Vision Development Module&#xff09;的一个常用 VI&#xff0c;用于创建一个图像变量。该图像变量可以存储和操作图像数据&#xff0c;是图像处理任务的基础。 ​ 通过以上操作&#xff0c;IMAQ Create VI 是构建…

[AI] 深度学习的“黑箱”探索:从解释性到透明性

目录 1. 深度学习的“黑箱”问题&#xff1a;何为不可解释&#xff1f; 1.1 为什么“黑箱”问题存在&#xff1f; 2. 可解释性研究的现状 2.1 模型解释的方法 2.1.1 后置可解释性方法&#xff08;Post-hoc Explanations&#xff09; 2.1.2 内在可解释性方法&#xff08;I…

UnityRenderStreaming使用记录(四)

测试把UnityRenderStreaming部署在docker&#xff0c;剧透一下&#xff0c;嘎了…… 当然webserver运行的妥妥的 那么打包出的程序运行log Mono path[0] /home/unity/Broadcast/Broadcast_Data/Managed Mono config path /home/unity/Broadcast/Broadcast_Data/MonoBleedingE…

javaEE-文件操作和IO-文件

目录 一.什么是文件 1.文件就是硬盘(磁盘)上的文件。 2.计算机中存储数据的设备&#xff1a; 3.硬盘的物理特征 4.树型结构组织和⽬录 5.文件路径 文件路径有两种表示方式&#xff1a; 6.文件的分类 二、java中文件系统的操作 1.File类中的属性&#xff1a; 2.构造方…

SqlSession的线程安全问题源码分析

&#x1f3ae; 作者主页&#xff1a;点击 &#x1f381; 完整专栏和代码&#xff1a;点击 &#x1f3e1; 博客主页&#xff1a;点击 文章目录 SqlSession 是线程安全的吗&#xff1f;为什么说是线程不安全的&#xff1f;事务管理问题 数据库连接的共享问题 一级缓存线程安全问题…

拆解 Web3:探寻去中心化网络的核心密码

近年来&#xff0c;Web3频繁出现在技术讨论中&#xff0c;被视为互联网发展的下一阶段。那么&#xff0c;Web3究竟是什么&#xff1f;它如何区别于传统互联网&#xff0c;又将为未来的网络带来哪些新的可能&#xff1f;本文将从科普的角度拆解Web3的核心密码&#xff0c;揭开它…

《Vue3实战教程》37:Vue3生产部署

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》 生产部署​ 开发环境 vs. 生产环境​ 在开发过程中&#xff0c;Vue 提供了许多功能来提升开发体验&#xff1a; 对常见错误和隐患的警告对组件 props / 自定义事件的校验响应性调试钩子开发工具集成 然而&#xff…

Ruby自动化:用Watir库获取YouTube视频链接

引言 Watir&#xff08;Web Application Testing in Ruby&#xff09;是一个强大的工具&#xff0c;它允许开发者使用Ruby语言来自动化控制浏览器。Watir最初被设计用于自动化Web应用测试&#xff0c;但其功能远不止于此。通过Watir&#xff0c;我们可以模拟用户行为&#xff…

[CTF/网络安全] 攻防世界 warmup 解题详析

查看页面源代码&#xff0c;发现source.php 得到一串代码&#xff0c;进行代码审计&#xff1a; <?phpclass emmm{public static function checkFile(&$page){$whitelist ["source">"source.php","hint">"hint.php"];…

solr9.7 单机安装教程

1.环境要求:jdk11以上 2.下载wget https://dlcdn.apache.org/solr/solr/9.7.0/solr-9.7.0.tgz 3.解压 4.修改solr.in.sh配置 5.启动命令 bin/solr start 6.创建core bin/solr create -c <core名称> 注意:用solr ui界面创建&#xff0c;会提示找不到solrconfig.xml和m…

应用架构模式-总体思路

采用引导式设计方法&#xff1a;以企业级架构为指导&#xff0c;形成较为齐全的规范指引。在实践中总结重要设计形成决策要点&#xff0c;一个决策要点对应一个设计模式。自底向上总结采用该设计模式的必备条件&#xff0c;将之转化通过简单需求分析就能得到的业务特点&#xf…

基于AI大模型的医院SOP优化:架构、实践与展望

一、引言 1.1 研究背景与意义 近年来,人工智能(AI)技术取得了迅猛发展,尤其是大模型的出现,为各个领域带来了革命性的变化。在医疗领域,AI 医疗大模型正逐渐崭露头角,展现出巨大的应用潜力。随着医疗数据的海量积累以及计算能力的大幅提升,AI 医疗大模型能够对复杂的…

AWS re:Invent 2024 - Dr. Werner Vogels 主题演讲

今年&#xff0c;我有幸亲临现场参加了所有的 keynote&#xff0c;每一场都让我感受到深深的震撼。无论是全新的功能发布&#xff0c;还是令人眼前一亮的新特性展示&#xff0c;每一场 keynote 都精彩纷呈&#xff0c;充满干货&#xff0c;值得反复学习和回味。 恰好&#xff…

UnionTech OS Server 20 网页无法访问yum源地址

统信yum地址 https://euler-packages.chinauos.com/server-euler/fuyu/1060/everything/sw_64/Packages/ 浏览器访问401报错无权限&#xff0c;查看linux uos环境下yum配置的用户名和密码 cat /etc/yum/vars/auth_* 然后自己组装生成Basic Authorization def generate_basic_…

自动化测试常考的面试题+答案汇总(持续更新)

Hi&#xff0c;大家好&#xff0c;。最近很多朋友都在说今年的互联网行情不好&#xff0c;面试很难&#xff0c;不知道怎么复习&#xff0c;我最近总结了一份在自动化测试面试中比较常见的面试题合集&#xff0c;希望对大家有帮助。 本文共 4800 字&#xff0c;预计阅读时间 1…

jvm结构介绍

JVM结构概述 Java虚拟机&#xff08;JVM&#xff09;是Java程序的运行环境&#xff0c;它负责将Java字节码转换为机器码并执行。JVM的结构主要包括类加载子系统、运行时数据区、执行引擎、本地接口以及垃圾收集器。 1. 类加载子系统&#xff08;Class Loader Subsystem&#xf…