This commit is contained in:
Han
2025-02-17 18:34:21 +08:00
parent 88271e27e6
commit 2c9740dcf7
38 changed files with 389 additions and 87 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
---
// 获取用户配置数据
import SITE_CONFIG from "../config";
import { fmtTime, fmtDate } from "../utils/index";
import { fmtDate } from "../utils/index";
const { Avatar, Author, Motto, WebSites } = SITE_CONFIG;
// 获取文章数据
import { getCategories, getTags, getRecommendArticles } from "../utils/getPostInfo";
@@ -78,7 +78,7 @@ import "../styles/components/Aside.less";
</p>
<p class="info">
<span>{(await i).title}</span>
<time>{fmtDate((await i).date)}</time>
<time>{fmtDate((await i).date)}前</time>
</p>
</a>
))
+9 -10
View File
@@ -3,20 +3,19 @@ import "../styles/components/Comment.less";
---
<section class="vh-comment"><section></section></section>
<script>
import updateRouter from "../utils/updateRouter";
import { LoadScript } from "../utils/index";
import SITE_CONFIG from "../config";
const { Twikoo } = SITE_CONFIG;
import SITE_INFO from "../config";
declare const twikoo: any;
// 初始化评论插件
updateRouter("afterMount", async () => {
if (!document.querySelector(".vh-comment>section")) return;
await LoadScript("https://registry.npmmirror.com/twikoo/1.6.41/files/dist/twikoo.all.min.js");
twikoo.init({
envId: Twikoo.envId,
el: ".vh-comment>section"
SITE_INFO.Twikoo.envId &&
updateRouter("afterMount", async () => {
if (!document.querySelector(".vh-comment>section")) return;
await LoadScript("https://registry.npmmirror.com/twikoo/1.6.41/files/dist/twikoo.all.min.js");
twikoo.init({
envId: SITE_INFO.Twikoo.envId,
el: ".vh-comment>section"
});
});
});
</script>
+17
View File
@@ -0,0 +1,17 @@
---
import { fmtDate } from "../utils/index";
// 页面内容的元数据
import SITE_CONFIG from "../config";
const { CreateTime } = SITE_CONFIG;
// 基础 样式
import "../styles/Footer.less";
---
<footer class="vh-footer">
<main>
<p>
<img src="/assets/images/footer/ing.svg" /><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path fill="#fff" d="M269.4 2.9C265.2 1 260.7 0 256 0s-9.2 1-13.4 2.9L54.3 82.8c-22 9.3-38.4 31-38.3 57.2c.5 99.2 41.3 280.7 213.6 363.2c16.7 8 36.1 8 52.8 0C454.7 420.7 495.5 239.2 496 140c.1-26.2-16.3-47.9-38.3-57.2L269.4 2.9zM144 221.3c0-33.8 27.4-61.3 61.3-61.3c16.2 0 31.8 6.5 43.3 17.9l7.4 7.4 7.4-7.4c11.5-11.5 27.1-17.9 43.3-17.9c33.8 0 61.3 27.4 61.3 61.3c0 16.2-6.5 31.8-17.9 43.3l-82.7 82.7c-6.2 6.2-16.4 6.2-22.6 0l-82.7-82.7c-11.5-11.5-17.9-27.1-17.9-43.3z"></path></svg><cite>稳定运行</cite><em>{fmtDate(CreateTime)}</em></span><a href="https://analytics.vvhan.com" target="_blank" rel="noopener noreferrer"><img src="/assets/images/footer/hananalytics.svg" /></a>
</p>
<p><a href="https://astro.build/" target="_blank" rel="noopener noreferrer"><img src="/assets/images/footer/astro.svg" /></a><a href="#" target="_blank" rel="noopener noreferrer"><img src="/assets/images/footer/theme.svg" /></a><a href="/sitemap-index.xml"><img src="/assets/images/footer/sitemap.svg" /></a><a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"><img src="/assets/images/footer/icp.svg" /></a></p>
</main>
</footer>
+2 -2
View File
@@ -23,7 +23,7 @@ import "../styles/components/Pagination.less";
}
<!-- 总页的前一页 -->
{
(!data.next || !data.last) && (
(!data.next || !data.last) && data.prev && (
<a class={`vh-pagination-item`} href={data.prev}>
{fmtPage(data.prev)}
</a>
@@ -33,7 +33,7 @@ import "../styles/components/Pagination.less";
<a class="vh-pagination-item active">{fmtPage(data.current) || "1"}</a>
<!-- 第一页的后一页 -->
{
(!data.prev || !data.first) && (
(!data.prev || !data.first) && data.next && (
<a class={`vh-pagination-item`} href={data.next}>
{fmtPage(data.next)}
</a>
+3 -3
View File
@@ -38,7 +38,7 @@ export default {
// 博客音乐组件解析接口
vhMusicApi: 'https://meting-dd.2333332.xyz/api',
// 评论组件 Twikoo
Twikoo: {
envId: "https://twikoo.vvhan.com/.netlify/functions/twikoo",
}
Twikoo: { envId: 'https://twikoo.vvhan.com/.netlify/functions/twikoo' },
// Han Analytics 统计(https://github.com/uxiaohan/HanAnalytics)
HanAnalytics: { enable: true, server: 'https://analytics.vvhan.com', siteId: 'Hello-HanHexoBlog' }
}
+9
View File
@@ -5,6 +5,8 @@ const { title, keywords, description, activeNav } = Astro.props;
import Head from "../components/Head.astro";
// 顶部 Header
import Header from "../components/Header.astro";
// 底部 Footer
import Footer from "../components/Footer.astro";
// 返回顶部
import BackTop from "../components/BackTop.astro";
// 手机端侧边栏
@@ -24,6 +26,7 @@ import "../styles/Layout.less";
<main class="vh-main">
<slot />
</main>
<Footer />
<BackTop />
<script>
import updateRouter from "../utils/updateRouter";
@@ -35,6 +38,10 @@ import "../styles/Layout.less";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
NProgress.configure({ easing: "ease", speed: 500, showSpinner: false, trickleSpeed: 200, minimum: 0.3 });
// Han Analytics 统计
import SITE_INFO from "../config";
const { HanAnalytics } = SITE_INFO;
import { LoadScript } from "../utils/index";
// 初始化 图片懒加载初
updateRouter("afterMount", () => {
// 预加载搜索数据
@@ -43,6 +50,8 @@ import "../styles/Layout.less";
vhLzImgInit();
// 进入页面,进度条结束
NProgress.done();
// Han Analytics 统计
HanAnalytics.enable && LoadScript(`${HanAnalytics.server}/tracker.min.js`, [{ k: "data-website-id", v: HanAnalytics.siteId }]);
});
// 离开页面,进度条开始
updateRouter("beforeCreate", NProgress.start);
+19
View File
@@ -0,0 +1,19 @@
export default {
// API 接口请求优先,数据格式保持和 data 一致
api: '',
// api 为空则使用 data 静态数据
data: [
{
"name": "韩小韩博客",
"link": "https://www.vvhan.com",
"avatar": "https://q1.qlogo.cn/g?b=qq&nk=1655466387&s=640",
"descr": "运气是计划之外的东西."
},
{
"name": "韩小韩API",
"link": "https://api.vvhan.com",
"avatar": "https://api.vvhan.com/static/images/logo.webp",
"descr": "免费Web API数据接口调用服务平台."
}
]
}
+31
View File
@@ -0,0 +1,31 @@
export default {
// API 接口请求优先,数据格式保持和 data 一致
api: '',
// api 为空则使用 data 静态数据
// 注意:图片请用 vh-img-flex 类包裹
data: [
{
"date": "2025-02-12 19:36:16",
"tags": [
"树",
"夕阳"
],
"content": "好美🌲<p class=\"vh-img-flex\"><img src=\"https://i0.wp.com/shp.qpic.cn/collector/1655466387/937ec070-8448-4c7b-9c8b-abd41ce892cb/0\"></p>"
},
{
"date": "2024-10-08 18:18:18",
"tags": [
"日常",
"工作"
],
"content": "下班!"
},
{
"date": "2024-10-05 16:16:06",
"tags": [
"日常"
],
"content": "记录第一条说说"
}
]
}
+2 -2
View File
@@ -1,7 +1,7 @@
---
// 页面 Info
import SITE_CONFIG from "../../config";
const { Description } = SITE_CONFIG;
const { Description, Twikoo } = SITE_CONFIG;
// Aside组件
import Aside from "../../components/Aside.astro";
// 公共 Layout
@@ -42,7 +42,7 @@ import "../../styles/About.less";
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener nofollow"><img data-vh-lz-src="https://img.shields.io/badge/VS_Code-007ACC?style=for-the-badge&logo=visual-studio-code&logoColor=white" alt="VS Code" /></a>
</div>
</main>
<Comment />
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
</section>
<Aside />
</section>
+6 -4
View File
@@ -1,4 +1,5 @@
---
import { fmtDate } from "../../utils/index";
import { getDescription, fmtTime } from "../../utils/index";
import { type CollectionEntry, getCollection } from "astro:content";
import { render } from "astro:content";
@@ -11,6 +12,7 @@ type Props = CollectionEntry<"blog">;
const post: any = Astro.props;
// 页面 Info
import SITE_CONFIG from "../../config";
const { Site, Title, Author, Twikoo } = SITE_CONFIG;
// 处理文章内容
const description = getDescription(post);
const { Content } = await render(post);
@@ -34,11 +36,11 @@ import "../../styles/Article.less";
<div class="article-meta">
<span class="article-meta-item">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <path d="M152 24c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40L64 64C28.7 64 0 92.7 0 128l0 16 0 48L0 448c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-256 0-48 0-16c0-35.3-28.7-64-64-64l-40 0 0-40c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 40L152 64l0-40zM48 192l80 0 0 56-80 0 0-56zm0 104l80 0 0 64-80 0 0-64zm128 0l96 0 0 64-96 0 0-64zm144 0l80 0 0 64-80 0 0-64zm80-48l-80 0 0-56 80 0 0 56zm0 160l0 40c0 8.8-7.2 16-16 16l-64 0 0-56 80 0zm-128 0l0 56-96 0 0-56 96 0zm-144 0l0 56-64 0c-8.8 0-16-7.2-16-16l0-40 80 0zM272 248l-96 0 0-56 96 0 0 56z"></path></svg>
<time>{fmtTime(post.data.date, "YYYY-MM-DD")}</time>
<time>{fmtDate(post.data.date)}前</time>
</span>
<span class="article-meta-item">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9L0 168c0 13.3 10.7 24 24 24l110.1 0c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24l0 104c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65 0-94.1c0-13.3-10.7-24-24-24z"></path></svg>
<time>{fmtTime(post.data.updated, "YYYY-MM-DD")}</time>
<time>{fmtDate(post.data.updated || post.data.date)}前</time>
</span>
<a class="article-meta-item" href={`/categories/${post.data.categories}`}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <path d="M40 48C26.7 48 16 58.7 16 72l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24L40 48zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L192 64zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-288 0zM16 232l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24l-48 0c-13.3 0-24 10.7-24 24zM40 368c-13.3 0-24 10.7-24 24l0 48c0 13.3 10.7 24 24 24l48 0c13.3 0 24-10.7 24-24l0-48c0-13.3-10.7-24-24-24l-48 0z"></path></svg>
@@ -47,8 +49,8 @@ import "../../styles/Article.less";
</div>
</header>
<Content />
<Copyright site={SITE_CONFIG.Site} id={post.data.id} title={post.data.title} sitename={SITE_CONFIG.Title} time={fmtTime(post.data.date, "YYYY-MM-DD")} auther={SITE_CONFIG.Author} />
<Comment />
<Copyright site={Site} id={post.data.id} title={post.data.title} sitename={Title} time={fmtTime(post.data.date, "YYYY-MM-DD")} auther={Author} />
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
</article>
<Aside />
</section>
+6 -3
View File
@@ -1,7 +1,7 @@
---
// 页面 Info
import SITE_CONFIG from "../../config";
const { Description } = SITE_CONFIG;
const { Description, Twikoo } = SITE_CONFIG;
// Aside组件
import Aside from "../../components/Aside.astro";
// 公共 Layout
@@ -20,7 +20,7 @@ import "../../styles/Links.less";
<p>天下快意之事莫若友。</p>
</header>
<main></main>
<Comment />
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
</section>
<Aside />
</section>
@@ -29,6 +29,9 @@ import "../../styles/Links.less";
import updateRouter from "../../utils/updateRouter";
import LinksInit from "../../scripts/Links";
// 进入页面时初始化
updateRouter("afterMount", LinksInit);
// 数据源
import LINKS_DATA from "../../page_data/Link";
const { api, data } = LINKS_DATA;
updateRouter("afterMount", () => LinksInit(api || data));
</script>
</Layout>
+2 -2
View File
@@ -1,7 +1,7 @@
---
// 页面 Info
import SITE_CONFIG from "../../config";
const { Description } = SITE_CONFIG;
const { Description, Twikoo } = SITE_CONFIG;
// Aside组件
import Aside from "../../components/Aside.astro";
// 公共 Layout
@@ -17,7 +17,7 @@ import Comment from "../../components/Comment.astro";
<h1>留言板 🌸</h1>
<p>快友之事莫若谈。</p>
</header>
<Comment />
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
</section>
<Aside />
</section>
+6 -3
View File
@@ -1,7 +1,7 @@
---
// 页面 Info
import SITE_CONFIG from "../../config";
const { Description } = SITE_CONFIG;
const { Description, Twikoo } = SITE_CONFIG;
// Aside组件
import Aside from "../../components/Aside.astro";
// 公共 Layout
@@ -20,7 +20,7 @@ import "../../styles/Talking.less";
<p>记录美好生活.</p>
</header>
<main></main>
<Comment />
{Twikoo.envId && <Comment envId={Twikoo.envId} />}
</section>
<Aside />
</section>
@@ -29,6 +29,9 @@ import "../../styles/Talking.less";
import updateRouter from "../../utils/updateRouter";
import TalkingInit from "../../scripts/Talking";
// 进入页面时初始化
updateRouter("afterMount", TalkingInit);
// 数据源
import TALKING_DATA from "../../page_data/Talking";
const { api, data } = TALKING_DATA;
updateRouter("afterMount", () => TalkingInit(api || data));
</script>
</Layout>
+5 -2
View File
@@ -4,12 +4,15 @@ import { $GET } from '../utils/index'
// 图片懒加载
import vhLzImgInit from "../scripts/vhLazyImg";
const LinksInit = async () => {
const LinksInit = async (data: any) => {
const linksDOM = document.querySelector('.vh-container>.vh-links>main')
if (!linksDOM) return;
vh.showLoading();
try {
const res = await $GET('https://tools-api.vvhan.com/blog/link');
let res = data;
if (typeof data === 'string') {
res = await $GET(data);
}
linksDOM.innerHTML = res.map((i: any) => `<a href="${i.link}" target="_blank"><img class="avatar" src="${i.avatar}" /><section class="link-info"><span>${i.name}</span><p>${i.descr}</p></section></a>`).join('');
// 图片懒加载
vhLzImgInit();
+7 -3
View File
@@ -1,5 +1,6 @@
import vh from 'vh-plugin'
import { fmtDate } from '../utils/index'
import { $GET } from '../utils/index'
// 图片懒加载
import vhLzImgInit from "../scripts/vhLazyImg";
@@ -8,13 +9,16 @@ import "../../public/assets/js/view-image.min.js";
declare const ViewImage: any;
// 灯箱JS初始化======
const TalkingInit = async () => {
const TalkingInit = async (data: any) => {
const talkingDOM = document.querySelector('.vh-container>.vh-talking>main')
if (!talkingDOM) return;
vh.showLoading();
try {
const res = await $GET('https://tools-api.vvhan.com/blog/feeling');
talkingDOM.innerHTML = res.map((i: any) => `<article><header><img data-vh-lz-src="https://q1.qlogo.cn/g?b=qq&nk=1655466387&s=640" /><p class="info"><span>.𝙃𝙖𝙣</span><time>${i.date}</time></p></header><section class="main">${i.content}</section><footer>${i.tags.map((tag: any) => `<span>${tag}</span>`).join('')}</footer></article>`).join('');
let res = data;
if (typeof data === 'string') {
res = await $GET(data);
}
talkingDOM.innerHTML = res.map((i: any) => `<article><header><img data-vh-lz-src="https://q1.qlogo.cn/g?b=qq&nk=1655466387&s=640" /><p class="info"><span>.𝙃𝙖𝙣</span><time>${fmtDate(i.date)}</time></p></header><section class="main">${i.content}</section><footer>${i.tags.map((tag: any) => `<span>${tag}</span>`).join('')}</footer></article>`).join('');
// 图片懒加载
vhLzImgInit();
// 灯箱JS初始化======
+2 -1
View File
@@ -3,7 +3,8 @@
:root {
--vh-animation-delay: 0ms;
--vh-padding-top: calc(66px + 1rem);
--vh-back-top: calc((calc(100vw - 2rem) - min((100vw - 2rem), 1388px)) / 2 + 1rem);
--vh-main-max-width: 1388px;
--vh-back-top: calc((calc(100vw - 2rem) - min((100vw - 2rem), var(--vh-main-max-width))) / 2 + 1rem);
}
h1,
+116
View File
@@ -0,0 +1,116 @@
.vh-footer {
margin-top: 1.68rem;
box-sizing: border-box;
padding: 2.6rem 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: max-content;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: saturate(100%) blur(2px);
user-select: none;
&>main {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 0.618rem;
width: 100%;
max-width: var(--vh-main-max-width);
height: max-content;
&>p {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 0.618rem;
line-height: 1;
&>a {
line-height: 1;
}
&>span {
box-sizing: border-box;
padding-left: 0.28rem;
display: flex;
align-items: center;
width: max-content;
height: 1.25rem;
background: #25C2A0;
font-size: 0.68rem;
border-radius: 0.18rem;
overflow: hidden;
&>cite,
&>em {
box-sizing: border-box;
padding: 0 0.618rem;
width: max-content;
height: 100%;
line-height: 1.25rem;
color: #fff;
font-style: normal;
}
&>cite {
padding-left: 0.28rem;
}
&>em {
background: #5D5D5D;
}
&>svg {
width: 0.88rem;
height: 0.88rem;
}
}
}
}
&>section {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 0.88rem;
width: 100%;
height: max-content;
p {
line-height: 1;
a {
line-height: 1;
}
}
&.text {
&>p {
font-size: 0.88rem;
&>a {
color: #94A3B8;
transition: opacity 0.16s ease-in-out;
&:hover {
opacity: 0.88;
}
}
}
}
&.icon {
&>p {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1rem;
}
}
}
}
+1 -2
View File
@@ -16,13 +16,12 @@ body {
flex-direction: column;
gap: 1rem;
width: 100%;
max-width: 1388px;
max-width: var(--vh-main-max-width);
height: max-content;
&>section.vh-container {
position: relative;
box-sizing: border-box;
padding-bottom: 5.66rem;
display: flex;
flex-direction: row-reverse;
gap: 1.8rem;
+1
View File
@@ -6,6 +6,7 @@ section.article-list {
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: 1.6rem;
width: 100%;
height: max-content;
overflow: hidden;
&>.vh-article-item {
+1 -1
View File
@@ -24,7 +24,7 @@ header.vh-header {
justify-content: space-between;
width: 100%;
height: 80%;
max-width: 1388px;
max-width: var(--vh-main-max-width);
a,
span.nav-btn-item {
+38 -24
View File
@@ -4,41 +4,55 @@ const getDescription = (post: any, num: number = 150) => (post.rendered ? post.r
//处理时间
const fmtTime = (time: any, fmt: string = 'MMMM D, YYYY') => dayjs(time).format(fmt)
// 处理日期
const fmtDate = (time: any) => {
const past = dayjs(time);
const now = dayjs();
const duration = now.diff(past, 'seconds');
if (duration < 60) {
return `${duration} 秒前`;
} else if (duration < 60 * 60) {
const minutes = Math.floor(duration / 60);
return `${minutes} 分钟前`;
} else if (duration < 60 * 60 * 24) {
const hours = Math.floor(duration / (60 * 60));
return `${hours} 小时前`;
} else if (duration < 60 * 60 * 24 * 30) {
const days = Math.floor(duration / (60 * 60 * 24));
return `${days} 天前`;
} else if (duration < 60 * 60 * 24 * 30 * 12) {
const months = Math.floor(duration / (60 * 60 * 24 * 30));
return `${months} 个月前`;
} else {
const years = Math.floor(duration / (60 * 60 * 24 * 30 * 12));
return `${years} 年前`;
}
const fmtDate = (time: string | Date) => {
const now = dayjs()
const past = dayjs(time)
// 计算时间差组件
const years = now.diff(past, 'year')
const months = now.diff(past.add(years, 'year'), 'month')
const days = now.diff(past.add(years, 'year').add(months, 'month'), 'day')
const hours = now.diff(past, 'hour')
const minutes = now.diff(past, 'minute')
const seconds = now.diff(past, 'second')
// 构建时间差描述
return [
years && `${years}年`,
months && `${months}月`,
days && `${days}天`,
!years && !months && hours && `${hours}小时`,
!years && !months && !days && minutes && `${minutes}分钟`,
!years && !months && !days && !hours && `${seconds}秒`
].filter(Boolean).join('')
}
// 处理页码展示
const fmtPage = (page: string | undefined) => page ? page.replace(/\//g, '') : null
// 加载外部脚本
const LoadScript = (src: string): Promise<HTMLScriptElement> => {
const LoadScript = (
src: string,
attrs?: Array<{ k: string; v: string | boolean }>
): Promise<HTMLScriptElement> => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = src;
// 添加自定义属性
if (attrs?.length) {
attrs.forEach(({ k, v }) => {
// 处理不同值类型
const value = typeof v === "boolean"
? (v ? "" : null) // 布尔值处理为 HTML 标准属性格式
: String(v); // 其他类型转为字符串
if (value !== null) {
script.setAttribute(k, value);
}
});
}
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
}
};
// 加载外部CSS
const LoadStyle = (href: string): Promise<HTMLLinkElement> => {
return new Promise((resolve, reject) => {