interface StatusObject { name: string; value: string; } interface DoubanConfig { baseAPI: string; container: string; types?: Array; onChange?: (value: string) => void; } interface DoubanObject { subject_id: string; name: string; card_subtitle: string; create_time: any; douban_score: string; link: string; type: string; poster: string; pubdate: string; year: string; status: string; } class Douban { readonly ver: string; type: any; finished: boolean; paged: number; genre_list: Array; subjects: Array; status: string; baseAPI: string; types: Array; container: string; constructor(config: DoubanConfig) { this.container = config.container; this.types = config.types ?? [ "movie", "book", "music", "game", "drama", ]; this.baseAPI = config.baseAPI; this.ver = "1.0.6"; this.type = "movie"; this.status = "done"; this.finished = false; this.paged = 1; this.genre_list = [ { name: "已看", value: "done", }, { name: "在看", value: "doing", }, { name: "想看", value: "mark", }, ]; this.subjects = []; this._create(); } on(event: string, element: string, callback: any) { const nodeList: NodeList = document.querySelectorAll(element); nodeList.forEach((item) => { item.addEventListener(event, callback); }); } _handleGenreClick(): void { this.on("click", ".db--genreItem", (t: MouseEvent) => { const self = t.currentTarget as HTMLElement; if (self.classList.contains("is-active")) { return; } document.querySelector(".db--list")!.innerHTML = ""; document.querySelector(".lds-ripple")!.classList.remove("u-hide"); this.status = self.dataset.status || ""; // Provide a default value of an empty string if self.dataset.status is undefined this._renderGenre(); this.paged = 1; this.finished = false; this.subjects = []; this._fetchData(); }); } _reanderTypes(): void { document.querySelector(".db--nav")!.innerHTML = this.types .map((item: string) => { return `${item}`; }) .join(""); this._handleNavClick(); } _renderGenre(): void { document.querySelector(".db--genres")!.innerHTML = this.genre_list .map((item: StatusObject) => { return `${item.name}`; }) .join(""); this._handleGenreClick(); } _fetchData(): void { const params: URLSearchParams = new URLSearchParams({ paged: this.paged.toString(), type: this.type, status: this.status, }); fetch(this.baseAPI + "list?" + params.toString()) .then((response) => response.json()) .then((t: any) => { console.log(t.results); if (t.results.length) { if ( document .querySelector(".db--list")! .classList.contains("db--list__card") ) { this.subjects = [...this.subjects, ...t.results]; this._randerDateTemplate(); } else { this.subjects = [...this.subjects, ...t.results]; this._randerListTemplate(); } document .querySelector(".lds-ripple")! .classList.add("u-hide"); } else { this.finished = true; document .querySelector(".lds-ripple")! .classList.add("u-hide"); } }); } _randerDateTemplate(): void { const result = this.subjects.reduce((result, item) => { const date = new Date(item.create_time); const year = date.getFullYear(); const month = date.getMonth() + 1; const key = `${year}-${month.toString().padStart(2, "0")}`; if (Object.prototype.hasOwnProperty.call(result, key)) { result[key].push(item); } else { result[key] = [item]; } return result; }, {}); let html = ``; for (let key in result) { const date = key.split("-"); html += `
${date[1]}
${date[0]}
`; html += result[key] .map((movie: any) => { return `
${ movie.douban_score > 0 ? '' + movie.douban_score : "" }${ movie.year > 0 ? " · " + movie.year : "" }
`; }) .join(""); html += `
`; } document.querySelector(".db--list")!.innerHTML = html; } _randerListTemplate(): void { document.querySelector(".db--list")!.innerHTML = this.subjects .map((item) => { return `
${ item.create_time }
${ item.douban_score ? '' + item.douban_score : "" }${ item.year ? " · " + item.year : "" }
`; }) .join(""); } _handleScroll(): void { window.addEventListener("scroll", () => { var t = window.scrollY || window.pageYOffset; const moreElement = document.querySelector( ".block-more" ) as HTMLElement; if ( moreElement.offsetTop + -window.innerHeight < t && document .querySelector(".lds-ripple")! .classList.contains("u-hide") && !this.finished ) { document .querySelector(".lds-ripple")! .classList.remove("u-hide"); this.paged++; this._fetchData(); } }); } _handleNavClick(): void { this.on("click", ".db--navItem", (t: MouseEvent) => { const self = t.currentTarget as HTMLElement; if (self.classList.contains("current")) return; this.status = "done"; this.type = self.dataset.type; this._renderGenre(); document.querySelector(".db--list")!.innerHTML = ""; document.querySelector(".lds-ripple")!.classList.remove("u-hide"); document .querySelector(".db--navItem.current")! .classList.remove("current"); self.classList.add("current"); this.paged = 1; this.finished = false; this.subjects = []; this._fetchData(); }); } _create(): void { if (document.querySelector(".db--container")) { const container = document.querySelector( this.container ) as HTMLElement; if (!container) return; container.innerHTML = `
`; this._renderGenre(); this._reanderTypes(); this._fetchData(); this._handleScroll(); } if (document.querySelector(".js-db")) { document.querySelectorAll(".js-db").forEach((item: any) => { const db = item; const id = db.dataset.id; const type = db.dataset.type; const nodeParent = db.parentNode as HTMLElement; fetch(this.baseAPI + `${type}/${id}`).then((response) => { response.json().then((t) => { if (t.data) { const data = t.data; const node = document.createElement("div"); node.classList.add("doulist-item"); node.innerHTML = `
${data.douban_score}
${data.card_subtitle}
`; nodeParent.replaceWith(node); } }); }); }); } if (document.querySelector(".db--collection")) { document .querySelectorAll(".db--collection") .forEach((item: any) => { this._fetchCollection(item); }); } } _fetchCollection(item: any): void { const type = item.dataset.style ? item.dataset.style : "card"; fetch( this.baseAPI + "/list?type=" + item.dataset.type + "&paged=1&start_time=" + item.dataset.start + "&end_time=" + item.dataset.end ) .then((response) => response.json()) .then((t: any) => { if (t.length) { if (type == "card") { item.innerHTML += t .map((movie: DoubanObject) => { return `
Marked ${movie.create_time}
${movie.douban_score}
${movie.card_subtitle}
`; }) .join(""); } else { const result = t.reduce( (result: any, item: DoubanObject) => { if ( Object.prototype.hasOwnProperty.call( result, item.create_time ) ) { result[item.create_time].push(item); } else { result[item.create_time] = [item]; } return result; }, {} ); let html = ``; for (let key in result) { html += `
${key}
`; html += result[key] .map((movie: any) => { return `
${ movie.douban_score }
${movie.remark || movie.card_subtitle}
`; }) .join(""); html += `
`; } item.innerHTML = html; } } }); } } export default Douban;