You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
4.6 KiB
205 lines
4.6 KiB
<template> |
|
<div v-for="article in articles" :key="article.url" id="articles"> |
|
<!-- 增加了这个 span --> |
|
<span v-if="article.category !== null" class="category"> |
|
{{ article.category.title }} |
|
</span> |
|
|
|
<span v-for="tag in article.tags" v-bind:key="tag" class="tag"> |
|
{{ tag }} |
|
</span> |
|
<!-- 路由链接:to的两个参数分别是: 路由名称和传入的id --> |
|
<router-link :to="{ name: 'detail', params: { id: article.id } }"> |
|
<div class="article-title">{{ article.title }}</div> |
|
</router-link> |
|
|
|
<div> |
|
{{ formatted_time(article.created) }} |
|
</div> |
|
</div> |
|
<!-- 实现分页 --> |
|
<div id="paginator"> |
|
<span v-if="is_page_exists('previous')"> |
|
<router-link :to="get_path('previous')"> Prev </router-link> |
|
</span> |
|
<span class="current-page"> |
|
{{ get_page_param("current") }} |
|
</span> |
|
<span v-if="is_page_exists('next')"> |
|
<router-link :to="get_path('next')"> Next </router-link> |
|
</span> |
|
</div> |
|
</template> |
|
|
|
<script> |
|
import $ from "jquery"; |
|
import { onMounted, ref } from "vue"; |
|
import { useRoute } from "vue-router"; |
|
|
|
export default { |
|
name: "ArticleList", |
|
setup() { |
|
onMounted(() => { |
|
get_article_data(); |
|
}); |
|
let articles = ref([]); // 保存当前页的所有文章 |
|
let previous = ref(null); // 保存上一页的url |
|
let next = ref(null); // 保存下一页的url |
|
|
|
let router = useRoute(); |
|
|
|
// 格式化创建时间 |
|
const formatted_time = (iso_date_string) => { |
|
const date = new Date(iso_date_string); |
|
return date.toLocaleDateString(); |
|
}; |
|
// 判断页面是否存在 |
|
const is_page_exists = (direction) => { |
|
if (direction === "next") { |
|
return next.value !== null; |
|
} |
|
return previous.value !== null; |
|
}; |
|
// 获取页码 |
|
const get_page_param = (direction) => { |
|
try { |
|
let url_string; |
|
switch (direction) { |
|
case "next": |
|
url_string = next.value; |
|
break; |
|
case "previous": |
|
url_string = previous.value; |
|
if ( |
|
url_string === "http://127.0.0.1:6789/api/article/" |
|
) { |
|
url_string = |
|
"http://127.0.0.1:6789/api/article/?page=1"; |
|
} |
|
break; |
|
default: |
|
// 首页需要特殊处理 |
|
if (!("page" in router.query)) return 1; |
|
if (router.query.page === null) return 1; |
|
return router.query.page; |
|
} |
|
const url = new URL(url_string); |
|
return url.searchParams.get("page"); |
|
} catch (error) { |
|
return; |
|
} |
|
}; |
|
|
|
const get_article_data = () => { |
|
let article_url = "http://127.0.0.1:6789/api/article"; // BaseUrl |
|
|
|
let params = new URLSearchParams(); |
|
// 注意 appendIfExists 方法是原生没有的 |
|
// 原生只有 append 方法,但此方法不能判断值是否存在 |
|
// 只有这两种参数存在时才加入url |
|
params.appendIfExists("page", router.query.page); |
|
params.appendIfExists("search", router.query.search); |
|
// 这下可以同时传入多种参数 |
|
const paramsString = params.toString(); |
|
if (paramsString.charAt(0) !== "") { |
|
article_url += "/?" + paramsString; |
|
} |
|
$.ajax({ |
|
url: article_url, |
|
type: "GET", |
|
success: (resp) => { |
|
articles.value = resp.results; // 获取当前页面的文章列表 |
|
previous.value = resp.previous; // 获取前一页的链接 |
|
next.value = resp.next; // 获取后一页的链接 |
|
}, |
|
}); |
|
}; |
|
// 在翻页后取得包括page和search的正确路径 |
|
const get_path = (direction) => { |
|
let url = ""; |
|
try { |
|
switch (direction) { |
|
case "next": |
|
if (next.value !== undefined) { |
|
url += new URL(next.value).search; |
|
} |
|
break; |
|
case "previous": |
|
if (previous.value !== undefined) { |
|
url += new URL(previous.value).search; |
|
} |
|
break; |
|
} |
|
} catch { |
|
return url; |
|
} |
|
return url; |
|
}; |
|
|
|
return { |
|
articles, |
|
formatted_time, |
|
is_page_exists, |
|
get_page_param, |
|
get_article_data, |
|
get_path, |
|
}; |
|
}, |
|
watch: { |
|
// 监听路由是否有变化 |
|
$route() { |
|
this.get_article_data(); |
|
}, |
|
}, |
|
}; |
|
</script> |
|
|
|
<style scoped> |
|
#articles { |
|
padding: 1rem; |
|
} |
|
|
|
.article-title { |
|
font-size: 1.2rem; |
|
font-weight: bolder; |
|
text-decoration: none; |
|
color: blue; |
|
padding: 5px 0 5px 0; |
|
} |
|
|
|
.tag { |
|
padding: 2px 5px 2px 5px; |
|
margin: 5px 5px 5px 0; |
|
font-family: Georgia, Arial, sans-serif; |
|
font-size: small; |
|
background-color: #4e4e4e; |
|
color: whitesmoke; |
|
border-radius: 5px; |
|
} |
|
|
|
#paginator { |
|
text-align: center; |
|
padding-bottom: 4rem; |
|
} |
|
|
|
#paginator a { |
|
color: black; |
|
} |
|
|
|
.current-page { |
|
font-size: x-large; |
|
font-weight: bold; |
|
padding-left: 10px; |
|
padding-right: 10px; |
|
} |
|
|
|
.category { |
|
padding: 5px 10px 5px 10px; |
|
margin: 5px 5px 5px 0; |
|
font-family: Georgia, Arial, sans-serif; |
|
font-size: small; |
|
background-color: darkred; |
|
color: whitesmoke; |
|
border-radius: 15px; |
|
} |
|
</style>
|
|
|