前言

搞ispeak时发现ispeak更新,能自定义评论了,也就是说我之前写的ispeak教程失效了
没办法我只能重新写了这篇教程

教程

后端部署

配置数据库

  1. 注册MongoDB账号,注册完成后会提示你创建一个组织,并且输入一个项目昵称,选择编程语言(不选也可以),随后点击右下角的 Continue(继续),如果没有可以跟如下图执行,点击 Create an Organization(创建组织)
    Organizations-Home
    Register-Organizations
    Create-Organization
    New-Project
    Project-Name
    Create-Project
    Build-Database
    Select-Free
    AWS-N.Virginia
  2. 选择免费的共享数据库,随后会跳出选择地区(选择离你服务端近的即可),点击 Create Cluster创建
  3. 随后您需要创建数据库用户,输入用户名和密码,继续向下滚动就是添加 IP 地址,最后点击下方的 Finish and Close(完成并关闭)按钮

注意
服务器部署,则填服务器公网 IP
无服务器(ServerLess)ServerLess 一般都是动态 IP,你无法得到一个固定 IP,我们建议填写 0.0.0.0

Add IP

  1. 稍作等待创建好数据库即可,随后点击 Connect(连接),点击选择 Connect you application(连接应用程序),然后复制连接数据库字符串

注意
需要将字符串中的 <password>替换为您在第三步创建的数据库用户密码,修改 myFirstDatabase为你想要的数据库名称例如:Discuss

Connect
Get Connect

部署kkapi

  1. 点击下方按钮,跳转至 Vercel 进行部署。
    部署到Vercel
  2. 配置环境变量:
    msedge_wwcTzH8isA
    环境变量可能随项目的迭代而增加必填的环境变量,具体请参考官网 —— kkapi环境变量
  3. 重新部署
    msedge_owqMifozPi
    msedge_5WUBTLL0NZ
    msedge_mMprAjHb9O
  4. 绑定域名(建议)
    msedge_miAwQcdm9f
  5. 初始化账户
    浏览器访问:
    你刚刚复制的地址/api/user/init?userName=你想设置的账户名
    如果不指定用户名则自动将账户名设置为admin


克隆源代码

git clone https://ghproxy.com/https://github.com/kkfive/kkapi-open.git

  1. 安装依赖
    yarn install
    如果没有yarn则先允许npm i yarn -g进行安装

  2. 安装 pm2

npm i pm2 -g

  1. 编译项目

yarn build

  1. 配置环境变量

在项目目录新建文件local.env,将环境变量写入其中即可。例如:

1
2
3
4
5
6
PORT=3000
DATABASE_URL=mongodb://127.0.0.1:27017/kkpaiopen?authSource=admin
DATABASE_USER=root
DATABASE_PASSWORD=root
# 加密密钥 测试
SECRETKEY=xxxxxxxxxxxxxxx

其中 PORT 表示启动的端口

  1. 启动项目

pm2 start pm2.json

然后通过命令curl http://127.0.0.1:3000/api/user/init检查是否允许成功

image-20220227101623911

更新项目

进入项目并执行一下命令

1
2
3
git pull
yarn build
pm2 restart pm2.json

尚未写完

部署kkadmin

介绍:kkadmin是kkapi的后台,方便发布说说

以下部署姿势你只需要任选其一即可,无需全部部署。

由于构建 kkadmin 时部分依赖文件需要 nodejs16 及以上版本才可以安装,因此不能将源代码扔给 vercel 进行构建。
所以只能够利用 GitHub actions 构建完成后将产物扔给 vercel 进行使用

  1. Fork这个项目:https://github.com/kkfive/kkadmin-open/

msedge_6HMaGfN000
msedge_UPsCgr2okQ

  1. 配置变量
    msedge_ktuszZjpej

VITE_GLOB_API_URL(必选)
msedge_wqMbtdCCon

  1. 构建actions

msedge_yymiOm8Kek
msedge_4awdNaFJGz
msedge_2uYyc6Qh4I

  1. 部署到Vercel
    复制下面这个网址
1
https://vercel.com/new/import?s=https://github.com/SinInno/kkadmin-open/tree/vercel

并将“SinInno”改为你Github的用户名后访问你刚刚修改的网址
PS:如果你Fork的这个项目有改Repository name,那么请将上面的“kkapi-open”改为你这个项目的Repository name

然后直接部署

msedge_GJbsx9xoOw

部署完成后点“Go to Dashboard”,并点左上角的“Visit”

msedge_Lyo0nIvIqF

账号输入你之前初始化账户的账户名
密码请输入默认密码:123456
登入面板后请更改默认密码!

如果你已经在 GitHub actions 部署了,那么 cf pages 部署时也可以选择 vercel 进行部署。也可以使用主干分支进行打包部署。
接下来的教程以使用 cf pages 构建为例介绍

  1. fork项目(可直接fork)
  2. 导入项目

msedge_1bV5Of8ioS
msedge_CcnQ68DBFQ

  1. 配置环境变量

环境变量参考:官网 —— kkadmin环境变量

配置环境变量

  1. 等待构建完成后即可

构建中

其他环境部署基本可以直接使用 GitHub 构建后的 vercel 进行,毕竟只是 HTML

前端配置

进入后台,查看个人ID

查看个人ID

前端引入

ipseak 使用 marked 依赖和 highlight 依赖,为了减少打包体积,并没有将该依赖打包,因此需要使用 cdn 进行外部引入。

  1. 在博客目录下运行hexo new page speaks
  2. 编辑[blogroot]\source\speaks\index.md,将里面的内容替换成:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    ---
    title: 说说
    comments: false
    aside: false
    ---
    {% raw %}

    <style>
    .speak-footer,.wl-power{
    display:none;
    }
    </style>
    <div id="ispeak"></div>
    <link
    rel="stylesheet"
    href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css"
    />
    <link
    rel="stylesheet"
    href="https://npm.elemecdn.com/ispeak@4.4.0/style.css"
    />
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script src="https://cdn.staticfile.org/marked/2.0.0/marked.min.js"></script>
    <!-- CSS -->
    <link href="https://unpkg.com/@waline/client/dist/waline.css" rel="stylesheet" />
    <script src="https://unpkg.com/@waline/client@v2/dist/waline.js"></script>
    <script>
    function load_ispeak() {
    setTimeout(function() {
    var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
    var src = 'https://cdn1.tianli0.top/npm/ispeak/ispeak.umd.js'
    var script = document.createElement('script')
    script.setAttribute('type','text/javascript')
    script.onload = function() {
    pjax_ispeak()
    }
    script.setAttribute('src', src)
    HEAD.appendChild(script)
    }, 1);
    };
    function pjax_ispeak() {
    if(!document.querySelectorAll("#ispeak")[0])return;
    ispeak
    .init({
    el: '#ispeak',//不用改
    api: '', //填写你kkapi地址(不是kkadmin后台地址!)
    author: '',//填写个人ID
    pageSize: 10,//每页speak显示的条数(建议保持默认,除非你要魔改css)
    loading_img: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif',//加载动画
    comment: function (speak) {
    const { _id, title, content } = speak
    const contentSub = content.substring(0, 30)
    Waline.init({
    el: '.ispeak-comment',//不用改
    serverURL: '',//填写你的Waline服务端地址
    path:'/speak/info.html?q=' + _id,//不用改,除非你将上面的speak改为别的文件夹昵称
    //后面可按照https://waline.js.org/reference/client/props.html 修改(必须放在path后面!以下为一个例子)
    emoji: ["//unpkg.com/@waline/emojis@1.0.1/weibo","https://emoji.shojo.cn/bili/src/小黄脸","//unpkg.com/@waline/emojis@1.0.1/bilibili","https://emoji.shojo.cn/bili/src/枕边童话","https://emoji.shojo.cn/bili/src/咩栗","https://emoji.shojo.cn/bili/src/呜米","https://emoji.shojo.cn/bili/src/进击的冰糖","https://emoji.shojo.cn/bili/src/冰糖IO 蜕变·闪耀","https://emoji.shojo.cn/bili/src/多多poi","https://emoji.shojo.cn/bili/src/穆小泠","https://emoji.shojo.cn/bili/src/早稻叽"],// 表情包大全
    });
    }
    });
    }
    load_ispeak();
    document.addEventListener('pjax:complete', function () {
    pjax_ispeak();
    });
    </script>
    <!--建议标注末尾链接-->
    <p style="width: 100%;text-align: end;font-size: .75em;color: #999;margin-top: 1em;">Powered by <a href="https://www.antmoe.com/speak/"><strong>iSpeak</strong></a> | Comment by <a href="https://discuss.js.org/"><strong>Waline</strong></a></p>
    {% endraw %}
  3. 在[blogroot]\source\speaks\下新建info.md,内容为下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    ---
    title: Speak
    aside: false
    comments: false
    description: 欢迎来到SinzMise的日记,快来看看SinzMise分享了什么! # speak介绍
    ---
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css" />
    <link href="https://unpkg.com/@waline/client/dist/waline.css" rel="stylesheet" />
    <div class='content'>
    <img src='https://bu.dusays.com/2022/05/01/626e88f349943.gif'>
    </div>
    {% btn '/speaks/',查看全部,far fa-hand-point-right,block center blue larger %}
    <hr />
    <div class='ispeak-comment'></div>
    <!-- JS -->
    <script src="https://unpkg.com/@waline/client@v2/dist/waline.js"></script>
    <script src="https://unpkg.com/marked@4.0.18/marked.min.js"></script>
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script>
    const searchParams = new URLSearchParams(window.location.search);
    const speakId = searchParams.get('q');
    const path = window.location.pathname;
    const apiURL = 'https://(你的api地址)/api/ispeak';
    const markedRender = (body, loading_img='https://bu.dusays.com/2022/05/01/626e88f349943.gif') => {
    const renderer = {
    image(href, title, text) {
    return `<a href="${href}" target="_blank" data-fancybox="group" class="fancybox">
    <img speak-src="${href}" src=${loading_img} alt='${text}'>
    </a>`
    }
    }
    marked.setOptions({
    renderer: new marked.Renderer(),
    highlight: function (code) {
    if (hljs) {
    return hljs.highlightAuto(code).value
    } else {
    return code
    }
    },
    pedantic: false,
    gfm: true,
    tables: true,
    breaks: true,
    sanitize: false,
    smartLists: true,
    smartypants: false,
    xhtml: false
    })
    marked.use({ renderer })
    return marked.parse(body)
    }
    fetch(`${apiURL}/get/${speakId}`)
    .then(response => response.json())
    .then(res => {
    const data = res.data;
    if(data){
    const {title,content} = data;
    const contentSub = content.substring(0, 30);
    document.querySelector('.content').innerHTML = markedRender(content);
    if(title){
    document.title = title;
    }
    Waline.init({
    el: '.ispeak-comment',//不用改
    serverURL: '',//填写你的Waline服务端地址
    path: path + '?q=' + _id,//不用改
    //后面可按照https://waline.js.org/reference/client/props.html 修改(必须放在path后面!以下为一个例子)
    emoji: ["//unpkg.com/@waline/emojis@1.0.1/weibo","https://emoji.shojo.cn/bili/src/小黄脸","//unpkg.com/@waline/emojis@1.0.1/bilibili","https://emoji.shojo.cn/bili/src/枕边童话","https://emoji.shojo.cn/bili/src/咩栗","https://emoji.shojo.cn/bili/src/呜米","https://emoji.shojo.cn/bili/src/进击的冰糖","https://emoji.shojo.cn/bili/src/冰糖IO 蜕变·闪耀","https://emoji.shojo.cn/bili/src/多多poi","https://emoji.shojo.cn/bili/src/穆小泠","https://emoji.shojo.cn/bili/src/早稻叽"],// 表情包大全
    });
    }
    });
    </script>
  1. 在博客目录下运行hexo new page speaks
  2. 编辑[blogroot]\source\speaks\index.md,将里面的内容替换成:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    ---
    title: 说说
    comments: false
    aside: false
    ---
    {% raw %}

    <style>
    .speak-footer,.tk-footer{
    display:none;
    }
    </style>
    <div id="ispeak"></div>
    <link
    rel="stylesheet"
    href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css"
    />
    <link
    rel="stylesheet"
    href="https://npm.elemecdn.com/ispeak@4.4.0/style.css"
    />
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script src="https://cdn.staticfile.org/marked/2.0.0/marked.min.js"></script>
    <script src="https://npm.elemecdn.com/ispeak@4.4.0/ispeak.umd.js"></script>
    <script src="https://npm.elemecdn.com/twikoo"></script>
    <script>
    function load_ispeak() {
    setTimeout(function() {
    var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
    var src = 'https://cdn1.tianli0.top/npm/ispeak/ispeak.umd.js'
    var script = document.createElement('script')
    script.setAttribute('type','text/javascript')
    script.onload = function() {
    pjax_ispeak()
    }
    script.setAttribute('src', src)
    HEAD.appendChild(script)
    }, 1);
    };
    function pjax_ispeak() {
    if(!document.querySelectorAll("#ispeak")[0])return;
    ispeak
    .init({
    el: '#ispeak',//不用改
    api: '', //填写你kkapi地址(不是kkadmin后台地址!)
    author: '',//填写个人ID
    pageSize: 10,//每页speak显示的条数(建议保持默认,除非你要魔改css)
    loading_img: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif',//加载动画
    comment: function (speak) {
    const { _id, title, content } = speak
    const contentSub = content.substring(0, 30)
    twikoo.init({
    envId: '', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
    el: '.ispeak-comment', // 不用改
    //region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
    path: '/speak/info.html?q=' + _id, //不用改,除非你将上面的speak改为别的文件夹昵称
    lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
    })
    }
    });
    }
    load_ispeak();
    document.addEventListener('pjax:complete', function () {
    pjax_ispeak();
    });
    </script>
    <!--建议标注末尾链接-->
    <p style="width: 100%;text-align: end;font-size: .75em;color: #999;margin-top: 1em;">Powered by <a href="https://www.antmoe.com/speak/"><strong>iSpeak</strong></a> | Comment by <a href="https://twikoo.js.org/"><strong>Twikoo</strong></a></p>
    {% endraw %}
  3. 在[blogroot]\source\speaks\下新建info.md,内容为下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    ---
    title: Speak
    aside: false
    comments: false
    description: 欢迎来到SinzMise的日记,快来看看SinzMise分享了什么! # speak介绍
    ---
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css" />
    <div class='content'>
    <img src='https://bu.dusays.com/2022/05/01/626e88f349943.gif'>
    </div>
    {% btn '/speaks/',查看全部,far fa-hand-point-right,block center blue larger %}
    <hr />
    <div class='ispeak-comment'></div>
    <!-- JS -->
    <script src="https://npm.elemecdn.com/twikoo"></script>
    <script src="https://unpkg.com/marked@4.0.18/marked.min.js"></script>
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script>
    const searchParams = new URLSearchParams(window.location.search);
    const speakId = searchParams.get('q');
    const path = window.location.pathname;
    const apiURL = 'https://(你的api地址)/api/ispeak';
    const markedRender = (body, loading_img='https://bu.dusays.com/2022/05/01/626e88f349943.gif') => {
    const renderer = {
    image(href, title, text) {
    return `<a href="${href}" target="_blank" data-fancybox="group" class="fancybox">
    <img speak-src="${href}" src=${loading_img} alt='${text}'>
    </a>`
    }
    }
    marked.setOptions({
    renderer: new marked.Renderer(),
    highlight: function (code) {
    if (hljs) {
    return hljs.highlightAuto(code).value
    } else {
    return code
    }
    },
    pedantic: false,
    gfm: true,
    tables: true,
    breaks: true,
    sanitize: false,
    smartLists: true,
    smartypants: false,
    xhtml: false
    })
    marked.use({ renderer })
    return marked.parse(body)
    }
    fetch(`${apiURL}/get/${speakId}`)
    .then(response => response.json())
    .then(res => {
    const data = res.data;
    if(data){
    const {title,content} = data;
    const contentSub = content.substring(0, 30);
    document.querySelector('.content').innerHTML = markedRender(content);
    if(title){
    document.title = title;
    }
    twikoo.init({
    envId: '', // 腾讯云环境填 envId;Vercel 环境填地址(https://xxx.vercel.app)
    el: '.ispeak-comment', // 不用改
    //region: 'ap-guangzhou', // 环境地域,默认为 ap-shanghai,腾讯云环境填 ap-shanghai 或 ap-guangzhou;Vercel 环境不填
    path: path + '?q=' + speakId, // 不用改
    lang: 'zh-CN', // 用于手动设定评论区语言,支持的语言列表 https://github.com/imaegoo/twikoo/blob/main/src/client/utils/i18n/index.js
    })
    }
    });
    </script>
  1. 在博客目录下运行hexo new page speaks
  2. 编辑[blogroot]\source\speaks\index.md,将里面的内容替换成:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    ---
    title: 说说
    comments: false
    aside: false
    ---
    {% raw %}

    <style>
    .speak-footer,.D-footer{
    display:none;
    }
    </style>
    <div id="ispeak"></div>
    <link
    rel="stylesheet"
    href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css"
    />
    <link
    rel="stylesheet"
    href="https://npm.elemecdn.com/ispeak@4.4.0/style.css"
    />
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script src="https://cdn.staticfile.org/marked/2.0.0/marked.min.js"></script>
    <script src="https://npm.elemecdn.com/ispeak@4.4.0/ispeak.umd.js"></script>
    <script src="https://npm.elemecdn.com/discuss@latest/dist/discuss.js"></script>
    <script>
    function load_ispeak() {
    setTimeout(function() {
    var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
    var src = 'https://cdn1.tianli0.top/npm/ispeak/ispeak.umd.js'
    var script = document.createElement('script')
    script.setAttribute('type','text/javascript')
    script.onload = function() {
    pjax_ispeak()
    }
    script.setAttribute('src', src)
    HEAD.appendChild(script)
    }, 1);
    };
    function pjax_ispeak() {
    if(!document.querySelectorAll("#ispeak")[0])return;
    ispeak
    .init({
    el: '#ispeak',//不用改
    api: '', //填写你kkapi地址(不是kkadmin后台地址!)
    author: '',//填写个人ID
    pageSize: 10,//每页speak显示的条数(建议保持默认,除非你要魔改css)
    loading_img: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif',//加载动画
    comment: function (speak) {
    const { _id, title, content } = speak
    const contentSub = content.substring(0, 30)
    discuss.init({
    el: '.ispeak-comment',// 不用改
    serverURLs: '',//填写你的Discuss服务端地址
    path: '/speak/info.html?q=' + _id,//不用改,除非你将上面的speak改为别的文件夹昵称
    //后面可按照https://discuss.js.org/Quick-Start.html#%E5%AE%A2%E6%88%B7%E7%AB%AF-client 修改(必须放在path后面!以下为一个例子)
    ph: '千山万水总是情,给个评论行不行' ,//评论框占位符
    imgLoading: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif'//评论图片加载动画
    })
    }
    });
    }
    load_ispeak();
    document.addEventListener('pjax:complete', function () {
    pjax_ispeak();
    });
    </script>
    <!--建议标注末尾链接-->
    <p style="width: 100%;text-align: end;font-size: .75em;color: #999;margin-top: 1em;">Powered by <a href="https://www.antmoe.com/speak/"><strong>iSpeak</strong></a> | Comment by <a href="https://discuss.js.org/"><strong>Discuss</strong></a></p>
    {% endraw %}
  3. 在[blogroot]\source\speaks\下新建info.md,内容为下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    ---
    title: Speak
    aside: false
    comments: false
    description: 欢迎来到SinzMise的日记,快来看看SinzMise分享了什么! # speak介绍
    ---
    <!-- CSS -->
    <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css" />
    <div class='content'>
    <img src='https://bu.dusays.com/2022/05/01/626e88f349943.gif'>
    </div>
    {% btn '/speaks/',查看全部,far fa-hand-point-right,block center blue larger %}
    <hr />
    <div class='ispeak-comment'></div>
    <!-- JS -->
    <script src="https://npm.elemecdn.com/discuss@latest/dist/discuss.js"></script>
    <script src="https://unpkg.com/marked@4.0.18/marked.min.js"></script>
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script>
    const searchParams = new URLSearchParams(window.location.search);
    const speakId = searchParams.get('q');
    const path = window.location.pathname;
    const apiURL = 'https://(你的api地址)/api/ispeak'; //api地址
    const markedRender = (body, loading_img='https://bu.dusays.com/2022/05/01/626e88f349943.gif') => {
    const renderer = {
    image(href, title, text) {
    return `<a href="${href}" target="_blank" data-fancybox="group" class="fancybox">
    <img speak-src="${href}" src=${loading_img} alt='${text}'>
    </a>`
    }
    }
    marked.setOptions({
    renderer: new marked.Renderer(),
    highlight: function (code) {
    if (hljs) {
    return hljs.highlightAuto(code).value
    } else {
    return code
    }
    },
    pedantic: false,
    gfm: true,
    tables: true,
    breaks: true,
    sanitize: false,
    smartLists: true,
    smartypants: false,
    xhtml: false
    })
    marked.use({ renderer })
    return marked.parse(body)
    }
    fetch(`${apiURL}/get/${speakId}`)
    .then(response => response.json())
    .then(res => {
    const data = res.data;
    if(data){
    const {title,content} = data;
    const contentSub = content.substring(0, 30);
    document.querySelector('.content').innerHTML = markedRender(content);
    if(title){
    document.title = title;
    }
    discuss.init({
    el: '.ispeak-comment',// 不用改
    serverURLs: '',//填写你的Discuss服务端地址
    path: path + '?q=' + speakId,// 不用改
    //后面可按照https://discuss.js.org/Quick-Start.html#%E5%AE%A2%E6%88%B7%E7%AB%AF-client 修改(必须放在path后面!以下为一个例子)
    ph: '千山万水总是情,给个评论行不行' ,//评论框占位符
    imgLoading: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif'//评论图片加载动画
    })
    }
    });
    </script>
  1. 在博客目录下运行hexo new page speaks
  2. 编辑[blogroot]\source\speaks\index.md,将里面的内容替换成:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    ---
    title: 说说
    comments: false
    aside: false
    ---
    {% raw %}
    <style>
    .speak-footer,.atk-list-footer{
    display:none;
    }
    </style>
    <div id="ispeak"></div>
    <link
    rel="stylesheet"
    href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css"
    />
    <link
    rel="stylesheet"
    href="https://npm.elemecdn.com/ispeak@4.4.0/style.css"
    />
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script src="https://cdn.staticfile.org/marked/2.0.0/marked.min.js"></script>
    <script src="https://npm.elemecdn.com/ispeak@4.4.0/ispeak.umd.js"></script>
    <!-- CSS -->
    <link href="https://unpkg.com/artalk@2.3.4/dist/Artalk.css" rel="stylesheet" />
    <!-- JS -->
    <script src="https://unpkg.com/artalk@2.3.4/dist/Artalk.js"></script>
    <script>
    function load_ispeak() {
    setTimeout(function() {
    var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
    var src = 'https://cdn1.tianli0.top/npm/ispeak/ispeak.umd.js'
    var script = document.createElement('script')
    script.setAttribute('type','text/javascript')
    script.onload = function() {
    pjax_ispeak()
    }
    script.setAttribute('src', src)
    HEAD.appendChild(script)
    }, 1);
    };
    function pjax_ispeak() {
    if(!document.querySelectorAll("#ispeak")[0])return;
    ispeak
    .init({
    el: '#ispeak',//不用改
    api: '', //填写你kkapi地址(不是kkadmin后台地址!)
    author: '',//填写个人ID
    pageSize: 10,//每页speak显示的条数(建议保持默认,除非你要魔改css)
    loading_img: 'https://bu.dusays.com/2021/03/04/d2d5e983e2961.gif',//加载动画
    comment: function (speak) {
    const { _id, title, content } = speak
    const contentSub = content.substring(0, 30)
    new Artalk({
    el: '.ispeak-comment', // 不用改
    pageKey: '/speak/info.html?q=' + _id, //不用改,除非你将上面的speak改为别的文件夹昵称
    pageTitle: title || contentSub, // 不用改
    server: '', //填写你的Artalk服务端地址
    site: '' // 填写你的站点名
    })
    }
    });
    }
    load_ispeak();
    document.addEventListener('pjax:complete', function () {
    pjax_ispeak();
    });
    </script>
    <!--建议标注末尾链接-->
    <p style="width: 100%;text-align: end;font-size: .75em;color: #999;margin-top: 1em;">Powered by <a href="https://www.antmoe.com/speak/"><strong>iSpeak</strong></a> | Comment by <a href="https://artalk.js.org/"><strong>Artalk</strong></a></p>
    {% endraw %}
  3. 在[blogroot]\source\speaks\下新建info.md,内容为下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    ---
    title: Speak
    aside: false
    comments: false
    description: 欢迎来到SinzMise的日记,快来看看SinzMise分享了什么! # speak介绍
    ---
    <!-- CSS -->
    <link href="https://unpkg.com/artalk@2.3.4/dist/Artalk.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.6.0/styles/atom-one-dark.min.css" />
    <div class='content'>
    <img src='https://bu.dusays.com/2022/05/01/626e88f349943.gif'>
    </div>
    {% btn '/speaks/',查看全部,far fa-hand-point-right,block center blue larger %}
    <hr />
    <div class='ispeak-comment'></div>
    <!-- JS -->
    <script src="https://unpkg.com/artalk@2.3.4/dist/Artalk.js"></script>
    <script src="https://unpkg.com/marked@4.0.18/marked.min.js"></script>
    <script src="https://cdn.staticfile.org/highlight.js/10.6.0/highlight.min.js"></script>
    <script>
    const searchParams = new URLSearchParams(window.location.search);
    const speakId = searchParams.get('q');
    const path = window.location.pathname;
    const apiURL = 'https://(你的api地址)/api/ispeak';
    const markedRender = (body, loading_img='https://bu.dusays.com/2022/05/01/626e88f349943.gif') => {
    const renderer = {
    image(href, title, text) {
    return `<a href="${href}" target="_blank" data-fancybox="group" class="fancybox">
    <img speak-src="${href}" src=${loading_img} alt='${text}'>
    </a>`
    }
    }
    marked.setOptions({
    renderer: new marked.Renderer(),
    highlight: function (code) {
    if (hljs) {
    return hljs.highlightAuto(code).value
    } else {
    return code
    }
    },
    pedantic: false,
    gfm: true,
    tables: true,
    breaks: true,
    sanitize: false,
    smartLists: true,
    smartypants: false,
    xhtml: false
    })
    marked.use({ renderer })
    return marked.parse(body)
    }
    fetch(`${apiURL}/get/${speakId}`)
    .then(response => response.json())
    .then(res => {
    const data = res.data;
    if(data){
    const {title,content} = data;
    const contentSub = content.substring(0, 30);
    document.querySelector('.content').innerHTML = markedRender(content);
    if(title){
    document.title = title;
    }
    new Artalk({
    el: '.ispeak-comment', // 不用改
    pageKey: path + '?q=' + speakId, // 不用改
    pageTitle: title || contentSub, // 不用改
    server: '', //填写你的Artalk服务端地址
    site: '' // 填写你的站点名
    })
    }
    });
    </script>