7 Commits

Author SHA1 Message Date
imsyy
bdfebd32b5 feat: 同步支持新版API 2024-04-10 17:52:54 +08:00
imsyy
77c9c0f8bc 🐞 fix: 修改 api 接口 2024-03-01 10:34:55 +08:00
底层用户
a274cf6f12 Merge pull request #20 from rehiy/master
pr: 添加缺失的卡片
2024-03-01 10:27:50 +08:00
若海
339248be6b fix: 修复带参数的热点信息 2024-02-02 14:59:01 +08:00
若海
97241241b9 feat: 适配豆瓣小组、网易云音乐、qq音乐、NGA、V2ex、Github卡片 2024-02-02 14:22:20 +08:00
若海
c72e7a915f feat: 支持设置请求参数 2024-02-02 14:21:11 +08:00
imsyy
90b9388386 feat: 支持配置站点目录 #18 2023-12-26 16:17:41 +08:00
25 changed files with 2076 additions and 1785 deletions

7
.env
View File

@@ -1,6 +1,9 @@
# 全局 API 地址
# VITE_GLOBAL_API="http://localhost:6688"
VITE_GLOBAL_API="https://api-hot.imsyy.top"
VITE_GLOBAL_API="https://api-hot.efefee.cn"
# ICP 备案号
VITE_ICP = "豫ICP备2022018134号-1"
VITE_ICP = "豫ICP备2022018134号-1"
# 全局目录
VITE_DIR = "/"

View File

@@ -3,7 +3,7 @@
"description": "今日热榜",
"author": "imsyy",
"github": "https://github.com/imsyy",
"version": "1.0.0",
"version": "1.1.0",
"private": true,
"scripts": {
"dev": "vite",
@@ -12,24 +12,24 @@
},
"dependencies": {
"@icon-park/vue-next": "^1.4.2",
"@jridgewell/sourcemap-codec": "^1.4.14",
"axios": "^1.3.3",
"@jridgewell/sourcemap-codec": "^1.4.15",
"axios": "^1.6.8",
"lunar-calendar": "^0.1.4",
"pinia": "^2.0.28",
"pinia-plugin-persistedstate": "^3.1.0",
"sass": "^1.56.1",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"sass": "^1.74.1",
"scrollreveal": "^4.0.9",
"terser": "^5.16.5",
"vue": "^3.2.45",
"vue-router": "^4.1.6",
"terser": "^5.30.3",
"vue": "^3.4.21",
"vue-router": "^4.3.0",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"naive-ui": "^2.34.3",
"unplugin-auto-import": "^0.12.0",
"unplugin-vue-components": "^0.22.11",
"vite": "^4.0.0",
"vite-plugin-pwa": "^0.14.1"
"@vitejs/plugin-vue": "^4.6.2",
"naive-ui": "^2.38.1",
"unplugin-auto-import": "^0.12.2",
"unplugin-vue-components": "^0.22.12",
"vite": "^4.5.3",
"vite-plugin-pwa": "^0.14.7"
}
}

3457
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/logo/acfun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
public/logo/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
public/logo/hellogithub.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/logo/honkai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/logo/jianshu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
public/logo/ngabbs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
public/logo/starrail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
public/logo/v2ex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/logo/zhihu-daily.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -4,11 +4,16 @@ import axios from "@/api/request";
* 获取热榜分类数据
* @param {string} type 热榜分类名称
* @param {boolean} isNew 是否拉取最新数据
* @param {object} params 请求参数
* @returns
*/
export const getHotLists = (type, isNew) => {
export const getHotLists = (type, isNew = false, params) => {
return axios({
method: "GET",
url: `/${type}${isNew ? "/new" : "/"}`,
url: `/${type}`,
params: {
cache: !isNew,
...params,
},
});
};

View File

@@ -1,10 +1,11 @@
<template>
<n-card
hoverable
class="hot-list"
:header-style="{ padding: '16px' }"
:content-style="{ padding: '0 16px' }"
:footer-style="{ padding: '16px' }"
:id="`hot-list-${hotData.name}`"
class="hot-list"
hoverable
@click="toList"
>
<template #header>
@@ -17,15 +18,15 @@
/>
<n-text class="name-text">{{ hotData.label }}</n-text>
</div>
<n-text v-if="hotListData?.subtitle" class="subtitle" :depth="2">
{{ hotListData.subtitle }}
<n-text v-if="hotListData?.type" class="subtitle" :depth="2">
{{ hotListData.type }}
</n-text>
<n-skeleton v-else width="60px" text round />
</n-space>
</template>
<n-scrollbar class="news-list" ref="scrollbarRef">
<Transition name="fade" mode="out-in">
<template v-if="loadingError">
<div v-if="loadingError" class="error">
<n-result
size="small"
status="500"
@@ -33,43 +34,51 @@
description="生活总会遇到不如意的事情"
style="margin-top: 40px"
/>
</template>
<template v-else-if="!hotListData || listLoading">
<div class="loading">
<n-skeleton text round :repeat="10" height="20px" />
</div>
</template>
<template v-else>
<div class="lists" :id="hotData.name + 'Lists'">
<div
class="item"
v-for="(item, index) in hotListData.data.slice(0, 15)"
:key="item"
<n-button
size="small"
secondary
strong
round
@click.stop="getHotListsData(hotData.name)"
>
<template #icon>
<n-icon :component="Refresh" />
</template>
重试
</n-button>
</div>
<div v-else-if="!hotListData || listLoading" class="loading">
<n-skeleton text round :repeat="10" height="20px" />
</div>
<div v-else class="lists" :id="hotData.name + 'Lists'">
<div
class="item"
v-for="(item, index) in hotListData.data.slice(0, 15)"
:key="item"
>
<n-text
class="num"
:class="
index === 0
? 'one'
: index === 1
? 'two'
: index === 2
? 'three'
: null
"
:depth="2"
>{{ index + 1 }}</n-text
>
<n-text
class="num"
:class="
index === 0
? 'one'
: index === 1
? 'two'
: index === 2
? 'three'
: null
"
:depth="2"
>{{ index + 1 }}</n-text
>
<n-text
:style="{ fontSize: store.listFontSize + 'px' }"
class="text"
@click.stop="jumpLink(item)"
>
{{ item.title }}
</n-text>
</div>
<n-text
:style="{ fontSize: store.listFontSize + 'px' }"
class="text"
@click.stop="jumpLink(item)"
>
{{ item.title }}
</n-text>
</div>
</template>
</div>
</Transition>
</n-scrollbar>
<template #footer>
@@ -158,11 +167,12 @@ const listLoading = ref(false);
const loadingError = ref(false);
// 获取热榜数据
const getHotListsData = async (type, isNew = false) => {
const getHotListsData = async (name, isNew = false) => {
try {
// hotListData.value = null;
loadingError.value = false;
const result = await getHotLists(type, isNew);
const item = store.newsArr.find((item) => item.name == name);
const result = await getHotLists(item.name, isNew, item.params);
// console.log(result);
if (result.code === 200) {
listLoading.value = false;
@@ -222,6 +232,23 @@ const toList = () => {
}
};
// 判断列表是否显示
const checkListShow = () => {
const typeName = props.hotData.name;
const listId = "hot-list-" + typeName;
const listDom = document.getElementById(listId);
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log(`👀 ${typeName} 可见,开始加载`);
getHotListsData(props.hotData.name);
observer.unobserve(entry.target);
}
});
});
observer.observe(listDom);
};
// 实时改变更新时间
watch(
() => store.timeData,
@@ -233,7 +260,7 @@ watch(
);
onMounted(() => {
if (props.hotData.name) getHotListsData(props.hotData.name);
checkListShow();
});
</script>
@@ -242,17 +269,6 @@ onMounted(() => {
border-radius: 12px;
transition: all 0.3s;
cursor: pointer;
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.title {
display: flex;
align-items: center;
@@ -294,6 +310,15 @@ onMounted(() => {
right: 0;
}
.error {
display: flex;
flex-direction: column;
align-items: center;
.n-button {
margin-top: 12px;
}
}
.loading {
display: flex;
flex-direction: column;

View File

@@ -82,13 +82,13 @@ export const mainStore = defineStore("mainData", {
},
{
label: "腾讯新闻",
name: "newsqq",
name: "qq-news",
order: 12,
show: true,
},
{
label: "豆瓣",
name: "douban_new",
label: "豆瓣电影",
name: "douban-movie",
order: 13,
show: true,
},
@@ -98,21 +98,21 @@ export const mainStore = defineStore("mainData", {
order: 14,
show: true,
},
{
label: "崩坏:星穹铁道",
name: "starrail",
order: 16,
show: true,
},
{
label: "LOL",
name: "lol",
order: 15,
show: true,
},
{
label: "快手",
name: "kuaishou",
order: 16,
show: true,
},
{
label: "网易新闻",
name: "netease",
name: "netease-news",
order: 17,
show: true,
},
@@ -122,6 +122,36 @@ export const mainStore = defineStore("mainData", {
order: 18,
show: true,
},
{
label: "豆瓣讨论小组",
name: "douban-group",
order: 19,
show: true,
},
{
label: "NGA",
name: "ngabbs",
order: 20,
show: true,
},
{
label: "HelloGitHub",
name: "hellogithub",
order: 21,
show: true,
},
{
label: "简书",
name: "jianshu",
order: 22,
show: true,
},
{
label: "知乎日报",
name: "zhihu-daily",
order: 23,
show: true,
},
],
newsArr: [],
// 链接跳转方式
@@ -131,7 +161,7 @@ export const mainStore = defineStore("mainData", {
// 时间数据
timeData: null,
// 字体大小
listFontSize: 14,
listFontSize: 16,
};
},
getters: {},

View File

@@ -1,5 +1,4 @@
// 全局样式
* {
margin: 0;
padding: 0;
@@ -11,4 +10,15 @@ html,
body,
#app {
height: 100%;
}
}
// 动画
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

View File

@@ -141,9 +141,10 @@ const pageNumber = ref(
const listData = ref(null);
// 获取热榜数据
const getHotListsData = (type, isNew = false) => {
const getHotListsData = async (name, isNew = false) => {
listData.value = null;
getHotLists(type, isNew).then((res) => {
const item = store.newsArr.find((item) => item.name == name)
getHotLists(item.name, isNew, item.params).then((res) => {
console.log(res);
if (res.code === 200) {
listData.value = res;

View File

@@ -53,7 +53,7 @@
<n-card class="set-item">
<div class="top" style="flex-direction: column; align-items: flex-start">
<div class="name">
<n-text class="text">歌词文本大小</n-text>
<n-text class="text">列表文本大小</n-text>
<n-card
class="tip"
:style="{
@@ -74,8 +74,8 @@
:min="14"
:step="0.01"
:marks="{
14: '默认',
16: '大一点',
14: '小一点',
16: '默认',
20: '最大',
}"
/>

View File

@@ -1,82 +1,88 @@
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import { defineConfig, loadEnv } from "vite";
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
import { VitePWA } from "vite-plugin-pwa";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
import { VitePWA } from "vite-plugin-pwa";
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: [
"vue",
{
"naive-ui": [
"useDialog",
"useMessage",
"useNotification",
"useLoadingBar",
export default defineConfig(({ mode }) => {
return {
base: loadEnv(mode, process.cwd())["VITE_DIR"],
plugins: [
vue(),
AutoImport({
imports: [
"vue",
{
"naive-ui": [
"useDialog",
"useMessage",
"useNotification",
"useLoadingBar",
],
},
],
}),
Components({
resolvers: [NaiveUiResolver()],
}),
// PWA
VitePWA({
registerType: "autoUpdate",
workbox: {
cleanupOutdatedCaches: true,
runtimeCaching: [
{
urlPattern: /(.*?)\.(woff2|woff|ttf)/,
handler: "CacheFirst",
options: {
cacheName: "file-cache",
},
},
{
urlPattern:
/(.*?)\.(webp|png|jpe?g|svg|gif|bmp|psd|tiff|tga|eps)/,
handler: "CacheFirst",
options: {
cacheName: "image-cache",
},
},
],
},
],
}),
Components({
resolvers: [NaiveUiResolver()],
}),
// PWA
VitePWA({
registerType: "autoUpdate",
workbox: {
cleanupOutdatedCaches: true,
runtimeCaching: [
{
urlPattern: /(.*?)\.(woff2|woff|ttf)/,
handler: "CacheFirst",
options: {
cacheName: "file-cache",
manifest: {
name: "今日热榜",
short_name: "DailyHot",
description: "汇聚全网热点,热门尽览无余",
display: "standalone",
start_url: "/",
theme_color: "#fff",
background_color: "#efefef",
icons: [
{
src: "/ico/favicon.png",
sizes: "200x200",
type: "image/png",
},
},
{
urlPattern: /(.*?)\.(webp|png|jpe?g|svg|gif|bmp|psd|tiff|tga|eps)/,
handler: "CacheFirst",
options: {
cacheName: "image-cache",
},
},
],
},
manifest: {
name: "今日热榜",
short_name: "DailyHot",
description: "汇聚全网热点,热门尽览无余",
display: "standalone",
start_url: "/",
theme_color: "#fff",
background_color: "#efefef",
icons: [
{
src: "/ico/favicon.png",
sizes: "200x200",
type: "image/png",
},
],
},
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
build: {
minify: "terser",
terserOptions: {
compress: {
pure_funcs: ["console.log"],
],
},
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
},
server: {
port: 6699,
},
build: {
minify: "terser",
terserOptions: {
compress: {
pure_funcs: ["console.log"],
},
},
},
};
});