UNPKG

bilibili-bangumi-component

Version:

展示 bilibili 与 Bangumi 追番列表的 WebComponent 组件

328 lines (313 loc) 19.1 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const index = require('./index-b78452e6.js'); function serializeSearchParams(searchParams) { return Object.entries(searchParams) .filter(([, value]) => !!value) .map(([key, value]) => `${key}=${value}`) .join('&'); } function formatUrl(baseUrl) { if (!baseUrl.startsWith('http')) return baseUrl; const url = new URL(baseUrl); const pathname = url.pathname === '/' ? '' : url.pathname; return `${url.origin}${pathname}`; } function thorttle(fn, wait) { let timer; return function (...args) { if (!timer) { timer = setTimeout(() => { timer = null; fn.apply(this, args); }, wait); } }; } const animeCollectionMap = { 全部: '0', 想看: '1', 在看: '2', 看过: '3', 搁置: '4', 抛弃: '5', }; const gameCollectionMap = { 全部: '0', 想玩: '1', 在玩: '2', 玩过: '3', 搁置: '4', 抛弃: '5', }; const bookCollectionMap = { 全部: '0', 想读: '1', 在读: '2', 读过: '3', 搁置: '4', 抛弃: '5', }; const collectionMap = { 动画: animeCollectionMap, 游戏: gameCollectionMap, 书籍: bookCollectionMap, 音乐: bookCollectionMap, 三次元: animeCollectionMap, }; const subjectMap = { 动画: '1', 游戏: '2', 书籍: '3', 音乐: '4', 三次元: '5', }; const collectionLabelMap = { 动画: ['全部', '想看', '在看', '看过', '搁置', '抛弃'], 游戏: ['全部', '想玩', '在玩', '玩过', '搁置', '抛弃'], 书籍: ['全部', '想读', '在读', '读过', '搁置', '抛弃'], 音乐: ['全部', '想听', '在听', '听过', '搁置', '抛弃'], 三次元: ['全部', '想看', '在看', '看过', '搁置', '抛弃'], }; async function getBilibili(baseUrl, params) { const query = Object.assign(Object.assign({}, params), { collectionType: animeCollectionMap[params.collectionType] }); const res = await fetch(`${formatUrl(baseUrl)}/bilibili?${serializeSearchParams(query)}`); return await res.json(); } async function getBangumi(baseUrl, params) { const { subjectType } = params; const query = Object.assign(Object.assign({}, params), { collectionType: collectionMap[subjectType][params.collectionType], subjectType: subjectMap[params.subjectType] }); const res = await fetch(`${formatUrl(baseUrl)}/bgm?${serializeSearchParams(query)}`); return await res.json(); } async function getCustom(baseUrl, params) { const { subjectType } = params; const query = Object.assign(Object.assign({}, params), { collectionType: collectionMap[subjectType][params.collectionType], subjectType: subjectMap[params.subjectType] }); const res = await fetch(`${formatUrl(baseUrl)}/custom?${serializeSearchParams(query)}`); return await res.json(); } function Tabs({ activeLabel, labels, containerState, onChange }) { const handleClick = (label) => { if (activeLabel !== label) onChange(label); }; return (index.h("div", { class: "bbc-tabs" }, labels.map(label => (index.h("div", { class: { 'bbc-tab-item': true, 'bbc-tab-item-active': label === activeLabel, 'bbc-md-tab-item': containerState !== 'large', }, key: label, onClick: () => handleClick(label) }, label))))); } function Skeleton() { return (index.h("div", { class: "bbc-skeleton-container bbc-bangumi-item" }, index.h("div", { class: "bbc-skeleton-avatar" }), index.h("div", { class: "bbc-skeleton-content bbc-bangumi-content" }, index.h("div", { class: "bbc-skeleton-row", style: { width: '30%' } }), index.h("div", { class: "bbc-skeleton-row", style: { width: '60%', height: '40px' } }), index.h("div", { class: "bbc-skeleton-row", style: { width: '90%', height: '32px' } })))); } const Label = ({ containerState, labels }) => { let renderLabels = [...labels]; if (containerState === 'middle') renderLabels = renderLabels.slice(0, 4); if (containerState === 'small') renderLabels = renderLabels.slice(0, 3); return (index.h("div", { class: "bbc-bangumi-labels" }, renderLabels.map(label => (index.h("div", { class: { 'bbc-bangumi-label': true, 'bbc-md-label-text': containerState !== 'large', } }, index.h("p", { class: "bbc-bangumi-label-title" }, label.label), label.value && index.h("p", null, label.value)))))); }; const List = ({ list, loading, containerState }) => { return (index.h("div", { class: "bbc-bangumi" }, list.map(item => (index.h("div", null, loading ? index.h(Skeleton, null) : (index.h("div", { class: "bbc-bangumi-item" }, index.h("a", { href: item.url, target: "_blank", rel: "noreferrer" }, index.h("img", { src: item.cover, alt: "cover", loading: "lazy", referrerpolicy: "no-referrer" })), index.h("div", { class: "bbc-bangumi-content" }, index.h("h3", { class: "bbc-bangumi-title" }, index.h("a", { href: item.url, target: "_blank", rel: "noreferrer", innerHTML: item.name ? item.name : item.nameCN }), item.name && index.h("small", { innerHTML: item.nameCN })), index.h(Label, { containerState: containerState, labels: item.labels }), index.h("p", { class: "bbc-bangumi-summary", innerHTML: item.summary }))))))))); }; function Pagination({ pageNumber, totalPages, onChange, onInputChange }) { return (index.h("div", { class: "bbc-pagination" }, index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('head') }, "\u9996\u9875"), index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('prev') }, "\u4E0A\u4E00\u9875"), index.h("span", { class: "bbc-pagination-pagenum" }, `${pageNumber} / ${totalPages}`), index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('next') }, "\u4E0B\u4E00\u9875"), index.h("a", { class: "bbc-pagination-button", onClick: () => onChange('tail') }, "\u5C3E\u9875"), index.h("div", { class: "bbc-pagination-input" }, index.h("span", null, "\u8DF3\u81F3"), index.h("input", { type: "text", maxLength: 4, onChange: onInputChange }), index.h("span", null, "\u9875")))); } function Empty() { return (index.h("div", { class: "bbc-empty" }, index.h("img", { src: "https://s1.hdslb.com/bfs/static/webssr/article/empty.png", alt: "empty", referrerpolicy: "no-referrer" }))); } function Error({ error }) { return (index.h("div", { class: "bbc-error" }, index.h("img", { src: "https://s1.hdslb.com/bfs/static/jinkela/long/bitmap/error_01.png", alt: "parse failed", // @ts-expect-error referrerpolicy: "no-referrer" }), index.h("p", null, "\u03A3(o\uFF9F\u0434\uFF9Fo\uFF89) \u53D1\u751F\u4E86\u4E00\u4E9B\u9519\u8BEF"), index.h("p", null, `message: ${error.message}`))); } const indexCss = "@layer bilibili-bangumi-component {\n :host {\n display: block;\n /* 基础文本颜色 */\n --bbc-text-base-color: #4c4948;\n /* 标签颜色 */\n --bbc-label-color: #FF9843;\n /* 下划线、背景之类的颜色 */\n --bbc-primary-color: #425aef;\n }\n\n * {\n box-sizing: border-box;\n }\n\n a {\n text-decoration: none;\n }\n\n .bbc-tabs {\n display: flex;\n gap: 8px;\n }\n\n .bbc-tab-item {\n padding: 4px 16px;\n border-radius: 8px;\n font-size: 14px;\n font-weight: bold;\n color: var(--bbc-text-base-color);\n cursor: pointer;\n }\n\n .bbc-md-tab-item {\n padding: 4px 12px;\n }\n\n .bbc-tab-item:hover {\n background-color: var(--bbc-primary-color);\n color: white;\n }\n\n .bbc-tab-item-active {\n color: white;\n background-color: var(--bbc-primary-color);\n }\n\n .bbc-header-platform {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .bbc-header-platform .divider {\n width: 3px;\n border-radius: 2px;\n height: 32px;\n background-color: var(--bbc-primary-color);\n }\n\n .bbc-bangumi:last-child {\n border-bottom: none;\n }\n\n .bbc-bangumi-item {\n display: flex;\n margin: 12px 0;\n gap: 16px;\n padding: 16px;\n border-bottom: 1px solid #ddd;\n }\n\n .bbc-bangumi-item img {\n border-radius: 8px;\n cursor: pointer;\n height: 140px;\n object-fit: cover;\n aspect-ratio: 3 / 4;\n }\n\n .bbc-bangumi-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n }\n\n .bbc-bangumi-title {\n margin: 0;\n margin-bottom: 8px;\n }\n\n .bbc-bangumi-title a {\n color: var(--bbc-text-base-color);\n }\n\n .bbc-bangumi-title a:hover {\n border-bottom: 2px solid var(--bbc-primary-color);\n }\n\n .bbc-bangumi-title small {\n margin-left: 8px;\n font-weight: normal;\n font-size: 14px;\n color: var(--bbc-text-base-color);\n height: 24px;\n }\n\n .bbc-bangumi-labels {\n color: var(--bbc-label-color);\n display: flex;\n }\n\n .bbc-bangumi-label {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: space-between;\n padding: 0 12px;\n height: 36px;\n font-size: 14px;\n border-right: 1px solid var(--bbc-label-color);\n }\n\n .bbc-bangumi-label:last-child {\n border-right: none;\n }\n\n .bbc-bangumi-label p {\n margin: 0;\n padding: 0 4px;\n }\n\n .bbc-bangumi-label:not(.bbc-bangumi-label > .bbc-bangumi-label-title) {\n justify-content: center;\n }\n\n .bbc-md-label-text {\n font-size: 12px;\n }\n\n .bbc-bangumi-summary {\n margin-bottom: 0;\n font-size: 12px;\n color: var(--bbc-text-base-color);\n\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n }\n\n .bbc-pagination {\n height: 40px;\n text-align: center;\n padding-right: 20px;\n }\n\n .bbc-pagination-button {\n border-radius: 4px;\n font-size: 14px;\n padding: 4px 8px;\n margin: 0 4px;\n color: #bbb;\n cursor: pointer;\n }\n\n .bbc-pagination-button:hover {\n background-color: var(--bbc-primary-color);\n color: white;\n }\n\n .bbc-pagination-pagenum {\n color: #bbb;\n font-size: 14px;\n }\n \n .bbc-pagination-input {\n display: inline-block;\n color: #bbb;\n font-size: 12px;\n }\n\n .bbc-pagination-input input {\n display: inline-block;\n margin: 0 8px;\n font-size: 12px;\n color: #bbb;\n width: 30px;\n height: 24px;\n line-height: 24px;\n border-radius: 4px;\n border: 1px solid #bbb;\n text-align: center;\n outline: none;\n transition: border .2s ease;\n }\n\n .bbc-skeleton-container {\n animation: skeleton-blink 1.2s ease-in-out infinite;\n }\n\n @keyframes skeleton-blink {\n 50% {\n opacity: 0.6;\n }\n }\n\n .bbc-skeleton-avatar {\n height: 140px;\n border-radius: 12px;\n background-color: #f2f3f5;\n aspect-ratio: 3 / 4;\n }\n\n .bbc-skeleton-row {\n height: 24px;\n border-radius: 8px;\n background-color: #f2f3f5;\n }\n\n .bbc-empty {\n display: flex;\n padding: 40px 0;\n justify-content: center;\n align-items: center;\n }\n\n .bbc-empty img {\n height: 140px;\n object-fit: cover;\n }\n\n .bbc-error {\n margin: 20px 0;\n display: flex;\n gap: 12px;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n\n .bbc-error img {\n display: block;\n width: 260px;\n }\n\n .bbc-error p {\n margin: 0;\n color: var(--bbc-text-base-color);\n font-size: 18px;\n }\n}"; const BilibiliBangumiStyle0 = indexCss; const BilibiliBangumi = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.platformLabels = ['Bilibili', 'Bangumi']; this.subjectLabels = ['动画', '游戏', '书籍', '音乐', '三次元']; this.fetchData = async () => { try { this.loading = true; this.error = null; let response; const bilibiliParams = { uid: this.bilibiliUid, collectionType: this.activeCollection, pageSize: this.pageSize, pageNumber: this.pageNumber, }; if (this.activePlatform === 'Bilibili') { response = await getBilibili(this.api, bilibiliParams); } else if (this.activePlatform === 'Bangumi') { response = await getBangumi(this.api, Object.assign(Object.assign({}, bilibiliParams), { uid: this.bgmUid, subjectType: this.activeSubject })); } else { response = await getCustom(this.api, Object.assign(Object.assign({}, bilibiliParams), { subjectType: this.activeSubject })); } if (response.code === 200) { this.responseData = response.data; } else { this.error = response; this.responseData = null; } } catch (error) { this.error = error; this.responseData = null; } this.loading = false; }; this.handlePlatformChange = (label) => { this.collectionLabels = label === 'Bilibili' ? ['全部', '想看', '在看', '看过'] : collectionLabelMap['动画']; this.activePlatform = label; this.pageNumber = 1; this.activeSubject = '动画'; this.activeCollection = '全部'; this.fetchData(); }; this.handleSubjectChange = (label) => { this.collectionLabels = this.activePlatform === 'Bilibili' ? ['全部', '想看', '在看', '看过'] : collectionLabelMap[label]; this.activeSubject = label; this.pageNumber = 1; this.activeCollection = '全部'; this.fetchData(); }; this.handleCollectionChange = (label) => { this.activeCollection = label; this.pageNumber = 1; this.fetchData(); }; this.scrollToTop = () => { document.documentElement.scrollTo({ top: 0, behavior: 'smooth', }); }; this.handlePageChange = (changeType) => { const { totalPages } = this.responseData; switch (changeType) { case 'head': this.pageNumber = 1; break; case 'prev': if (this.pageNumber === 1) return; this.pageNumber--; break; case 'next': if (this.pageNumber === totalPages) return; this.pageNumber++; break; case 'tail': this.pageNumber = totalPages; break; } this.scrollToTop(); this.fetchData(); }; this.handleInputChange = (event) => { const inputValue = Number.parseInt(event.target.value); if (Object.is(inputValue, Number.NaN)) return; const { totalPages } = this.responseData; if (inputValue < 1) this.pageNumber = 1; else if (inputValue > totalPages) this.pageNumber = totalPages; else this.pageNumber = inputValue; this.scrollToTop(); this.fetchData(); }; this.api = undefined; this.bilibiliUid = undefined; this.bgmUid = undefined; this.bilibiliEnabled = true; this.bgmEnabled = true; this.pageSize = 15; this.customEnabled = false; this.customLabel = '自定义'; this.loading = false; this.error = undefined; this.pageNumber = 1; this.responseData = undefined; this.activePlatform = 'Bilibili'; this.activeSubject = '动画'; this.collectionLabels = ['全部', '想看', '在看', '看过']; this.activeCollection = '全部'; this.containerRef = null; this.containerState = 'large'; } componentWillLoad() { const platformLabels = [...this.platformLabels]; if (this.customEnabled) platformLabels.push(this.customLabel); const filterArr = [this.bilibiliEnabled, this.bgmEnabled, this.customEnabled]; this.platformLabels = platformLabels.filter((_, index) => filterArr[index]); this.activePlatform = this.platformLabels[0]; this.fetchData(); } componentDidLoad() { // 监听容器尺寸变化 const update = thorttle((width) => { let containerState = 'large'; if (width <= 640) containerState = 'middle'; if (width <= 465) containerState = 'small'; this.containerState = containerState; }, 100).bind(this); const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) update(entry.contentRect.width); }); resizeObserver.observe(this.containerRef); } render() { return (index.h("div", { ref: ele => this.containerRef = ele }, index.h("div", { class: "bbc-header-platform" }, index.h(Tabs, { containerState: this.containerState, activeLabel: this.activePlatform, labels: this.platformLabels, onChange: this.handlePlatformChange }), this.activePlatform !== 'Bilibili' && index.h("div", { class: "divider" }), this.activePlatform !== 'Bilibili' && index.h(Tabs, { containerState: this.containerState, activeLabel: this.activeSubject, labels: this.subjectLabels, onChange: this.handleSubjectChange })), index.h("div", null, index.h(Tabs, { containerState: this.containerState, activeLabel: this.activeCollection, labels: this.collectionLabels, onChange: this.handleCollectionChange })), this.loading && index.h(Skeleton, null), this.error && index.h(Error, { error: this.error }), this.responseData && index.h(List, { containerState: this.containerState, loading: this.loading, list: this.responseData.list }), this.responseData && this.responseData.total === 0 && index.h(Empty, null), this.responseData && (index.h(Pagination, { pageNumber: this.pageNumber, totalPages: this.responseData.totalPages, onChange: this.handlePageChange, onInputChange: this.handleInputChange })))); } }; BilibiliBangumi.style = BilibiliBangumiStyle0; exports.bilibili_bangumi = BilibiliBangumi;