博客文章 的子部分

4.自定义页面以康奈尔笔记格式输出

Cues

标签/提示 :静态网页,康奈尔笔记,hugo,自定义页面输出格式。

Notes

创建css文件

康奈尔笔记的布局,其中左侧是 “Cues”,右侧是 “Notes”,底部是 “Summary”,您可以使用 Flexbox 的 row 和 column 布局来实现。这里是一个 CSS 示例,它将创建一个两列的布局,左侧为 “Cues”,右侧为 “Notes”,然后在这两列下方是 “Summary”。

首先,创建一个名为 cornell-notes.css 的 CSS 文件,并添加以下样式代码:

.cornell-notes {
  
  border: 1px solid #ccc;
  padding: 20px;
  margin: 20px;
  display: flex;
  flex-wrap: wrap;
}

.cues {
  background-image: url('icon2.svg');
  border-right: 1px solid #080808;
  flex: 1;
  padding-right: 20px;
}

.notes {
  background-image: url('icon1.svg');
  border-left: 1px solid #080808;
  flex: 2;
  padding-left: 20px;
}

.summary {
  width: 100%;
  clear: both; /* 清除浮动,确保摘要部分在下方 */
  padding-top: 20px;
  background-image:url('wwwatercolor.jpg');
  margin-top: 20px;
}

在这个样式中,.cornell-notes 是整个康奈尔笔记的容器,我们使用 display: flex; 和 flex-wrap: wrap; 来创建一个 Flex 容器,允许子元素根据需要换行。.cues 和 .notes 分别设置为容器的子项,并通过 flex 属性来分配空间。.summary 部分使用 width: 100%; 来确保它在 .cues 和 .notes 下方显示,并使用 clear: both; 来清除之前的浮动。

此处分享一个免费的svg网站,bbburst: bbburst bbburst 在这个网站或许你可以找到适合你的背景图,用以装饰自己的康奈尔容器,此处的svg是放在css文件夹下的,所以是直接引用,如果你放在别的的放那么还请使用正确的的路径。

在hugo配置中设置

接下来,确保将 cornell-notes.css 文件放置在 Hugo 站点的 static/css 目录下。然后,在 Hugo 的配置文件 config.toml 中添加对 CSS 文件的引用:

# config.toml
[Params]
  # 其他参数...

[markup]
  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true  # 允许在 Markdown 中使用 HTML  CSS

如果使用的是code打开的项目,那么可以先在code内搜索上面的配置是否已添加,因为部分hugo主题原来就添加了这一配置。

在markdown文档中引用

在 Markdown 文件中,你可以这样使用这些类:

---
title: "康奈尔笔记示例"
---

<link rel="stylesheet" href="/css/cornell-notes.css">  <div class="cornell-notes">    <div class="cues">
## Cues
    - 这里是关键词和提示。
</div>
<div class="notes">
## Notes
  - 这里是详细的笔记内容。
</div>
<div class="summary">
## Summary
 - 这里是笔记的摘要。
</div>
</div>

确保在 Markdown 文件中添加 标签来引入 CSS 文件,并为每个部分使用相应的类。这样,当你构建并查看 Hugo 站点时,康奈尔笔记将以正确的样式显示,其中 “Cues” 在左侧,“Notes” 在右侧,“Summary” 在底部。

Summary

在上面我们学习了如何自定义hugo中页面的输出格式,在引用hugo主题情况下,自定义自己的页面输出格式只需要三点,即创建css文件,在config配置文件中配置相关信息,在markdown中引用。其实我们可以拓展,如果你想创建属于自己的笔记你可以参照上面的方法自制简单的笔记,希望本期内容对你有所帮助。

2025年4月6日

3.github和atomgit的混合使用,哪些是值得注意的?

一台电脑如何使用多个远程仓库?

  • 下载好git,并做好相关的配置
  • 关联github仓库
    • 创建github仓库
    • 克隆仓库到本地
  git clone <你的远程仓库URL>

创建多个 SSH 密钥对涉及到在本地生成多个公钥和私钥文件,并将公钥添加到你想要访问的远程服务器上。以下是详细步骤:

打开终端: 打开你的终端(在 macOS 或 Linux 上)或 Git Bash(在 Windows 上)。

导航到 SSH 目录: 使用 cd 命令切换到你的 SSH 目录,通常是 ~/.ssh。

cd ~/.ssh 生成新的 SSH 密钥对: 使用 ssh-keygen 命令生成新的密钥对。每次运行此命令时,你都可以指定不同的文件名,以便区分不同的密钥对。例如,为 GitHub 生成的密钥对可能命名为 id_rsa_github,为 GitLab 生成的密钥对可能命名为 id_rsa_gitlab。

ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f id_rsa_github 重复上述步骤,为其他服务生成密钥对,只需更改文件名即可。例如:

ssh-keygen -t rsa -b 4096 -C "another_email@example.com" -f id_rsa_gitlab

-b是指密码的长度是4096,与加密有关,-f表示要创建的ssh有关文件名

在生成密钥时,系统会提示你输入一个文件名来保存新的密钥对。如果你直接按回车键,它将使用默认的文件名(如 id_rsa)。如果你想要为每个服务使用不同的文件名,确保在命令中指定 -f 选项后跟你想要的文件名。

将公钥添加到远程服务器: 生成密钥对后,你需要将公钥(文件名后缀为 .pub)添加到远程服务器的 SSH 密钥管理界面。例如,在 GitHub 上,你可以在 Settings > SSH and GPG keys 部分添加新的公钥。

配置 SSH 配置文件: 为了确保 SSH 使用正确的密钥对连接到正确的服务器,你可以在 .ssh 目录下创建一个名为 config 的文件(如果该文件不存在的话),并为每个服务配置不同的设置。

touch ~/.ssh/config

然后编辑这个文件,添加以下内容:

Host github.com
  User git
  IdentityFile ~/.ssh/id_rsa_github
  IdentitiesOnly yes

Host gitlab.com
  User git
  IdentityFile ~/.ssh/id_rsa_gitlab
  IdentitiesOnly yes

这里的 Host 是你为每个服务设置的别名,User 是 Git 服务的默认用户名(通常是 git),IdentityFile 是你的私钥文件的路径。

测试 SSH 连接: 使用 ssh -T 命令测试与远程服务器的连接。例如:

ssh -T git@github.com
ssh -T git@gitlab.com

如果一切设置正确,你应该会看到一条欢迎消息,表明你已经成功通过 SSH 密钥验证。

当我有连个github账户时,当我用同一台电脑推送的时候如何区分是那个推送的

推荐文章

答案:

首先是克隆下来两个项目一个为test_A,一个为test_B,假设有一个账户是A,另一个是B,而且A账户的邮箱是设置为全局的,即用了这条命令:

git config --global user.name "A"

git config --global user.email A@qq.com
#假设这里的邮箱是QQ

那么在在text_A中不作任何修改,在text_B中添加如下命令即可:

git config  user.name "B"

git config user.email B@qq.com
#假设这里的邮箱是QQ

遇到的一些问题

问题一


解决Git上传代码error: failed to push some refs to ‘xxx‘hint:(e.g., ‘git pull …‘) before pushing again错误


推荐文章

问题二


【Git】错误:权限被拒绝(公钥)(Permission denied (publickey).)


推荐文章2)

问题三


解决Git上传代码error: failed to push some refs to ‘xxx‘hint:(e.g., ‘git pull …‘) before pushing again错误


推荐文章

附件

2.git与github的使用

网页链接

原文章链接

1、使用git commi -m “……“时报错: Your branch is up to date with ‘origin/master‘

今天在提交项目的时候报了这个错误,在网上查了解决办法,有的说创建新的分支可以解决,但我的不行。

最后我的解决办法是先退出git base here,再重新进入,下面是具体步骤:

  • 先使用git add .
  • 接着使用git status -s查看我更改的文件,也是我要推送的文件,如果前面的有M和??,那么久重新更新文件,然后重复上面的步骤;
  • 发现前面的M是绿色的后就可以使用git commit -m "文件备注"
  • 然后推送就可以了git push origin main

AndroidStudio中的grable下载很慢,如何解决?

参考地址

这里的关键命令是git status -s,我现在搞不懂这个错误产生的原因……

虚拟机连接不上网络,没有分配ENS33

参考链接

浏览器突然无法访问某些网站,之前可以突然不行。

解决办法: 11 11

在浏览器中关闭,使用安全的 DNS 指定如何查找网站的网络地址,然后就可以访问了,原因我还没弄清楚。

AndroidStudio中为什么不建议使用case R.id.generate判断,而是使用if(v.getId() == R.id.generate)

1.当使用Switch语句写如下代码时,

switch (v.getId()){
            case R.id.generate:

                break;
            case R.id.add:

                break;
        }

(红色表示报错)如果报错android studio报错constant expression required, 可以把Switch语句换成if-else语句即

Ifv.getId() == R.id.generate
{
……
}else ifv.getId() == R.id.add{
……
}

这样就可以了。来源以及具体原因: https://blog.csdn.net/weixin_43912621/article/details/106178388

附件

1.博客优化笔记

代码分制表

< tabs title="file name" >,< /tabs >结尾; 然后里面写以% tab title="code name" style="info" color="blue " %,% /tab %结尾 他们都需要用{{}}括起来

{{< tabs title="hello." >}}
//代码块一
{{% tab title="py" %}}
    ```markdown
    **这里写代码**
    ```
{{% /tab %}}
//代码块二
{{% tab title="sh" %}}
    ```markdown
    **这里写代码**
    ```
{{% /tab %}}

{{< /tabs >}}

显示附件文件

在双花括号里写:attachments color="fuchsia" icon="fab fa-hackerrank",即可,

    {{% attachments color="fuchsia" icon="fab fa-hackerrank" %}}

——————————————————分割线

不论是创建首页页面,还是左边框的选项,归根结底它们都是“目录”,从根上说,你需要创建一个目录然后在目录里面创建一个.md文件来装这个目录或说显示这个目录的内容。

首页的创建

创建首页使用了命令

    hugo new --kind home _index.md

而后产生的页面,它自动生成头文件

    +++
    archetype = "home"
    title = ""
    +++

值得注意的是,如果你用命令

    hugo new site myblog

创建了一个myblog的目录,那么你在里面操作hugo new –kind home _index.md的时候,用于显示首页的文件**_index.md**会自动产生在content文件里。

创建章节的方法

使用命令:

    hugo new <chapter>/<name>/_index.md

或者:

    hugo new <chapter>/<name>.md

在创建章节的时候需要加上alwaysopen = false,如下面的例子:

        +++
        alwaysopen = false
        archetype = "chapter"
        title = "2.AndroidStudio"
        weight = 20
        +++

在标题前面显示图标

    +++
    menuPre = "<i class='fab fa-github'></i> "
    +++

添加这个后的效果如下:

效果图 效果图

最新技术文章

{{ partial "post-card.html" (dict "path" "log/7.初次安装AD需要做哪些设置?.md") }}
物联网安全架构

#13 物联网网络安全论

探讨边缘计算环境下的安全防护体系...

发布于 2024-03-12 | 分类:网络安全

附件

7.初次安装AD需要做哪些设置?

设置自动保存

打开程序点击系统设置,如下图:

系统设置 系统设置

按照下图步骤设置自动保存,每10分钟保存一次(你也可以设置成其他的,但不建议设置太小或太大比如1分钟或者30分钟)

设置自动保存 设置自动保存

设置单一’'表示负信号

按照下图设置

在这里插入图片描述 在这里插入图片描述

负信号表示 负信号表示

最终效果 最终效果

设置复制自动增加 设置复制自动增加

复制自动增加 复制自动增加

复制不自动增加 复制不自动增加

设置显示对话框

我设置前

名字重叠 名字重叠

未设置前 未设置前

当把上面的打钩后

设置后 设置后

在这里插入图片描述 在这里插入图片描述

[外链图片转存中…(img-ZngLHveL-1719559556141)]

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

按住shift+h可以显示和关闭

[外链图片转存中…(img-bxjUhYCS-1719559556142)]

在这里插入图片描述 在这里插入图片描述

[外链图片转存中…(img-qKvH9IRn-1719559556142)] 在这里插入图片描述 在这里插入图片描述

[外链图片转存中…(img-HAD9BmgV-1719559556142)]

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

6.云端一体化的环境变量问题

参数context 与环境变量有关,不管是系统还是用户自定义的都用这个来获取,获取方式:context.env

  • 先部署云函数到远端,然后在去远端添加环境变量,然后在本地运行虚拟机查看。

负载均衡的四种方式

  • 随机
  • 轮询
  • 最少连接数
  • 最短响应时间

延迟时长的重试策略

  • zero:一旦云函数调用失败,则调用,中间不等待
  • constant:调用失败等一段时间
  • jittered:调用失败等一段时间,再次调用,如果失败等待时间以指数增长

熔断

设置一定的条件,满足条件后会执行熔断。熔断后云函数不在提供服务。

e e

附件

5.云端一体化如何查看云函数日志?

调用云函数的时候我们总有输入输出结果不符合自己预期的时候,这个时候查看日志很重要!!在云端一体化开发过程中,我们可以通过下面的方式解决这一问题:

首先参数event的输出以JSON格式输出,这样方便阅读,下面有一段代码,

云函数代码如下:

import { stringify } from "querystring";
let myHandler = async function (event, context, callback, logger) {
  logger.info(JSON.stringify(event));
  const name = event.body.name
  callback({
    code: 200,
    message: `Hello ${name}.`
  });
};

export { myHandler };

获取信息的ets文件如下:

import cloud from '@hw-agconnect/cloud';
@Entry
@Component
struct MyIndex {
  @State message: string = ''
  @State name:string = ''

  build() {
    Row() {
      Column() {
        TextInput({placeholder:'请输入姓名'})
          .onChange(value => {
            this.name = value
          })
        Button('使用云端一体化')
          .fontSize(20)
          .onClick(async ()=>{
            const result = await cloud.callFunction({
              name:'login',//云函数的名字
              version:'$latest',
              params:{name:this.name}
            })
            this.message=result.getValue().message
          })
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

function函数的说明:

  • 触发条件:
    • HTTP请求,
    • 云数据库插入,
    • 云存储
  • 参数含义:
    • 输入信息(),
    • 执行时上下文信息(环境变量),
    • 输出(返回结果给调用者),
    • 记录日志()

此处做了这样的处理使得日志输出以JSON格式输出:


logger.info(JSON.stringify(event))

当遇到问题时,去官方的开发论坛搜索相关内容, 此处我想实现输入一个名字,然后让云端函数以不同的方式输出,比如:


你好,张三

你好,李四


但是现在输出的是:你好,%&*%%

解决办法:

查看event参数说明

jietu1 jietu1

再去CAG官网选择云监控—>日志服务

截图2 截图2

然后复制右边的内容,放到一个json文件夹,用编译器(比如VScode)打开,并调整格式(如果用的是VScode,请使用shift+alt+F调整json格式)如下:

{
    "path": "login-$latest",
    "httpMethod": "POST",

    "headers": 
    {
        "user-agent": "libcurl-agent/1.0",
        "x-forwarded-for": "115.46.239.151, 10.135.135.154, 10.134.64.11",
        "x-forwarded-port": "443",
        "accept": "*/*",
        "accept-encoding": "gzip",
        "agcgw-trigger-authtype": "apigw-client",
        "host": "10.14.4.31",
        "content-type": "application/json;charset=UTF-8",
        "packagename": "com.itwcx.test",
        "appid": "5765880207853994135",
        "authorization": "SDK-HMAC-SHA256 containPath=false,containBody=false,containQuery=false,accessId=m7rTGQ2uNvyq1AGfJFLo,timestamp=1711175951586,signedHeaders=appid;productid,signature=44fe3b4da7d80ec9dfee07ca3747c448e94254b5454fef87f468150089f38623",
        "x-forwarded-host": "10.14.4.31",
        "appversion": "1.0.0",
        "sdkplatformversion": "3.2.0.0",
        "sdktype": "TS",
        "content-length": "19",
        "x-real-ip": "10.134.64.11",
        "sdkversion": "1.0.0",
        "x-forwarded-proto": "https",
        "sdkservicename": "agconnect-cloud",
        "x-trace-id": "f09f1070-6d7b-46a9-875d-bfb2bc4bfdb9",
        "productid": "388421841222044287",
        "sdkplatform": "OpenHarmony"
    },

    "queryStringParameters": "",
    "queryParameters": null,
    "body": "{\"name\":\"zhangsan\"}",
    "isBase64Encoded": false,
    "pathVariable": null
}

此处我们只查看参数event的HTTP请求的相关说明:

比较官网的event参数格式与自己日志打印的格式,看看有什么不同

截图3 截图3

截图4 截图4

观察可知问题出在body这个参数,参数是json格式,而日志打印的是字符格式,我们需要把字符格式转换成json格式,做如下操作:

import { stringify } from "querystring";
let myHandler = async function (event, context, callback, logger) {
  logger.info(JSON.stringify(event));
  const obj = JSON.parse(event.body)
  const name = obj.name
  callback({
    code: 200,
    message: `Hello ${name}.`
  });
};

export { myHandler };

上面的代码中


const obj = JSON.parse(event.body)
const name = obj.name

就是转换操作。

认证服务

登录页面的实现

截图7 截图7

登录验证代码:

import { AuthMode,Login } from '@hw-agconnect/auth-component'
import { AuthUser } from '@hw-agconnect/cloud'
import router from '@ohos.router'
@Entry
@Component
struct MyLogin {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Login({
          modes:[AuthMode.PHONE_VERIFY_CODE],
          onSuccess:(usr:AuthUser) =>{
              router.pushUrl({url:'page/MyWelcome'})
          }
        }){
          Button('登录')
        }
      }
      .width('100%')
    }
    .height('100%')
  }
}

12 12

页面登录获取验证码

import { Auth, VerifyCodeAction } from '@hw-agconnect/cloud';
import cloud from '@hw-agconnect/cloud'
import hilog from '@ohos.hilog'
import router from '@ohos.router';
@Entry
@Component

struct MyLogin {
  @State countDown: number = 10
//用一个变量来获取setinterval的ID
  intervalId:number = 0 //因为这个变量与页面显示无关所以不用@State注释
  @State verifyCodeButtonEnable:boolean = false
  @State verifyCodeButtonText:string ='获取验证码'
  @State phoneNumber: string = ''
  @State verifyCode:string = '输入验证码'

  //定时器代码
  waiting(){
    this.verifyCodeButtonEnable = false
    this.verifyCodeButtonText = `${this.countDown}s`//在一点击时,就显示10S
    this.intervalId = setInterval(() => { //要知道定时器结束没有需要知道它的返回结果,这个结果可以通过setinterval的ID获取。
      this.verifyCodeButtonText = `${this.countDown}s`//将倒计时显示出来
      if(this.countDown < 0){
        //如果减到0,清楚定时器
        clearInterval(this.intervalId)//得到返回结果后清楚定时器
        this.countDown = 10
        this.intervalId = 0
        this.verifyCodeButtonText = '获取验证码'//倒计时结束时回复
        this.verifyCodeButtonEnable = true //当点击时按键不可用
        return //不需要再往下继续执行了,所以return
      }
      this.countDown--
    },1000)//每隔1秒减一次
  }
   judgement(){
     if(this.phoneNumber.length === 11){
       this.verifyCodeButtonEnable=true
     }else {
       this.verifyCodeButtonEnable=false
     }
   }
  async sending_verifyCode(){
    try { //调用方式异步调用
      await cloud.auth().requestVerifyCode({
        verifyCodeType: {
          kind: 'phone',
          phoneNumber: this.phoneNumber,
          countryCode: '86'
        },
        action: VerifyCodeAction.REGISTER_LOGIN, //验证的方式
        lang: 'zh_CN',
        sendInterval: 10
      })
      hilog.info(0,'VerifCode','Success')
    } catch (e) {
      AlertDialog.show({ title: '错误', message: '验证码失败' })//弹窗内容
      hilog.info(0,'VerifCode',JSON.stringify(e))
    }
  }
  async login_verify(){
    try {
      const result  = await cloud.auth().signIn({ //定义一个变量来接收它的返回结果,返回的是result,在通过result.gitUsr获取用户信息
        credentialInfo: {
          kind: 'phone',
          countryCode: '86',
          phoneNumber: this.phoneNumber,
          verifyCode: this.verifyCode
        }
      })
      const user = result.getUser()
      AppStorage.SetOrCreate('user',user)//存储用户到 AppStorage
      hilog.info(0,'Login','Success')
      router.replaceUrl({ url: 'pages/MyLoginLignOut' })
    } catch (e) {
      AlertDialog.show({title:'错误',message:'登陆失败'})
      hilog.info(0,'Login',JSON.stringify(e))
    }
  }


  build() {
    Row() {
      Column() {
        TextInput({placeholder:'请输入手机号'})
          .type(InputType.Number)
          .onChange(values =>{
          this.phoneNumber = values
            this.judgement()//点击获取验证码时,判断按键是否可用
        })
       Row(){
         TextInput({placeholder:'验证码'})
           .width('70%')
           .onChange(value =>{
             this.verifyCode=value
           })
         Button(this.verifyCodeButtonText)
           .width('30%')
           .enabled(this.verifyCodeButtonEnable)//该属性是控制按键是否可用
           .onClick(async () =>{ //所在方法加async
             this.waiting()
             this.sending_verifyCode()
           })
       }
        .width('100%')
        Button('登录')
          .enabled(this.phoneNumber.length === 11 && this.verifyCode.length === 6)//手机号11位,且验证码六位时点击登录才有效
          .onClick(async ()=>{
            this.login_verify()
          })

      }
      .width('100%')

    }
    .height('100%')
  }

}

登录后跳转到的页面:

import cloud ,{AuthUser}from "@hw-agconnect/cloud"
import hilog from '@ohos.hilog'
import router from '@ohos.router'
@Entry
@Component
struct MyLoginLignOut {
  @State photoUrl:string = '' //存储头像路径
  @State displayName:string = '' //存储用户昵称
  @StorageLink('user') user:AuthUser = null //定义一个AuthUser的类型来获取用户存的值,('user')叫存储对象名,是在另一个页面定义的名字
aboutToAppear(){//build渲染前就运行
  //1.cloud.auth().getCurrentUser()
  //2.appStorage
  this.displayName = this.user.getDisplayName()//获取用户,间接通过用户拿到用户头像等数据。
  this.photoUrl = this.user.getPhotoUrl()
}
  build() {
    Row() {
      Column() {
        Row(){
          Image(this.photoUrl?this.photoUrl:$r('app.media.app_icon'))
            .width(80)
            .height(80)
            .onClick(()=>{
              this.photoUrl= 'https://img.btstu.cn/api/images/5a2a5d5560223.jpg'
            })
        }.width('100%')
        .justifyContent(FlexAlign.Center)
        .padding({bottom:70})


        TextInput({placeholder:'设置昵称',text:this.displayName})
          .fontSize(25)
          .fontWeight(FontWeight.Bold)
          .onChange(value =>{
            this.displayName = value
          })
        Button('保存')
          .margin({top:10,bottom:10})
          .width(90)
          .onClick(async ()=>{//保存图片与用户名

            try {
              await this.user.updateProfile({//保存头像、用户名
                displayName: this.displayName,
                photoUrl: this.photoUrl
              })
              hilog.info(0,'updateProfile','Success')
            } catch (e) {
              hilog.info(0,'updateProfile',JSON.stringify(e))
            }
          })
        Button('登出')
          .margin({top:10,bottom:10})
          .width(90)
          .onClick(async ()=>{
            try {
              await cloud.auth().signOut()
              hilog.info(0,'SignOut','Success')
              router.replaceUrl({url:'pages/MyLogin'})
            } catch (e) {
              hilog.info(0,'SignOut',JSON.stringify(e))
            }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

个人设置中心

import cloud, { AuthUser } from '@hw-agconnect/cloud'
import router from '@ohos.router'
import hilog from '@ohos.hilog'
import picker from '@ohos.file.picker'

@Entry
@Component
struct MyIndex {
  @State photoUrl: string = '' //存储头像路径
  @State displayName: string = '' //存储用户昵称
  @StorageLink('user') user: AuthUser = null //定义一个AuthUser的类型来获取用户存的值,('user')叫存储对象名,是在另一个页面定义的名字
  @State uploading: boolean = false//用户控制图片是否可以点击,上传时不可以点击
  @State uploadingText: string = '0%'//上传进度

  aboutToAppear() {  //build渲染前就运行
    // 1. cloud.auth().getCurrentUser()
    // 2. AppStorage
    this.displayName = this.user?.getDisplayName()//获取用户,间接通过用户拿到用户头像等数据。加问号是因为在aboutToAppear运行时是无法获取用户名或图片等信息的
    this.photoUrl = this.user?.getPhotoUrl()
  }

  build() {
    Row() {
      Column({ space: 10 }) {
        Stack() {//Stack该属性让它所包含的组件重叠
          Image(this.photoUrl ? this.photoUrl : $r('app.media.user_dark'))
            .width(70)
            .height(70)
            .borderRadius(70)
            .enabled(!this.uploading)
            .onComplete(()=>{
              this.uploading = false
            })
            .onClick(async () => {
              // this.photoUrl = '网络图片的地址'
              try {
                // 1. 从相簿中选照片
                const options = new picker.PhotoSelectOptions()
                options.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE//媒体类型:视频还是图片
                options.maxSelectNumber = 1//最大选择个数
                const result = await new picker.PhotoViewPicker().select(options)//把参数传给下面的方法,把结果传给result
                hilog.info(0, 'Upload', `Picker Success ${result.photoUris[0]}`)
                this.uploading = true
                // 2. 调云存储 api 上传照片
                await cloud.storage().upload({
                  localPath: result.photoUris[0],//本地图片路径
                  cloudPath: `test/${this.user.getUid()}.jpg`,//云存储那边存储的路径
                  onUploadProgress: event => {
                    const percent = Math.floor(100 * event.loaded / event.total)//获取上传的百分比,event.loaded:已上传的自己数,event.total:总的自己数,Math.floor:舍弃小数点
                    this.uploadingText = `${percent}%`  //数值更新到uploadingText
                  }
                })
                hilog.info(0, 'Upload', 'Upload Success')
                // 3. 获取上传照片的网络地址
                const url = await cloud.storage().getDownloadURL(`test/${this.user.getUid()}.jpg`)//获取公网地址,拿到图片在网络上的地址
                this.photoUrl = `${url}&ts=${new Date().getTime()}`//这个里做这个处理是因为,图片被缓存起来了,上传的时候如果只是给云存储的地址,那么在传第二张图片到云存储后,photoUrl再获取时,因为图片地址不变照片也不会改变,它会缓存起来,而在图片后面加上一个时间图片就不在被缓存。
                // this.uploading = false
                hilog.info(0, 'Upload', `url: ${url}`)
              } catch (e) {
                hilog.error(0, 'Upload', JSON.stringify(e))
              }
            })
          if (this.uploading) {
            // 显示上传进度
            Text(this.uploadingText)
              .width(70)
              .height(70)
              .borderRadius(70)
              .fontColor('white')
              .backgroundColor('black')
              .opacity(0.6)
              .fontSize(24)
              .fontWeight(FontWeight.Bolder)
              .textAlign(TextAlign.Center)//文字对齐方式
          }
        }


        TextInput({ placeholder: '请设置昵称', text: this.displayName })
          .width('50%')
          .onChange(value => {
            this.displayName = value
          })
        Button(`保存`)
          .onClick(async () => {
            try {
              await this.user.updateProfile({
                displayName: this.displayName,
                photoUrl: this.photoUrl
              })
              hilog.info(0, 'updateProfile', 'Success')
            } catch (e) {
              hilog.error(0, 'updateProfile', JSON.stringify(e))
            }
          })
        Button(`登出`)
          .onClick(async () => {
            try {
              await cloud.auth().signOut()
              hilog.info(0, 'SignOut', 'Success')
              router.replaceUrl({ url: 'pages/MyLoginCustom' })
            } catch (e) {
              hilog.error(0, 'SignOut', JSON.stringify(e))
            }
          })

      }
      .width('100%')
    }
    .height('100%')
  }
}

登录页面

import { Auth, VerifyCodeAction } from '@hw-agconnect/cloud';
import cloud from '@hw-agconnect/cloud'
import hilog from '@ohos.hilog'
import router from '@ohos.router';
@Entry
@Component

struct MyLogin {
  @State countDown: number = 10
//用一个变量来获取setinterval的ID
  intervalId:number = 0 //因为这个变量与页面显示无关所以不用@State注释
  @State verifyCodeButtonEnable:boolean = false
  @State verifyCodeButtonText:string ='获取验证码'
  @State phoneNumber: string = ''
  @State verifyCode:string = '输入验证码'
  private mainPage = `pages/StudentPage`

  async aboutToAppear() {
    try {
      const user = await cloud.auth().getCurrentUser()//获取当前认证用户
      if (user != null) {//获取用户是否为空,如果用户已经断开会话,则下一次进入时间走时登录流程
        AppStorage.SetOrCreate('user', user) // 如果会话未断开,不走登录流程,而是吧用户数据存到AppStorage
        router.replaceUrl({ url: this.mainPage })
      }
    } catch (e) {
      hilog.error(0, 'Login', JSON.stringify(e))
    }
  }
  //定时器代码
  waiting(){
    this.verifyCodeButtonEnable = false
    this.verifyCodeButtonText = `${this.countDown}s`//在一点击时,就显示10S
    this.intervalId = setInterval(() => { //要知道定时器结束没有需要知道它的返回结果,这个结果可以通过setinterval的ID获取。
      this.verifyCodeButtonText = `${this.countDown}s`//将倒计时显示出来
      if(this.countDown < 0){
        //如果减到0,清楚定时器
        clearInterval(this.intervalId)//得到返回结果后清楚定时器
        this.countDown = 10
        this.intervalId = 0
        this.verifyCodeButtonText = '获取验证码'//倒计时结束时回复
        this.verifyCodeButtonEnable = true //当点击时按键不可用
        return //不需要再往下继续执行了,所以return
      }
      this.countDown--
    },1000)//每隔1秒减一次
  }
   judgement(){
     if(this.phoneNumber.length === 11){
       this.verifyCodeButtonEnable=true
     }else {
       this.verifyCodeButtonEnable=false
     }
   }
  async sending_verifyCode(){
    try { //调用方式异步调用
      await cloud.auth().requestVerifyCode({
        verifyCodeType: {
          kind: 'phone',
          phoneNumber: this.phoneNumber,
          countryCode: '86'
        },
        action: VerifyCodeAction.REGISTER_LOGIN, //验证的方式
        lang: 'zh_CN',
        sendInterval: 10
      })
      hilog.info(0,'VerifCode','Success')
    } catch (e) {
      AlertDialog.show({ title: '错误', message: '验证码失败' })//弹窗内容
      hilog.info(0,'VerifCode',JSON.stringify(e))
    }
  }
  async login_verify(){
    try {
      const result  = await cloud.auth().signIn({ //定义一个变量来接收它的返回结果,返回的是result,在通过result.gitUsr获取用户信息
        credentialInfo: {
          kind: 'phone',
          countryCode: '86',
          phoneNumber: this.phoneNumber,
          verifyCode: this.verifyCode
        }
      })
      const user = result.getUser()
      AppStorage.SetOrCreate('user',user)//存储用户到 AppStorage
      hilog.info(0,'Login','Success')
      router.replaceUrl({ url: 'pages/MyLoginLignOut' })
    } catch (e) {
      AlertDialog.show({title:'错误',message:'登陆失败'})
      hilog.info(0,'Login',JSON.stringify(e))
    }
  }


  build() {
    Row() {
      Column() {
        TextInput({placeholder:'请输入手机号'})
          .type(InputType.Number)
          .onChange(values =>{
          this.phoneNumber = values
            this.judgement()//点击获取验证码时,判断按键是否可用
        })
       Row(){
         TextInput({placeholder:'验证码'})
           .width('70%')
           .onChange(value =>{
             this.verifyCode=value
           })
         Button(this.verifyCodeButtonText)
           .width('30%')
           .enabled(this.verifyCodeButtonEnable)//该属性是控制按键是否可用
           .onClick(async () =>{ //所在方法加async
             this.waiting()
             this.sending_verifyCode()
           })
       }
        .width('100%')
        Button('登录')
          .enabled(this.phoneNumber.length === 11 && this.verifyCode.length === 6)//手机号11位,且验证码六位时点击登录才有效
          .onClick(async ()=>{
            this.login_verify()
          })

      }
      .width('100%')

    }
    .height('100%')
  }

}

希望文档对你有所帮助。

附件

8.如何用旧电脑搭建自己的服务器?

拥有一个属于自己的服务器个人感觉就很赞,我最初是在不买公网IP情况下,用一台电脑做服务器,然后另外一台电脑可以远程访问,部署网站,然后别人可以访问这个网站,这样一个功能。如果你也有这样的想法还请阅读下去,希望自己的经验对你有所帮助。

所需设备

一台旧电脑,一台目前在使用的电脑。

在旧电脑上安装natapp程序

在百度上搜索NATAPP,或点击这个链接:NATAPP,

网站首页面 网站首页面

进入网站后先简单注册一下用户,顺便实名认证一下,因为买隧道会提示需要实名,接着点击首页下载自己的电脑系统对应版本,我的是Windows(下面的测试也是基于Windows)

下载页面 下载页面

下载好后解压文件,得到如下图程序:

下载页面 下载页面

在解压文件下新建一个文件: config.ini,做好这一步后我们去购买免费的隧道,如下图:

下载页面 下载页面

点击网站的文档,然后点击教程/文档里的 使用本地配置文件config.ini 文章,

下载页面 下载页面

然后复制里面的配置信息:

下载页面 下载页面

#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken=                      #对应一条隧道的authtoken
clienttoken=                    #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none                        #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=ERROR                  #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy=                     #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空

在字段authtoken后面值在我的隧道配置里可以找到,如下:

下载页面 下载页面

复制下图authtoken的值填到config.ini文件里

下载页面 下载页面

我这边演示的效果如下:

下载页面 下载页面 下载页面 下载页面

完成上面的操作后,我们运行NATAPP程序,结果如下:

下载页面 下载页面

你会得到一个随机的网址,这个网址指向你的本地端口,当你有网站挂载到本地端口时,你就可以通过网址访问到你的本地端口网址,内网外网都可以,但是这个网址每一次运行都会不一样,这点需要注意,然后就是端口的设置,看下图:

下载页面 下载页面

我这里写的的端口是1313,如果你网站运行后不在1313,例如你运行网站后,本地的网站启动端口是8080.那你就把这个地方的1313改为8080就可以了。

下面我们举个例子看看,假设我现在我需要外网的网友访问我挂在在自己电脑本地的网站,那们在做好上面的操作后我先去启动自己的网站,这里使用hugo的网站模版,用code程序打开网站模版,然后调出终端,在终端输入命令 hugo server ,表示启动网站,如下图:

下载页面 下载页面

然后去复制刚才运行NATAPP程序后的网站,在浏览器中打开,我们先试一下局域网能不能访问,结果如下:

下载页面 下载页面

将网站发给自己的朋友,测试外网是否可以访问,

下载页面 下载页面

结果是外网可以正常可以访问。

下载页面 下载页面

那么到这里你的电脑其实就充当了一台服务器,将网站放到了你电脑上,只要你的电脑保持开机状态那么别人就可以一直访问你的网站,或许NATAPP网站还有别的有趣功能,所以你不妨去摸索摸索,所不定可以找到多次启动NATAPP指向你本地的网址不变,你也就不用每次启动后都要发新的网址给对方了。

那么其实到这里就差不多了,但是我们还需要用一台电脑控制另一台电脑,简而言之就是电脑的远程登录,其实电脑远程登录有许多的方法,这里我需要借助一个来实现内网穿透,其实上面的旧电脑变服务器也是使用了内网穿透,

附件

10.如何自定义hugo主题页面输出格式?

**声明:**需要注意文章只提供思路,当处理一些复杂问题时可能需要变换思路,所以请根据自身情况选择是否阅读本篇文章。

因本人没学过go语言,所以大部分代码都交由AI编写,不排除代码可能存在隐患。

接下来我以自己使用的hugo-theme-relearn主题为例,讲解如何自定义康奈尔笔记页面输出格式。(hugo-theme-relearn主题

工程目录结构

your-blog/
├── archetypes/
│   └── cornell-notes.md        # 笔记原型模板
├── layouts/
│   ├── cornell-notes/
│   │   └── views/
│   │       └── article.html    # 页面布局模板
│   ├── partials/
│   │   ├── cornell-notes.html  # 内容输出逻辑
│   │   └── custom-header.html  # 自定义样式表
│   └── shortcodes/
│       ├── cues.html           # 左侧标签短代码
│       ├── notes.html          # 右侧内容短代码
│       └── summary.html        # 底部总结短代码
└── static/
    └── images/
        └── cornell-img/
            ├── icon1.svg       # 右侧装饰图标
            ├── icon2.svg       # 左侧装饰图标
            └── watermark.jpg   # 底部背景图

功能实现全流程

具体需求

我想输出一个左侧写标签,右侧写内容,最底下显示写总结的“康奈尔笔记”页面,使用命令:

hugo new --kind cornell-notes learning/algorithm/_index.md

创建.md文档后,md文件自动展示为如下格式:

  +++
  title = "{{ replace .Name "-" " " | title }}"
  type = "cornell-notes"
  date = {{ .Date }}
  draft = true
  +++

  {{% cues %}}
  写标签区域

  {{% /cues %}}

  {{% notes %}}
  写内容区域
  {{% /notes %}}

  {{% summary %}}
  总结区域
  {{% /summary %}}

当识别到短代码{{% cues %}}时,将{{% cues %}} {{% /cues %}}中的内容显示在标签一侧,剩下的依次类推。

样式定义

注意:假设自己的主题目录为your-blog,那么接下来的文件创建都该目录下,可参考目录结构

创建 layouts/partials/custom-header.html遇到没有的目录或文件请自行创建,下同,所以不再提示!!

<style>
/* 康奈尔笔记容器 */
.cornell-notes {
  padding: 20px;
  margin: 15px 0;
  display: flex;
  flex-wrap: wrap;
  background: #f9f9f9;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* 左侧标签区 */
.cues {
  background: url('/images/cornell-img/icon2.svg') no-repeat 95% 20px;
  border-right: 2px dashed #ccc;
  flex: 1;
  padding-right: 25px;
  min-width: 250px;
}

/* 右侧内容区 */
.notes {
  background: url('/images/cornell-img/icon1.svg') no-repeat 5% 20px;
  border-left: 2px dashed #ccc;
  flex: 2;
  padding-left: 25px;
  min-width: 300px;
}

/* 底部总结区 */
.summary {
  width: 100%;
  clear: both;
  padding: 20px;
  margin-top: 20px;
  background: url('/images/cornell-img/watermark.jpg') center/cover;
  border-radius: 6px;
  position: relative;
}

/* 响应式适配 */
@media (max-width: 768px) {
  .cornell-notes {
    flex-direction: column;
  }
  .cues, .notes {
    border: none;
    padding: 15px 0;
  }
}
</style>

短代码开发

​1.​左侧标签区​​ (layouts/shortcodes/cues.html):

{{ $scratch := .Page.Scratch >}}
{{ $scratch.Set "cuesContent" ( .Inner | markdownify ) }}

2.右侧内容区:(layouts/shortcodes/notes.html):

{{ $scratch := .Page.Scratch }}
{{ $scratch.Set "notesContent" ( .Inner | markdownify ) }}

3.底部总结区:(layouts/shortcodes/summary.html):

{{ $scratch := .Page.Scratch }}
{{ $scratch.Set "summaryContent" ( .Inner | markdownify ) }}

页面样式

创建页面样式 layouts/partials/cornell-notes.html

<div class="cornell-content">
  /*{{/* 初始化存储空间 */}}
  {{ .Scratch.Delete "cuesContent" }}
  {{ .Scratch.Delete "notesContent" }}
  {{ .Scratch.Delete "summaryContent" }}*/
  
  {{/* 触发短代码解析 */}}
  {{ $dummy := .Content }}
  
  {{/* 结构化输出 */}}
  <div class="cues-section">
    {{ with .Scratch.Get "cuesContent" }}
      <div class="cues-header">📌 关键标签</div>
      <div class="cues-body">{{ . | safeHTML }}</div>
    {{ else }}
      <div class="warning">⚠️ 未检测到标签内容</div>
    {{ end }}
  </div>

  <div class="notes-section">
    {{ with .Scratch.Get "notesContent" }}
      <div class="notes-header">📝 学习记录</div>
      <div class="notes-body">{{ . | safeHTML }}</div>
    {{ else }}
      <div class="warning">⚠️ 未检测到笔记内容</div>
    {{ end }}
  </div>

  <div class="summary-section">
    {{ with .Scratch.Get "summaryContent" }}
      <div class="summary-header">✨ 学习总结</div>
      <div class="summary-body">{{ . | safeHTML }}</div>
    {{ else }}
      <div class="warning">⚠️ 未检测到总结内容</div>
    {{ end }}
  </div>
</div>

页面内容输出逻辑

创建 layouts/cornell-notes/views/article.html

<article class="cornell-notes">
  {{ partial "cornell-notes.html" . }}
</article>

此处就是系统将自动调用了我们之前写的layouts/partials/cornell-notes.html文件,将内容输出到这个容器中。

实际效果如下alt text alt text

为了丰富页面你可以设置标题、页脚等显示样式,这些你使用的主题一般会提供,如我这里的:

<article class="cornell-notes">
  <header class="headline">
    {{partial "content-header.html" .}}
  </header>
  {{partial "heading-pre.html" .}}{{partial "heading.html" .}}{{partial "heading-post.html" .}}
  {{partial "cornell-notes.html" .}}

  <footer class="footline">
    {{partial "content-footer.html" .}}
  </footer>
</article>

代码说明如下(都是基于我使用的主题,可不看):

1.从上而下,article容器的class值为cornell-notes,这个值在layouts/partials/custom-header.html中,这是之前编写页面布局时写的,此处调用该cornell-notes样式:

<article class="cornell-notes">

alt text alt text

2.编写内容顶栏代码,如果项目未提供就自己创建一个,同样放地,放在layouts/partials/custom-header.html中,因此次使用主题自带故不再创建。当然,你也可以选择不写。

<header class="headline">
    {{partial "content-header.html" .}}
  </header>

对于标题设置:

{{partial "heading-pre.html" .}}{{partial "heading.html" .}}{{partial "heading-post.html" .}}

页脚设置:

<footer class="footline">
    {{partial "content-footer.html" .}}
  </footer>

依实际需求而定,可写可不写

3.总而言之,如果你只是输出之前需求的页面,那么可以只写这句代码:

<article class="cornell-notes">
  {{ partial "cornell-notes.html" . }}//就加了这句
</article>

{{ partial "cornell-notes.html" . }}这里就是去调用我们之前写的layouts/partials/cornell-notes.html文件,将内容输出到这个容器中。还记得吗?这个文件是我们写完短代码后写的文件,目的是将内容按照要求输出。

原型模板创建

我们在创建页面时,如果使用命令:

hugo new --kind cornell-notes log/cornell-notes/_index.md

这个命令会自动创建一个archetypes/cornell-notes.md模型的文件,实现这一操作你需要先创建layouts/cornell-notes/views/article.html,这一操作在上面已经完成,接下来只需要创建archetypes/cornell-notes.md文件即可。值得注意的是无论是命令、还是layouts、archetypes文件下,都提到了cornell-notes这个名字,所以创建时留意名字需要相同

这里小结这一思路:

  • layouts/[fileName]/views/article.html中编写页面输出逻辑。
  • layouts/[fileName].md中编写文件模版
  • 使用命令hugo new --kind [fileName] [path]生成模版文件

接下来是详细操作:

1.创建archetypes/cornell-notes.md文件:

  +++
  title = "{{ replace .Name "-" " " | title }}"
  type = "cornell-notes"
  date = {{ .Date }}
  draft = true
  +++

  {{% cues %}}
  写标签区域

  {{% /cues %}}

  {{% notes %}}
  写内容区域
  {{% /notes %}}

  {{% summary %}}
  总结区域
  {{% /summary %}}

2.使用方式

​​生成模版文件​​:

hugo new --kind cornell-notes learning/algorithm/_index.md

实际效果图:

alt text alt text

需要注意的是写模版文件的时候是依据你个人需求而定的,例如创建layouts/[fileName]/views/article.html这样的文件,在有些主题可能是layouts/partials/[fileName]/article.html,又如,此处md文件头部模版为:

+++
  title = "{{ replace .Name "-" " " | title }}"
  type = "cornell-notes"
  date = {{ .Date }}
  +++

而有些则是以:

  ---
    title = "{{ replace .Name "-" " " | title }}"
    archetype = "cornell-notes"
    date = {{ .Date }}
  ---

旨在告诉你编写形式各不相同,所以清楚这一思路即可。要避免这些坑请结合主题的说明文档帮助查看

总结

最后的最后做个总结,要想自定义hugo主题页面输出格式,你可以按照如下步骤展开:

  • 确定需求,不会写代码让AI完成。
  • 如果有需要请编写段代码。
  • 编写输出逻辑,如果不会还请求助社区、AI等。
  • 编写页面模板,参照以上思路,同时参考自己hugo主题的文档说明