|
|
|
|
<script>
|
|
|
|
|
const { ref, onMounted } = require('vue');
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
compatConfig: {
|
|
|
|
|
MODE: 3
|
|
|
|
|
},
|
|
|
|
|
compilerOptions: {
|
|
|
|
|
whitespace: 'condense'
|
|
|
|
|
},
|
|
|
|
|
setup() {
|
|
|
|
|
const bbsUrl = 'https://bbs.isekai.cn';
|
|
|
|
|
|
|
|
|
|
const mounted = ref(false);
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
const feedList = ref([]);
|
|
|
|
|
const api = new mw.Api();
|
|
|
|
|
|
|
|
|
|
let recentData = {
|
|
|
|
|
recentNew: null,
|
|
|
|
|
recentEdit: null,
|
|
|
|
|
recentThread: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let requiredData = ['recentNew', 'recentEdit'];
|
|
|
|
|
let externalData = [];
|
|
|
|
|
|
|
|
|
|
const onLoaded = () => {
|
|
|
|
|
if (!requiredData.every((key) => Array.isArray(recentData[key]))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!externalData.every((key) => Array.isArray(recentData[key]))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 混合两个列表
|
|
|
|
|
let recentList = [];
|
|
|
|
|
requiredData.forEach((key) => {
|
|
|
|
|
recentList.push(...recentData[key]);
|
|
|
|
|
});
|
|
|
|
|
externalData.forEach((key) => {
|
|
|
|
|
recentList.push(...recentData[key]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
recentList.sort((a, b) => b.orderWeight - a.orderWeight);
|
|
|
|
|
|
|
|
|
|
// 去除重复,获取pageid列表
|
|
|
|
|
let pageIdList = [];
|
|
|
|
|
recentList = recentList.filter((item) => {
|
|
|
|
|
if (item.external) { // 不过滤外部页面
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pageIdList.includes(item.pageid)) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
pageIdList.push(item.pageid);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 获取页面详细信息
|
|
|
|
|
api.get({
|
|
|
|
|
"action": "query",
|
|
|
|
|
"prop": "extracts|info",
|
|
|
|
|
"pageids": pageIdList.join('|'),
|
|
|
|
|
"redirects": 1,
|
|
|
|
|
"converttitles": 1,
|
|
|
|
|
"exchars": 100,
|
|
|
|
|
"exintro": 1,
|
|
|
|
|
"explaintext": 1,
|
|
|
|
|
"inprop": "url"
|
|
|
|
|
}).done((data) => {
|
|
|
|
|
if (data.query && data.query.pages) {
|
|
|
|
|
const pageInfoList = data.query.pages;
|
|
|
|
|
recentList = recentList.map((info) => {
|
|
|
|
|
if (info.external) {
|
|
|
|
|
return {
|
|
|
|
|
pageid: -1,
|
|
|
|
|
...info,
|
|
|
|
|
};
|
|
|
|
|
} else if (info.pageid in pageInfoList) {
|
|
|
|
|
const pageInfo = pageInfoList[info.pageid];
|
|
|
|
|
return {
|
|
|
|
|
pageid: info.pageid,
|
|
|
|
|
title: pageInfo.title,
|
|
|
|
|
description: pageInfo.extract,
|
|
|
|
|
url: pageInfo.fullurl
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return {
|
|
|
|
|
pageid: info.pageid,
|
|
|
|
|
title: info.title,
|
|
|
|
|
description: '',
|
|
|
|
|
url: mw.util.getUrl(info.title)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 设置data
|
|
|
|
|
feedList.value = recentList;
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const loadData = () => {
|
|
|
|
|
api.get({
|
|
|
|
|
action: 'query',
|
|
|
|
|
list: 'recentchanges',
|
|
|
|
|
rctype: 'edit',
|
|
|
|
|
rcnamespace: 0,
|
|
|
|
|
rclimit: 20,
|
|
|
|
|
}).done((data) => {
|
|
|
|
|
recentData.recentEdit = [];
|
|
|
|
|
if (data.query && Array.isArray(data.query.recentchanges)) { //有成功取到数据
|
|
|
|
|
data.query.recentchanges.forEach((one) => {
|
|
|
|
|
if (one.timestamp) {
|
|
|
|
|
one.timestamp = new Date(one.timestamp).getTime();
|
|
|
|
|
one.orderWeight = one.timestamp;
|
|
|
|
|
} else {
|
|
|
|
|
one.orderWeight = 0;
|
|
|
|
|
}
|
|
|
|
|
recentData.recentEdit.push(one);
|
|
|
|
|
});
|
|
|
|
|
onLoaded();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
api.get({
|
|
|
|
|
action: 'query',
|
|
|
|
|
list: 'recentchanges',
|
|
|
|
|
rctype: 'new',
|
|
|
|
|
rcnamespace: 0,
|
|
|
|
|
rclimit: 20,
|
|
|
|
|
}).done((data) => {
|
|
|
|
|
recentData.recentNew = [];
|
|
|
|
|
if (data.query && Array.isArray(data.query.recentchanges)) { // 成功取到数据
|
|
|
|
|
data.query.recentchanges.forEach((one) => {
|
|
|
|
|
if (one.timestamp) {
|
|
|
|
|
one.timestamp = new Date(one.timestamp).getTime();
|
|
|
|
|
one.orderWeight = one.timestamp + (86400 * 1000); // 新页面保护,权重比页面更新高7天
|
|
|
|
|
} else {
|
|
|
|
|
one.orderWeight = 0;
|
|
|
|
|
}
|
|
|
|
|
recentData.recentNew.push(one);
|
|
|
|
|
});
|
|
|
|
|
onLoaded();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (bbsUrl) {
|
|
|
|
|
externalData.push('recentThread');
|
|
|
|
|
let formatter = document.createElement('div');
|
|
|
|
|
fetch('/api/bbs/recent').then((res) => {
|
|
|
|
|
if (res.ok) {
|
|
|
|
|
return res.json();
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('Cannot load bbs threads: HTTP ' + res.status + ' ' + res.statusText);
|
|
|
|
|
}
|
|
|
|
|
}).then((data) => {
|
|
|
|
|
recentData.recentThread = [];
|
|
|
|
|
if (data && Array.isArray(data.topics)) {
|
|
|
|
|
data.topics.forEach((topicData) => {
|
|
|
|
|
let data = {
|
|
|
|
|
external: true,
|
|
|
|
|
siteName: '异世界红茶馆',
|
|
|
|
|
title: topicData.title,
|
|
|
|
|
orderWeight: new Date(topicData.timestamp).getTime(),
|
|
|
|
|
url: bbsUrl + '/topic/' + topicData.slug,
|
|
|
|
|
}
|
|
|
|
|
if (topicData.teaser) {
|
|
|
|
|
data.url += '/' + topicData.teaser.index.toString();
|
|
|
|
|
// 去除HTML标签
|
|
|
|
|
formatter.innerHTML = topicData.teaser.content
|
|
|
|
|
data.description = formatter.innerText;
|
|
|
|
|
}
|
|
|
|
|
recentData.recentThread.push(data);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
onLoaded();
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
console.error(err);
|
|
|
|
|
// 加载论坛帖子失败不阻塞
|
|
|
|
|
recentData.recentThread = [];
|
|
|
|
|
onLoaded();
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
recentData.recentThread = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
mounted.value = true;
|
|
|
|
|
loadData();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
mounted,
|
|
|
|
|
loading,
|
|
|
|
|
feedList
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="isekai-feed-list isekai-thin-scrollbar" :class="{ mounted: mounted }">
|
|
|
|
|
<div v-if="loading" class="loading">
|
|
|
|
|
<div class="spinner">
|
|
|
|
|
<div class="oo-ui-widget oo-ui-widget-enabled oo-ui-progressBarWidget-indeterminate oo-ui-progressBarWidget"
|
|
|
|
|
aria-disabled="false" role="progressbar" aria-valuemin="0" aria-valuemax="100">
|
|
|
|
|
<div class="oo-ui-progressBarWidget-bar"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<ul v-else class="isekai-list">
|
|
|
|
|
<a class="isekai-list-item" v-for="(feedItem, index) in feedList" :key="index" :href="feedItem.url"
|
|
|
|
|
target="_blank">
|
|
|
|
|
<div class="isekai-list-item-content">
|
|
|
|
|
<div class="isekai-list-item-title">
|
|
|
|
|
<div>{{ feedItem.title }}</div>
|
|
|
|
|
<div v-if="feedItem.siteName" class="tag">{{ feedItem.siteName }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="isekai-list-item-text">{{ feedItem.description }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="isekai-list-item-icon">
|
|
|
|
|
<span
|
|
|
|
|
class="oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement oo-ui-iconElement-icon oo-ui-image-progressive oo-ui-icon-next oo-ui-labelElement-invisible oo-ui-iconWidget"
|
|
|
|
|
aria-disabled="false"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</a>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|