子比主题添加朋友圈和收藏栏

子比主题添加朋友圈和收藏栏

概述

原版教程:https://gavin-chen.top/post/602922a7.html

适配友链朋友圈收藏栏,让wordpress朋友圈有收藏功能

图片[1]-子比主题添加朋友圈和收藏栏-瀚星阁

详细效果请前往 朋友圈 页面查看。

关于收藏栏的后端部分,这里采用的是Github+Supabase+Vercel的方案,另外配有缓存机制,首次进入页面调用云端数据库的数据,重新进入页面则调用本地缓存,缓存设有 1 天的有效期,过期后会自动拉取云端数据库以更新本地缓存。另外为实现多端同步,在收藏栏右上角设有刷新按钮,可以实现一键同步。收藏栏默认显示两行,多余部分折叠隐藏,可点击右上角按钮展开全部内容。

朋友圈配置

这个文章收藏是在友链朋友圈的基础上延伸出来的一个附加功能,所以基础是要先配置好友链朋友圈功能。朋友圈我是按照 官方文档 配置的,其中后端部分采用的是Github+Sqlite+Vercel的 无服务器部署 方式,前端部分采用的是 林木木 的方案。林木木的方案不带管理面板,如果需要更改设置的话,会比较麻烦,但优点是代码量少,简短易读,方便我二次修改。

林木木的朋友圈方案配有一个顶栏,可以显示“订阅”、“活跃”、“日志”等消息,点击“订阅”可以查看随机一个友链的文章列表,点击“活跃”可以切换私有友链库和公共库两种数据源,点击“日志”可以手动清空本地缓存,重新拉取云端数据库。另外支持“创建日期”和“更新日期”两种排列方式,可以自由手动切换。

数据库配置

Vercel 的 Integrations Marketplace 上提供有很多种数据库,其中 MongoDB Atlas 之前在部署 twikoo 评论的时候用过,Upstash 在配置个人网盘的时候用过,听群友推荐说 PlanetScale 和 Supabase 也不错。这里采用 Supabase 作为项目云端数据存储。

图片[2]-子比主题添加朋友圈和收藏栏-瀚星阁

注册 Supabase 后进入 Dashboard,首先点击 New project 创建一个新的项目,设置项目名称和数据库密码,选择合适的地区,然后确认创建。整个创建过程有点长,需要耐心等待几十秒的时间。

图片[3]-子比主题添加朋友圈和收藏栏-瀚星阁

创建完成会自动进入项目,页面左侧有一条纵向菜单栏,点击 Table Editor 可以对数据库进行可视化操作,点击 SQL Editor 可以进行命令行操作,这对于小白来说还是非常方便的。我们进入 Table Editor 点击 New table 创建一个新数据表(建议数据表名字设置为Subscribe),设置好表名、描述,最重要的是各列的参数配置,默认的 Column 有 id 和 created_at 两列,点击 Add column 新增几条参数,分别是 index、title、link、author、avatar、time 六个参数,参数类型均设置为 text,点击 save 保存。

图片[4]-子比主题添加朋友圈和收藏栏-瀚星阁
图片[5]-子比主题添加朋友圈和收藏栏-瀚星阁

进入数据表,点击 Insert 可以手动插入数据,因为是可视化界面,增删改查都十分直观。当然我们不需要在这里插入数据,而是要能够在外部调用它的增删改查能力。在左侧菜单栏中点击 API Docs 可以查看详细的使用方法,其中,Introduction 栏提供了初始化代码。

import { createClient } from "@supabase/supabase-js";
const supabaseUrl = "https://xxxxxxxxxxxxx.supabase.co";
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

这里有两个重要参数supabaseUrl和supabaseKey,需要在设置里面查找。点击左侧菜单栏中的 Project Settings,点击 API 栏,其中 Project URL 即为 supabaseUrl,Project API keys 即为 supabaseKey。

图片[6]-子比主题添加朋友圈和收藏栏-瀚星阁

回到 API Docs,在 Tables and Views 栏找到刚刚新建的表名,进入文档后可以看到详细的增删改查示例代码。

图片[7]-子比主题添加朋友圈和收藏栏-瀚星阁

后端代码

github 新建一个私有仓库,仓库内主要包含 3 个文件,内容如下。

index.js

注意这里 supabaseUrl 做了脱敏处理,要替换成自己的。

// 导入所需包和模块
const express = require("express");
require("dotenv").config(); // 导入 dotenv 并加载 .env 文件
const app = express();
app.use(function (req, res, next) {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
  next();
});
// 创建 Express 应用程序
app.use(express.json());
const savedKey = process.env.KEY;
// 连接数据库
const { createClient } = require("@supabase/supabase-js");
const supabaseUrl = "https://xxxxxxxxxxxxxxxxx.supabase.co";
const supabaseKey = process.env.SUPABASE_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

// 新增收藏
app.get("/subscribe", async (req, res) => {
  try {
    const key = req.query.key.toString();
    const index = req.query.index.toString();
    const title = req.query.title.toString();
    const link = req.query.link.toString();
    const author = req.query.author.toString();
    const avatar = req.query.avatar.toString();
    const time = req.query.time.toString();
    if (key == savedKey) {
      // 存入数据库
      const { data: filterData, error: error1 } = await supabase
        .from("Subscribe")
        .select()
        .eq("index", index);
      if (error1) {
        console.error("Error:", error1);
        res.status(500).json({ code: "500", message: "查询失败", content: "" });
      } else {
        if (!filterData.length) {
          const { data: createdData, error: error2 } = await supabase
            .from("Subscribe")
            .insert([
              {
                index: index,
                title: title,
                link: link,
                author: author,
                avatar: avatar,
                time: time,
              },
            ])
            .select();
          if (error2) {
            console.error("Error:", error2);
            res
              .status(500)
              .json({ code: "500", message: "存储失败", content: "" });
          } else {
            console.log("Data stored successfully:", createdData[0]);
            res.status(200).json({
              code: "200",
              message: "存储成功",
              content: createdData[0],
            });
          }
        } else {
          res
            .status(402)
            .json({ code: "402", message: "文章已存在", content: "" });
        }
      }
    } else {
      res.status(401).json({ code: "401", message: "密码错误", content: "" });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// 文章删除
app.get("/delsavedtitles", async (req, res) => {
  try {
    const key = req.query.key.toString();
    const index = req.query.index.toString();
    if (key == savedKey) {
      const { data, error } = await supabase
        .from("Subscribe")
        .delete()
        .eq("index", index);
      if (error) {
        console.error("Error:", error);
        res.status(500).json({ code: "500", message: "删除失败", content: "" });
      } else {
        if (!data.length) {
          res
            .status(404)
            .json({ code: "404", message: "未找到文章", content: data });
        } else {
          console.log("Data delete completely");
          res
            .status(200)
            .json({ code: "200", message: "删除成功", content: data });
        }
      }
    } else {
      res.status(401).json({ code: "401", message: "密码错误", content: "" });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// 收藏文章查询
app.get("/getsavedtitles", async (req, res) => {
  try {
    const mode = req.query.mode.toString();
    const column = req.query.column.toString();
    const value = req.query.value.toString();
    if (mode == "search") {
      const { data: filterData, error } = await supabase
        .from("Subscribe")
        .select()
        .eq(column, value);
      if (error) {
        console.error("Error:", error);
        res.status(404).json({ code: "404", message: "查询失败", content: "" });
      } else {
        console.log("Data serach completely:", filterData);
        res
          .status(200)
          .json({ code: "200", message: "查询成功", content: filterData });
      }
    } else if (mode == "all") {
      const { data: filterData, error } = await supabase
        .from("Subscribe")
        .select();
      if (error) {
        console.error("Error:", error);
        res.status(404).json({ code: "404", message: "查询失败", content: "" });
      } else if (mode == "all") {
        console.log("Data serach completely:", filterData);
        res
          .status(200)
          .json({ code: "200", message: "查询成功", content: filterData });
      }
    } else {
      res.status(401).json({ code: "401", message: "参数错误", content: "" });
    }
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// 启动服务器
const server = app.listen(process.env.PORT || 3000, () => {
  const port = server.address().port;
  console.log(`Server is running on port ${port}`);
});

package.json

{
  "name": "projectname",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "Echo \"error: error\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "axios": "^0.27.2",
    "express": "^4.18.1",
    "dotenv": "^8.2.0",
    "@supabase/supabase-js": "^1.0.0"
  }
}

vercel.json

注意 KEY 和 SUPABASE_KEY 要换成自己的,其中KEY为 SHA256 加密后的前端身份验证密码,SUPABASE_KEY为 Supabase 密钥

{
  "version": 2,
  "builds": [
    {
      "src": "./index.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "/"
    }
  ],
  "env": {
    "KEY": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "SUPABASE_KEY": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}

仓库建好后,部署到 Vercel 上,在 Storage 栏进入 Integrations Marketplace 找到 Supabase,点击 Add Integration 将其添加进项目。然后就是配置自定义域名,自此后端配置完成。

如果vercel添加supabase storage出现错误,导致无法正常使用那么可以开启RLS功能

图片[8]-子比主题添加朋友圈和收藏栏-瀚星阁
图片[9]-子比主题添加朋友圈和收藏栏-瀚星阁

前端代码

moments.php

原版教程的是html代码,但是在wordpress的页面上添加html代码,无法跟效果图一样

<?php

/**
 * Template name: 安凝雪科技-朋友圈
 * Description:   安凝雪科技-朋友圈
 */

get_header();
$header_style = zib_get_page_header_style();
?>
<style>
    .container {
        max-width: var(--mian-max-width);
        width: 1200px!important;
    }
    #head-cover {
        width: 1200px;
        height: 350px;
        margin: 0 0 20px;
        border: 1px solid var(--gavin-border-color);
        box-shadow: var(--gavin-shadow-2);
        max-width: var(--mian-max-width);
        left:120px;
        border-radius: 20px;
        overflow: hidden;
        position: relative;
    }
.head-cover-descr {
    position: absolute;
    bottom: 40px;
    left: 40px;
    line-height: 1.3;
    font-size: 1.15rem;
    color: #f5f5f5;
    text-shadow: 0 0 2px rgba(0,0,0,.5);
    max-width: calc(100% - 50px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    z-index: 1;
}
.head-cover-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.head-cover-title {
    position: absolute;
    top: 40px;
    left: 40px;
    color: #fff;
    font-size: 2.5rem;
    font-weight: 700;
    line-height: 1;
    text-shadow: 0 0 10px rgba(0,0,0,.5);
    z-index: 1;
}
    #head-cover, header a, header img, header nav, header span, header time {
        user-select: none;
    }
</style>
<div id="head-cover"><img class="head-cover-img entered loaded" src="https://i3.wp.com/file.77il.cn/wp-content/uploads/2023/04/88a992dcfe211559.jpg" data-lazy-src="https://i3.wp.com/file.77il.cn/wp-content/uploads/2023/04/88a992dcfe211559.jpg" alt="cover" data-ll-status="loaded"><span class="head-cover-title">订阅</span><span class="head-cover-descr">使用 友链朋友圈 订阅友链最新文章</span></div>
<main class="container"></div>
<h2 id="我的收藏">
    我的收藏
    <a class="refresh" onclick="localStorage.removeItem('savedArticles');location.reload();">
        <i class="blogfont icon-refresh"></i>
    </a>
    <a class="fold" onclick="foldSavedArticles(event)">
        <i class="blogfont icon-arrow-down"></i>
    </a>
</h2>

<div id="cf-saved-post">
</div>

<h2 id="最新文章">
    最新文章
</h2>

<div class="post-content">
    <div id="cf-container">
        与主机通讯中……
    </div>
</div>

<div class="inputBoxMain" id="fcircleInputBox">
  <div class="inputBox">
    <div class="content-body">
      <span class="title"></span>
      <span class="tips">请输入密码以验证您的身份</span>
      <input class="input-password" type="password" placeholder="密码">
    </div>
    <div class="content-bottom">
      <span class="btn close" onclick="switchSecretInput()">取消</span>
      <span class="btn" onclick="sendSubscribeInfo()">确认</span>
    </div>
  </div>
  <div class="noteBox hide">
    <a class="icon">
      <i class="fas fa-spinner fa-spin"></i>
    </a>
    <span class="tips">请稍候 ...</span>
  </div>
  <div class="inputBoxMask">
  </div>
</div>

<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/crypto-js/4.1.1/crypto-js.min.js"></script>
<link rel="stylesheet" href="/anxkj/css/link.css">
<link rel="stylesheet" href="https://npm.elemecdn.com/element-ui@2.15.6/lib/theme-chalk/index.css"  media="defer" onload="this.media='all'">
<script async src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js"></script>
<script async src="https://npm.elemecdn.com/element-ui@2.15.6/lib/index.js"></script>
<body>
<script src="/anxkj/js/moments.js"></script>
<script src="/anxkj/js/sc.js"></script>
</body>
<?php the_content( $more_link_text, $strip_teaser, $more_file ); ?>
<?php comments_template('/template/comments.php', true); ?>
    </div>
</main>
<?php
get_footer();

link.css

这里使用得是铭心石刻的css,这边调整了一部分代码(颜色代码的失效)

:root {
	--kouseki-floorcolor: #a9a9b3;
	--kouseki-dark-floorcolor: #454545
}[data-theme=light] {
	--kouseki-bg-3: rgb(248,248,248)
}[data-theme=dark] {
	--kouseki-bg-3: rgb(7,7,7)
}

#article-container a:not([data-fancybox=gallery]):not(.headerlink):not(.cf-friends-link):not(.tag-Link):not(.btn-anzhiyu):not(.no-text-decoration) {
	border-bottom: none;
	font-family: zzht
}

#article-container a:not([data-fancybox=gallery]):not(.headerlink):not(.cf-friends-link):not(.btn-anzhiyu):not(.no-text-decoration):hover {
	background: 0 0;
	color: var(--anzhiyu-main)
}

#cf-state {
	font-size: 16px;
	border-radius: .8rem;
	border: 1px solid var(--kouseki-card-border);
	box-shadow: none;
	margin: 0 0 20px;
	overflow: hidden;
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	padding: 8px 0
}

.cf-state-data {
	width: 100%;
	display: flex;
	justify-content: space-evenly;
	margin-top: 10px
}

.cf-data-active,.cf-data-article,.cf-data-friends {
	height: 60px;
	background: 0 0;
	display: flex;
	flex-direction: column;
	cursor: pointer;
	font-weight: 700
}

.cf-data-active:hover,.cf-data-article:hover,.cf-data-friends:hover {
	color: var(--kouseki-lighttext)
}

.cf-label {
	font-size: 16px;
	padding: 0;
	align-self: center;
	text-align: center;
	width: 100%;
	height: 30px
}

.cf-message {
	align-self: center;
	text-align: center;
	padding: 0;
	width: 100%;
	font-size: 20px
}

#cf-change {
	font-size: 14px;
	display: block;
	padding: 12px 0 4px;
	width: 100%;
	text-align: center
}

#cf-more {
	width: 60%;
	height: 30px;
	margin: 20px auto 0;
	border-radius: 50px;
	border: 1px solid rgb(227, 232, 240);
	font-weight: bolder;
	text-align: center;
	display: flex;
	flex-direction: column;
	justify-content: space-around;
	cursor: pointer;
	transition: .3s
}

#cf-more:hover {
	width: 100%;
	background: var(--kouseki-lighttext);
	color: var(--f-0)
}

#cf-more i.fas::before {
	content: "∞"
}

#cf-container {
	width: 100%;
	height: auto;
	margin: auto;
	display: flex;
	flex-wrap: wrap;
	gap: 20px
}

#cf-container a {
	text-decoration: none
}

.cf-article {
	border-radius: .8rem;
	border: 1px solid rgb(227, 232, 240);
	font-weight: bolder;
	overflow: hidden;
	transition: all ease-out .3s;
	position: relative;
	padding: .5rem 1rem;
	width: calc(100% / 3 - 40px / 3);
	float: left;
	height: 110px
}

#cf-container>.cf-article:hover {
	transition: all .3s;
	-webkit-transform: scale(1.03);
	transform: scale(1.03);
	border-color: var(--kouseki-lighttext)
}

.cf-article:hover {
	border-color: var(--kouseki-lighttext)
}

.cf-article::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 200%;
	background: linear-gradient(to right,transparent,#fff,transparent);
	transform: translateX(-200%);
	transition: transform .5s linear;
	z-index: 1
}

.cf-article:hover::before {
	transform: translateX(100%) skewX(-60deg)
}

.cf-article-avatar {
	display: flex;
	align-items: center;
	position: absolute;
	bottom: 8px;
	left: 15px
}

.cf-img-avatar {
	align-self: center;
	text-align: center;
	display: inline-block!important;
	width: 22px;
	height: 22px;
	border-radius: 50%!important;
	margin: 0 0 -4px!important;
	background: #fff
}

.cf-article-author {
	line-height: 35px;
	font-size: 14px;
	font-weight: 400;
	margin-left: 5px;
	align-self: center;
	text-align: center;
	height: 40px;
	white-space: nowrap;
	overflow: hidden
}

.cf-article-title {
	color: var(--kouseki-fontcolor)!important;
	font-weight: 500;
	position: relative;
	z-index: 2;
	width: fit-content;
	max-width: calc(100% - 30px);
	display: -webkit-box;
	font-size: 18px;
	align-self: start;
	text-align: left;
	line-height: 1.3;
	padding: 0;
	margin: 5px 0;
	transition: .3s;
	overflow: hidden;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2
}

.cf-article-time {
	font-size: 14px;
	text-align: right;
	float: right;
	font-weight: 400;
	position: absolute;
	right: 17px;
	bottom: 17px;
	line-height: 1
}

.cf-time-created,.cf-time-updated {
	display: inline-block;
	text-align: left;
	white-space: nowrap
}

.cf-time-created i.far,.cf-time-updated i.fas {
	padding-right: 8px
}

.cf-article-time i:before {
	margin-right: 5px
}

#cf-footer {
	margin: 6rem 1% 2rem 0;
	text-align: right;
	font-size: 13px;
	color: var(--kouseki-note-font-color)
}

.cf-data-lastupdated {
	font-size: 13px;
	text-align: right;
	display: block
}

#cf-overlay,#cf-overshow {
	position: fixed;
	width: 100%;
	height: 100%
}

#cf-overlay {
	top: 0;
	left: 0;
	background-color: rgba(255,255,255,.12);
	-webkit-backdrop-filter: blur(0);
	backdrop-filter: blur(0);
	overflow-y: auto;
	pointer-events: none;
	opacity: 0;
	transition: all .5s ease;
	z-index: 998
}

#cf-overshow {
	bottom: 0;
	left: 0;
	opacity: 0;
	pointer-events: none;
	transition: all .5s ease;
	z-index: 999
}

#cf-overlay.cf-show-now {
	left: 0;
	-webkit-backdrop-filter: blur(7px);
	backdrop-filter: blur(7px);
	opacity: 1;
	transition: all .5s ease
}

#cf-overshow.cf-show-now {
	bottom: 0;
	opacity: 1;
	pointer-events: all;
	transition: all .5s ease
}

.cf-overshow {
	text-align: center;
	border-radius: 1rem;
	position: absolute;
	width: 290px;
	padding: 0 10px;
	min-height: 170px;
	left: 50%;
	top: 50%;
	transform: translate(-50%,-50%);
	box-shadow: 0 12px 40px rgba(0,0,0,.093);
	background: var(--kouseki-card-bg-about);
	border: 1px solid var(--kouseki-border-color)
}

.cf-overshow-head:hover img.cf-img-avatar {
	transform: rotate(360deg);
	transition: .8s
}

.cf-overshow .cf-overshow-head a {
	color: var(--dis-f-0)!important;
	display: block;
	text-align: center;
	font-weight: 700;
	margin-top: -5px;
	padding: 5px 8px 5px
}

.cf-overshow img.cf-img-avatar {
	background: #fff;
	width: 80px;
	height: 80px;
	border-radius: 50%!important;
	margin: 20px auto 0!important;
	box-shadow: 0 12px 40px rgb(0 0 0 / 9%);
	transform: rotate(-360deg);
	transition: .3s!important
}

.cf-overshow p {
	margin: .3rem 5px;
	width: 100%;
	position: relative
}

.cf-overshow p a.cf-article-title {
	display: block;
	text-align: left;
	position: relative;
	z-index: 2;
	font-size: 15px;
	font-weight: 600;
	line-height: 25px;
	margin-bottom: 15px;
	letter-spacing: normal;
	max-height: 50px;
	overflow: hidden;
	white-space: normal;
	text-overflow: ellipsis;
	display: -webkit-box;
	display: box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical
}

.cf-overshow p span {
	position: absolute;
	bottom: -1rem;
	right: 8px;
	z-index: 1;
	font-style: italic;
	font-size: 12px
}

#cf-container .cf-overshow p a:hover {
	letter-spacing: 1px;
	transition: .3s
}

.cf-overshow .cf-overshow-content {
	padding: 10px 10px 25px;
	border-bottom-left-radius: 20px;
	border-bottom-right-radius: 20px
}

#cf-overshow .cf-overshow-close {
	position: sticky;
	display: block;
	width: 100%;
	height: 100%
}

.cf-article .cf-article-title:hover {
	color: var(--kouseki-lighttext)!important
}

#cf-more i.fas:hover {
	color: #fff;
	transition: .5
}

#cf-more,#cf-state {
	background: #fff;
	color: #363636
}

#cf-change,.cf-article-floor,.cf-time-created,.cf-time-updated {
	color: var(--kouseki-floorcolor)
}

.cf-article a.cf-article-title,.cf-article-author,.cf-article:hover .cf-time-created,.cf-article:hover .cf-time-updated {
	color: var(--kouseki-fontcolor)
}

.cf-article {
    background: #fff;
    box-shadow: 0 8px 12px -4px #2c2d300c;
}

#cf-change span:hover {
	color: var(--kouseki-lighttext);
	cursor: pointer
}

#cf-change .cf-change-now {
	color: rgb(129,78,250);
	font-weight: 800
}

.cf-overshow p a:hover {
	color: var(--kouseki-blue2)!important;
	text-decoration: none!important
}

.cf-overshow p span {
	color: var(--kouseki-note-font-color)
}

.dark-theme #cf-overlay,.theme-dark #cf-overlay {
	background-color: rgba(59,61,66,.42)
}

.dark-theme .cf-overshow,.theme-dark .cf-overshow {
	background: #292a2d
}

.dark-theme .cf-overshow p a,.theme-dark .cf-overshow p a {
	color: var(--kouseki-fontcolor)
}

.dark-theme .cf-overshow .cf-overshow-content,.theme-dark .cf-overshow .cf-overshow-content {
	background: #eaeaea
}

.dark-theme #cf-more,.dark-theme #cf-state,.theme-dark #cf-more,.theme-dark #cf-state {
	background: var(--kouseki-card-bg-about);
	color: var(--kouseki-dark-fontcolor)
}

.dark-theme #cf-change,.dark-theme .cf-article-floor,.dark-theme .cf-time-created,.dark-theme .cf-time-updated,.theme-dark #cf-change,.theme-dark .cf-article-floor,.theme-dark .cf-time-created,.theme-dark .cf-time-updated {
	color: var(--kouseki-dark-floorcolor)
}

.dark-theme .cf-article a.cf-article-title,.dark-theme .cf-article-author,.theme-dark .cf-article a.cf-article-title,.theme-dark .cf-article-author {
	color: var(--kouseki-dark-fontcolor)
}

.dark-theme .cf-article,.theme-dark .cf-article {
	background: var(--kouseki-card-bg-about)
}

.dark-theme .cf-article:hover .cf-article-floor,.dark-theme .cf-article:hover .cf-time-created,.dark-theme .cf-article:hover .cf-time-updated,.dark-theme .cf-overshow p span,.theme-dark .cf-article:hover .cf-article-floor,.theme-dark .cf-article:hover .cf-time-created,.theme-dark .cf-article:hover .cf-time-updated,.theme-dark .cf-overshow p span {
	color: var(--kouseki-dark-fontcolor)
}

#cf-container .img-alt.is-center {
	display: none
}

#article-container a:hover {
	text-decoration: none
}

span.cf-article-author:hover {
	color: var(--kouseki-lighttext)!important
}

div#cf-footer a {
	color: var(--dis-f-0)
}

.cf-overshow-head {
	margin: 0 10px;
	border-bottom: 1px dashed var(--kouseki-note-font-color)
}

div#cf-random-post {
	background: var(--kouseki-card-bg-about);
	border: 1px solid var(--kouseki-border-color);
	border-radius: .8rem;
	height: 4rem;
	margin: 0 10px;
	display: flex;
	align-items: center;
	padding: 15px;
	color: var(--kouseki-fontcolor);
	font-size: 1.1rem;
	height: fit-content
}

.cf-overshow>.cf-overshow-content>p {
	margin: 0 0 2rem!important
}

.cf-overshow .cf-overshow-head a:hover {
	text-decoration: none!important;
	color: var(--kouseki-blue2)!important
}

#cf-footer a:hover {
	text-decoration: none;
	color: var(--kouseki-blue2)
}

.cf-star {
	color: var(--kouseki-fontcolor)!important;
	width: 35px;
	height: 35px;
	font-size: 18px;
	position: absolute;
	right: 8px;
	top: 8px;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer
}

.cf-star:hover {
	color: var(--kouseki-lighttext)!important
}

.cf-star.saved i {
	font-weight: 900;
	color: orange
}

#fcircleInputBox {
	display: flex;
	position: fixed;
	width: 100vw;
	height: 100vh;
	top: 0;
	left: 0;
	z-index: 999;
	justify-content: center;
	transition: .5s ease;
	flex-direction: column;
	align-items: center;
	pointer-events: none
}

#fcircleInputBox.open {
	pointer-events: all
}

#fcircleInputBox.open .inputBox.hide,.inputBox {
	width: 300px;
	height: 200px;
	background: rgb(248,248,248);
	border: 1px solid rgb(227, 232, 240);
	border-radius: 18px;
	overflow: hidden;
	position: absolute;
	opacity: 0;
	pointer-events: none;
	transition: .5s ease
}

#fcircleInputBox.open .inputBox {
	opacity: 1;
	pointer-events: all;
	transition: .5s ease
}

.content-body {
	width: 100%;
	height: 145px;
	padding: 20px;
	position: relative
}

.content-body span.title {
	display: block;
	width: 100%;
	font-size: 2rem;
	font-weight: 700;
	line-height: 1.3;
	text-align: center;
	margin: 0 0 8px
}

.content-body span.tips {
	display: block;
	width: 100%;
	font-size: 1.2rem;
	line-height: 1.3;
	text-align: center;
	color: rgb(102, 102, 102)
}

.content-body input.input-password {
	display: block;
	line-height: 1.3;
	width: 260px;
	border-radius: 6px;
	height: 35px;
	position: absolute;
	bottom: 10px;
	left: 20px;
	padding: 4px 8px;
	color: var(--font-color);
	background: var(--f-0);
	border: 1px solid var(--kouseki-border-color)
}

.content-body input.input-password:focus {
	outline: 0
}

.content-body input.input-password::placeholder {
	color: var(--kouseki-note-font-color)
}

.content-bottom {
	width: 100%;
	height: 55px;
	border-top: 1px solid var(--kouseki-border-color);
	display: flex
}

.content-bottom .btn {
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	letter-spacing: 1px;
	font-size: 1.1rem;
	color: rgb(0, 136, 255);
	cursor: pointer
}

.content-bottom .btn:first-child {
	border-right: 1px solid rgb(227, 232, 240)
}

.content-bottom .btn:hover {
	background-color: var(--kouseki-op-dis-1)
}

.inputBoxMask {
	background: var(--kouseki-console-mask);
	-webkit-backdrop-filter: blur(0);
	backdrop-filter: blur(0);
	width: 100vw;
	height: 100vh;
	top: 0;
	left: 0;
	position: absolute;
	z-index: -1;
	opacity: 0;
	transition: .5s ease;
	-webkit-backface-visibility: hidden;
	backface-visibility: hidden;
	-webkit-transform-style: preserve-3d;
	transform-style: preserve-3d;
	transform: translateZ(0)
}

#fcircleInputBox.open .inputBoxMask {
	-webkit-backdrop-filter: blur(7px);
	backdrop-filter: blur(7px);
	opacity: 1;
	transform: translateZ(0)
}

#fcircleInputBox .noteBox {
	width: 150px;
	height: 150px;
	background: var(--kouseki-bg-3);
	border: 1px solid var(--kouseki-border-color);
	border-radius: 15px;
	overflow: hidden;
	position: absolute;
	display: flex;
	justify-content: center;
	align-items: center;
	opacity: 1;
	pointer-events: none;
	transition: .5s ease
}

#fcircleInputBox .noteBox.hide {
	opacity: 0;
	transition: .5s ease
}

#fcircleInputBox .noteBox .icon {
	width: 80px;
	height: 80px;
	display: flex;
	align-items: center;
	justify-content: center;
	position: absolute;
	top: 20px;
	color: var(--font-color);
	font-size: 48px
}

#fcircleInputBox .noteBox .tips {
	position: absolute;
	bottom: 27px;
	line-height: 1.3;
	font-size: 1rem;
	color: var(--font-color);
	text-align: center
}

#cf-saved-post {
	display: flex;
	flex-wrap: wrap;
	gap: 20px;
	max-height: 240px;
	overflow: hidden;
	transition: .3s ease-in-out
}

#我的收藏 {
	display: flex;
	align-items: center
}

#我的收藏 .fold,#我的收藏 .refresh {
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 6px;
	width: 36px;
	height: 36px;
	position: absolute;
	cursor: pointer;
	color: rgb(45, 45, 45);
	border: 1px solid rgb(227, 232, 240);
	background-color: #fff;
	transition: .3s
}

#我的收藏 .refresh {
	right: 48px
}

#我的收藏 .fold {
	right: 0
}

#我的收藏 .fold:hover,#我的收藏 .refresh:hover {
	background-color: #fff;
	transition: .3s
}

#我的收藏 .refresh i {
	font-size: 20px
}

#我的收藏 .fold i {
	font-size: 12px
}

@media screen and (max-width:1200px) {
	.cf-article {
		width: calc(100% / 2 - 20px / 2)!important
	}
}

@media screen and (max-width:850px) {
	.cf-article {
		width: 100%!important
	}
}

@media screen and (max-width:500px) {
	#cf-more {
		width: 100%!important
	}
}

@media screen and (max-width:300px) {
	#cf-state,.cf-article-time {
		display: none
	}
}

moments.js

/*
Last Modified time : 20220326 15:38 by https://immmmm.com
已适配 FriendCircle 公共库和主库
*/

//默认数据
var fdata = {
  jsonurl: "",
  apiurl: "",
  apipublieurl: "https://moments.anxkj.top/", //默认公共库
  initnumber: 24, //首次加载文章数
  stepnumber: 12, //更多加载文章数
  article_sort: "created", //文章排序 updated or created
  error_img: "https://www.anxkj.top/wp-content/uploads/2023/12/1702785694-c4ce86c69b4e15c246cd8eaf35db1c71-150x150.jpg",
};
if ("undefined" != typeof fdataUser)
    for (var key in fdataUser)
        fdataUser[key] && (fdata[key] = fdataUser[key]);
var article_num = ""
  , sortNow = ""
  , UrlNow = ""
  , friends_num = ""
  , container = document.getElementById("cf-container") || document.getElementById("fcircleContainer")
  , localSortNow = localStorage.getItem("sortNow")
  , localUrlNow = localStorage.getItem("urlNow");
function loadStatistical(sdata) {
    article_num = sdata.article_num,
    friends_num = sdata.friends_num;
    var messageBoard = `\n    <div id="cf-state" class="cf-new-add">\n      <div class="cf-state-data">\n        <div class="cf-data-friends" onclick="openToShow()">\n          <span class="cf-label">订阅</span>\n          <span class="cf-message">${sdata.friends_num}</span>\n        </div>\n        <div class="cf-data-active" onclick="changeEgg()">\n          <span class="cf-label">活跃</span>\n          <span class="cf-message">${sdata.active_num}</span>\n        </div>\n        <div class="cf-data-article" onclick="clearLocal()">\n          <span class="cf-label">日志</span>\n          <span class="cf-message">${sdata.article_num}</span>\n        </div>\n      </div>\n      <div id="cf-change">\n          <span id="cf-change-created" data-sort="created" onclick="changeSort(event)" class="${"created" == sortNow ? "cf-change-now" : ""}">Created</span> | <span id="cf-change-updated" data-sort="updated" onclick="changeSort(event)" class="${"updated" == sortNow ? "cf-change-now" : ""}" >Updated</span>\n      </div>\n    </div>\n    `
      , loadMoreBtn = `\n      <div id="cf-more" class="cf-new-add" onclick="loadNextArticle()"><i class="fas fa-angle-double-down"></i></div>\n      <div id="cf-footer" class="cf-new-add">\n       <span id="cf-version-up" onclick="checkVersion()"></span>\n       <span class="cf-data-lastupdated">更新于:${sdata.last_updated_time}</span>\n        Powered by <a target="_blank" href="https://github.com/Rock-Candy-Tea/hexo-circle-of-friends" target="_blank">FriendCircle</a>\n        <br>\n        Designed by <a target="_blank" href="https://immmmm.com" target="_blank">林木木</a>\n        <br>\n        Adapted by <a target="_blank" href="https://gavin-chen.top" target="_blank">南方嘉木</a>\n      </div>\n      <div id="cf-overlay" class="cf-new-add" onclick="closeShow()"></div>\n      <div id="cf-overshow" class="cf-new-add"></div>\n    `;
    container && (container.insertAdjacentHTML("beforebegin", messageBoard),
    container.insertAdjacentHTML("afterend", loadMoreBtn))
}
function loadArticleItem(datalist, start, end) {
    var articleItem = ""
      , articleNum = article_num
      , endFor = end;
    if (end > article_num && (endFor = article_num),
    start < article_num) {
        for (var i = start; i < endFor; i++) {
            var item = datalist[i], id;
            articleItem += `\n        <div class="cf-article ${"cf-" + CryptoJS.MD5(item.link).toString()}">\n            <a class="cf-article-title" href="${item.link}" target="_blank" rel="noopener nofollow" data-title="${item.title}">${item.title}</a>\n            <a class="cf-star" onclick="switchSecretInput(event)"><i class="fa fa-star"></i></a>\n            <div class="cf-article-avatar no-lightbox flink-item-icon">\n                <img class="cf-img-avatar avatar" src="${item.avatar}" alt="avatar" onerror="this.src='${fdata.error_img}'; this.onerror = null;">\n                <a onclick="openMeShow(event)" data-link="${item.link}" class="" target="_blank" rel="noopener nofollow" href="javascript:;"><span class="cf-article-author">${item.author}</span></a>\n            </div>\n            <span class="cf-article-time">\n                <span class="cf-time-created" style="${"created" == sortNow ? "" : "display:none"}">${item.created}</span>\n                <span class="cf-time-updated" style="${"updated" == sortNow ? "" : "display:none"}">${item.updated}</span>\n            </span>\n        </div>\n        `
        }
        container.insertAdjacentHTML("beforeend", articleItem),
        null != savedArticlesIndex && checkStared(savedArticlesIndex),
        fetchNextArticle()
    } else
        document.getElementById("cf-more").outerHTML = '<div id="cf-more" class="cf-new-add" onclick="loadNoArticle()"><small>一切皆有尽头!</small></div>'
}
function loadFcircleShow(userinfo, articledata) {
    for (var showHtml = `\n        <div class="cf-overshow">\n          <div class="cf-overshow-head">\n            <img class="cf-img-avatar avatar" src="${userinfo.avatar}" alt="avatar" onerror="this.src='${fdata.error_img}'; this.onerror = null;">\n            <a class="" target="_blank" rel="noopener nofollow" href="${userinfo.link}">${userinfo.name}</a>\n          </div>\n          <div class="cf-overshow-content">\n    `, i = 0; i < userinfo.article_num; i++) {
        var item = articledata[i];
        showHtml += `\n        <p><a class="cf-article-title"  href="${item.link}" target="_blank" rel="noopener nofollow" data-title="${item.title}">${item.title}</a><span>${item.created}</span></p>\n      `
    }
    showHtml += "</div></div>",
    document.getElementById("cf-overshow").insertAdjacentHTML("beforeend", showHtml),
    document.getElementById("cf-overshow").className = "cf-show-now"
}
function fetchNextArticle() {
    var start = document.querySelectorAll("#cf-container .cf-article").length
      , end = start + fdata.stepnumber
      , articleNum = article_num;
    if (end > articleNum && (end = articleNum),
    start < articleNum) {
        UrlNow = localStorage.getItem("urlNow");
        var fetchUrl = UrlNow + "rule=" + sortNow + "&start=" + start + "&end=" + end;
        fetch(fetchUrl).then(res=>res.json()).then(json=>{
            var nextArticle = eval(json.article_data);
            console.log("已预载?rule=" + sortNow + "&start=" + start + "&end=" + end),
            localStorage.setItem("nextArticle", JSON.stringify(nextArticle))
        }
        )
    } else
        (start = articleNum) && (document.getElementById("cf-more").outerHTML = '<div id="cf-more" class="cf-new-add" onclick="loadNoArticle()"><small>一切皆有尽头!</small></div>')
}
function loadNextArticle() {
    for (var nextArticle = JSON.parse(localStorage.getItem("nextArticle")), articleItem = "", i = 0; i < nextArticle.length; i++) {
        var item = nextArticle[i], id;
        articleItem += `\n        <div class="cf-article ${"cf-" + CryptoJS.MD5(item.link).toString()}">\n            <a class="cf-article-title" href="${item.link}" target="_blank" rel="noopener nofollow" data-title="${item.title}">${item.title}</a>\n            <a class="cf-star" onclick="switchSecretInput(event)"><i class="fa fa-star"></i></a>\n            <div class="cf-article-avatar no-lightbox flink-item-icon">\n                <img class="cf-img-avatar avatar" src="${item.avatar}" alt="avatar" onerror="this.src='${fdata.error_img}'; this.onerror = null;">\n                <a onclick="openMeShow(event)" data-link="${item.link}" class="" target="_blank" rel="noopener nofollow" href="javascript:;"><span class="cf-article-author">${item.author}</span></a>\n            </div>\n            <span class="cf-article-time">\n                <span class="cf-time-created" style="${"created" == sortNow ? "" : "display:none"}">${item.created}</span>\n                <span class="cf-time-updated" style="${"updated" == sortNow ? "" : "display:none"}">${item.updated}</span>\n            </span>\n        </div>\n        `
    }
    container.insertAdjacentHTML("beforeend", articleItem),
    null != savedArticlesIndex && checkStared(savedArticlesIndex),
    fetchNextArticle()
}
function loadNoArticle() {
    var articleSortData = sortNow + "ArticleData";
    localStorage.removeItem(articleSortData),
    localStorage.removeItem("statisticalData"),
    document.getElementById("cf-more").remove(),
    window.scrollTo(0, document.getElementsByClassName("cf-state").offsetTop)
}
function clearLocal() {
    localStorage.removeItem("updatedArticleData"),
    localStorage.removeItem("createdArticleData"),
    localStorage.removeItem("nextArticle"),
    localStorage.removeItem("statisticalData"),
    localStorage.removeItem("sortNow"),
    localStorage.removeItem("urlNow"),
    location.reload()
}
function checkVersion() {
    var url = fdata.apiurl + "version";
    fetch(url).then(res=>res.json()).then(json=>{
        console.log(json);
        var nowStatus = json.status
          , nowVersion = json.current_version
          , newVersion = json.latest_version
          , versionID = document.getElementById("cf-version-up");
        versionID.innerHTML = 0 == nowStatus ? "当前版本:v" + nowVersion : 1 == nowStatus ? "发现新版本:v" + nowVersion + " ↦ " + newVersion : "网络错误,检测失败!"
    }
    )
}
function changeEgg() {
    if (fdata.jsonurl || fdata.apiurl) {
        document.querySelectorAll(".cf-new-add").forEach(el=>el.remove()),
        localStorage.removeItem("updatedArticleData"),
        localStorage.removeItem("createdArticleData"),
        localStorage.removeItem("nextArticle"),
        localStorage.removeItem("statisticalData"),
        container.innerHTML = "",
        UrlNow = localStorage.getItem("urlNow");
        var UrlNowPublic = fdata.apipublieurl + "all?";
        UrlNow !== UrlNowPublic ? changeUrl = fdata.apipublieurl + "all?" : fdata.jsonurl ? changeUrl = fdata.apipublieurl + "postjson?jsonlink=" + fdata.jsonurl + "&" : fdata.apiurl && (changeUrl = fdata.apiurl + "all?"),
        localStorage.setItem("urlNow", changeUrl),
        FetchFriendCircle(sortNow, changeUrl)
    } else
        clearLocal()
}
function FetchFriendCircle(sortNow, changeUrl) {
    var end = fdata.initnumber
      , fetchUrl = UrlNow + "rule=" + sortNow + "&start=0&end=" + end;
    changeUrl && (fetchUrl = changeUrl + "rule=" + sortNow + "&start=0&end=" + end),
    fetch(fetchUrl).then(res=>res.json()).then(json=>{
        var statisticalData = json.statistical_data
          , articleData = eval(json.article_data)
          , articleSortData = sortNow + "ArticleData";
        loadStatistical(statisticalData),
        loadArticleItem(articleData, 0, end),
        localStorage.setItem("statisticalData", JSON.stringify(statisticalData)),
        localStorage.setItem(articleSortData, JSON.stringify(articleData))
    }
    )
}
function changeSort(event) {
    sortNow = event.currentTarget.dataset.sort,
    localStorage.setItem("sortNow", sortNow),
    document.querySelectorAll(".cf-new-add").forEach(el=>el.remove()),
    container.innerHTML = "",
    changeUrl = localStorage.getItem("urlNow"),
    initFriendCircle(sortNow, changeUrl),
    fdata.apiurl && checkVersion()
}
function openMeShow(event) {
    event.preventDefault();
    var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/
      , meLink = event.currentTarget.dataset.link.replace(parse_url, "$1:$2$3");
    console.log(meLink);
    var fetchUrl = "";
    fetchUrl = fdata.apiurl ? fdata.apiurl + "post?num=5&link=" + meLink : fdata.apipublieurl + "post?num=5&link=" + meLink,
    "ok" == noClick && (noClick = "no",
    fetchShow(fetchUrl))
}
function closeShow() {
    document.getElementById("cf-overlay").className -= "cf-show-now",
    document.getElementById("cf-overshow").className -= "cf-show-now",
    document.getElementById("cf-overshow").innerHTML = ""
}
localSortNow && localUrlNow ? (sortNow = localSortNow,
UrlNow = localUrlNow) : (sortNow = fdata.article_sort,
UrlNow = fdata.jsonurl ? fdata.apipublieurl + "postjson?jsonlink=" + fdata.jsonurl + "&" : fdata.apiurl ? fdata.apiurl + "all?" : fdata.apipublieurl + "all?",
console.log("当前模式:" + UrlNow),
localStorage.setItem("urlNow", UrlNow),
localStorage.setItem("sortNow", sortNow));
var noClick = "ok";
function openToShow() {
    var fetchUrl = "";
    fetchUrl = fdata.apiurl ? fdata.apiurl + "post" : fdata.apipublieurl + "post",
    "ok" == noClick && (noClick = "no",
    fetchShow(fetchUrl))
}
function fetchShow(url) {
    var closeHtml = '\n      <div class="cf-overshow-close" onclick="closeShow()"></div>\n    ';
    document.getElementById("cf-overlay").className = "cf-show-now",
    document.getElementById("cf-overshow").insertAdjacentHTML("afterbegin", closeHtml),
    console.log("开往" + url),
    fetch(url).then(res=>res.json()).then(json=>{
        noClick = "ok";
        var statisticalData = json.statistical_data
          , articleData = eval(json.article_data);
        loadFcircleShow(statisticalData, articleData)
    }
    )
}
function initFriendCircle(sortNow, changeUrl) {
    var articleSortData = sortNow + "ArticleData"
      , localStatisticalData = JSON.parse(localStorage.getItem("statisticalData"))
      , localArticleData = JSON.parse(localStorage.getItem(articleSortData));
    if (container.innerHTML = "",
    localStatisticalData && localArticleData) {
        loadStatistical(localStatisticalData),
        loadArticleItem(localArticleData, 0, fdata.initnumber),
        console.log("本地数据加载成功");
        var fetchUrl = UrlNow + "rule=" + sortNow + "&start=0&end=" + fdata.initnumber;
        fetch(fetchUrl).then(res=>res.json()).then(json=>{
            var statisticalData = json.statistical_data
              , articleData = eval(json.article_data)
              , localSnum = localStatisticalData.article_num
              , newSnum = statisticalData.article_num
              , localAtile = localArticleData[0].title
              , newAtile = articleData[0].title;
            if (localSnum !== newSnum || localAtile !== newAtile) {
                document.getElementById("cf-state").remove(),
                document.getElementById("cf-more").remove(),
                document.getElementById("cf-footer").remove(),
                container.innerHTML = "";
                var articleSortData = sortNow + "ArticleData";
                loadStatistical(statisticalData),
                loadArticleItem(articleData, 0, fdata.initnumber),
                localStorage.setItem("statisticalData", JSON.stringify(statisticalData)),
                localStorage.setItem(articleSortData, JSON.stringify(articleData)),
                console.log("热更新完成")
            } else
                console.log("API数据未更新")
        }
        )
    } else
        FetchFriendCircle(sortNow, changeUrl),
        console.log("第一次加载完成")
}
initFriendCircle(sortNow);

sc.js

    var fdataUser = {
      apiurl: 'https://moments.anxkj.top/'
    };
    var article_index = '',
        article_title = '',
        article_link = '',
        article_author = '',
        article_avatar = '',
        article_time = '',
        article_pwd = '';
    var savedArticlesIndex = '';
    var catchNowTime = Date.now();
    var updateTime = localStorage.getItem("updateTime");
    updateTime == null || catchNowTime - updateTime < 86400000 ? null : localStorage.removeItem("savedArticles");//每隔1天刷新一次
    var savedArticles = localStorage.getItem("savedArticles");
    if (savedArticles != null) {
        console.log("内存读取成功");
        var savedArticlesJson = JSON.parse(savedArticles)
        addArticleCard(savedArticlesJson);
        savedArticlesIndex = savedArticlesJson.map(item => item.index);
    } else {
        fetch("https://sc.api.anxkj.top/getsavedtitles?mode=all&column=&value=&output=jsonp")
            .then(response => response.json())
            .then(data => {
                if (data.code == 200) {
                    console.log('获取收藏夹成功');
                    savedArticles = data.content;
                    localStorage.setItem("updateTime", catchNowTime);
                    localStorage.setItem("savedArticles", JSON.stringify(savedArticles));
                    console.log(savedArticles);
                    addArticleCard(savedArticles);
                    savedArticlesIndex = savedArticles.map(item => item.index);
                    console.log(savedArticlesIndex);
                    checkStared(savedArticlesIndex);
                } else {
                    console.log('获取收藏夹失败')
                }
            })
            .catch(error => {
                console.error('获取收藏夹失败', error);
            })
    }
    function checkStared(s) {
        for (let i = 0; i < s.length; i++) {
            var j = document.querySelector("#cf-container ." + s[i] + " .cf-star");
            if (j) {
                j.classList.contains("saved") ? null : j.classList.add("saved");
            }
        }
    }
    function addArticleCard(a) {
        var container = '';
        for (let i=0; i<a.length; i++) {
            var item = a[i];
            container += `
            <div class="cf-article ${item.index}">
                <a class="cf-article-title" href="${item.link}" target="_blank" rel="noopener nofollow" data-title="${item.title}">${item.title}</a>
                <a class="cf-star saved" onclick="switchSecretInput(event)"><i class="fa fa-star"></i></a>
                <div class="cf-article-avatar no-lightbox flink-item-icon">
                    <img class="cf-img-avatar avatar" src="${item.avatar}" alt="avatar" onerror="this.src=''; this.onerror = null;">
                    <a class="" target="_blank" rel="noopener nofollow"><span class="cf-article-author">${item.author}</span></a>
                </div>
                <span class="cf-article-time">
                    <span class="cf-time-created">${item.time}</span>
                </span>
            </div>
            `;
        }
        document.getElementById("cf-saved-post").insertAdjacentHTML('beforeend', container);
    }
    function switchSecretInput(event) {
        const a = document.getElementById("fcircleInputBox");
        const b = a.querySelector(".input-password");
        const f = a.querySelector(".content-body .title");
        function c(e) { article_pwd = e.target.value; }
        if (a.classList.contains("open")) {
            a.classList.remove("open");
            b.removeEventListener('input', c);
            article_index = '';
            article_title = '';
            article_link = '';
            article_author = '';
            article_avatar = '';
            article_time = '';
        } else {
            a.classList.add("open");
            b.addEventListener('input', c);
            var d = '';
            if (event.target.nodeName.toUpperCase() == "A") {
                if (event.target.classList.contains("saved")) {
                    sendMode = 1;
                    f.innerHTML = "移出收藏";
                } else {
                    sendMode = 0;
                    f.innerHTML = "添加收藏";
                }
                d = event.target.parentElement.querySelector(".cf-article-title");
                article_author = event.target.parentElement.querySelector(".cf-article-avatar .cf-article-author").innerText;
                article_avatar = event.target.parentElement.querySelector(".cf-article-avatar .cf-img-avatar").src;
                article_time = event.target.parentElement.querySelector(".cf-article-time .cf-time-created").innerText;
            } else if (event.target.nodeName.toUpperCase() == "I") {
                if (event.target.parentElement.classList.contains("saved")) {
                    sendMode = 1;
                    f.innerHTML = "移出收藏";
                } else {
                    sendMode = 0;
                    f.innerHTML = "添加收藏";
                }
                d = event.target.parentElement.parentElement.querySelector(".cf-article-title");
                article_author = event.target.parentElement.parentElement.querySelector(".cf-article-avatar .cf-article-author").innerText;
                article_avatar = event.target.parentElement.parentElement.querySelector(".cf-article-avatar .cf-img-avatar").src;
                article_time = event.target.parentElement.parentElement.querySelector(".cf-article-time .cf-time-created").innerText;
            }
            article_title = d.innerText;
            article_link = d.getAttribute("href");
            article_index = "cf-" + CryptoJS.MD5(article_link).toString();
        }
    }
    function sendSubscribeInfo() {
        var key = CryptoJS.SHA256(article_pwd);
        var url = sendMode == 1
            ? "https://sc.api.anxkj.top/delsavedtitles?key=" + key + "&index=" + article_index
            : "https://sc.api.anxkj.top/subscribe?key=" + key + "&index=" + article_index + "&title=" + article_title + "&link=" + article_link + "&author=" + article_author + "&avatar=" + article_avatar + "&time=" + article_time;
        var inputBox = document.querySelector("#fcircleInputBox .inputBox");
        var noteBox = document.querySelector("#fcircleInputBox .noteBox");
        inputBox.classList.add("hide");
        noteBox.classList.remove("hide");
        fetch(url)
            .then(response => response.json())
            .then(data => {
                if (data.code == 200) {
                    var a = document.querySelector("#cf-saved-post ." + article_index);
                    if (a) { a.outerHTML = ""; }
                    var b = document.querySelector("." + article_index + " .cf-star");
                    if (typeof savedArticlesIndex != 'undefined') savedArticlesIndex = savedArticlesIndex.filter(element => element !== article_index);
                    if (b) {b.classList.contains("saved") ? b.classList.remove("saved") : b.classList.add("saved");}
                    if (sendMode == 0) {
                        var container = `
                            <div class="cf-article ${article_index}">
                                <a class="cf-article-title" href="${article_link}" target="_blank" rel="noopener nofollow" data-title="${article_title}">${article_title}</a>
                                <a class="cf-star saved" onclick="switchSecretInput(event)"><i class="fa  fa-star"></i></a>
                                <div class="cf-article-avatar no-lightbox flink-item-icon">
                                    <img class="cf-img-avatar avatar" src="${article_avatar}" alt="avatar" onerror="this.src=''; this.onerror = null;">
                                    <a class="" target="_blank" rel="noopener nofollow"><span class="cf-article-author">${article_author}</span></a>
                                </div>
                                <span class="cf-article-time">
                                    <span class="cf-time-created">${article_time}</span>
                                </span>
                            </div>
                            `;
                        document.getElementById("cf-saved-post").insertAdjacentHTML('beforeend', container);
                    }
                    tools.showMessage(data.message, "success", 2);
                    localStorage.removeItem("savedArticles");
                    inputBox.classList.remove("hide");
                    noteBox.classList.add("hide");
                    document.querySelector("#fcircleInputBox .btn.close").click();
                } else {
                    tools.showMessage(data.message, "error", 2);
                    inputBox.classList.remove("hide");
                    noteBox.classList.add("hide");
                    document.querySelector("#fcircleInputBox .btn.close").click();
                }
            })
            .catch(error => {
                console.error('收藏失败:', error);
            })
    }
    function foldSavedArticles(event) {
        var a = document.getElementById("cf-saved-post")
        var b = event.target.parentElement.querySelector("i")
        if (b.style.transform == "rotate(180deg)") {
            a.style.maxHeight = "240px"
            b.style.transform = ""
        } else {
            a.style.maxHeight = typeof savedArticlesIndex != 'undefined' ? (savedArticlesIndex.length * 130 - 20) + "px" : "fit-content"
            b.style.transform = "rotate(180deg)"
        }
    }

更新内容

2024.02.12 20:12
  • 修复图标无法显示
  • 修复弹窗无法显示
  • 修复无法收藏
  • 修复朋友圈api与收藏api冲突
2024.02.12 17:00

初步完成

© 版权声明
WWW.ANXKJ.TOP
喜欢就支持一下吧
点赞7 分享
评论 共2条
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片