Merge pull request #108 from camera-2018/106-fix-friend-cards-components-style

格式化代码
This commit is contained in:
少轻狂
2023-08-14 23:43:54 +08:00
committed by GitHub
10 changed files with 484 additions and 329 deletions

View File

@@ -1,25 +1,32 @@
<template> <template>
<iframe :src="`//player.bilibili.com/player.html?bvid=${bvid}&autoplay=false`" scrolling="no" border="0" frameborder="no" <iframe
framespacing="0" allowfullscreen="true" class="bili_iframe"/> :src="`//player.bilibili.com/player.html?bvid=${bvid}&autoplay=false`"
scrolling="no"
border="0"
frameborder="no"
framespacing="0"
allowfullscreen="true"
class="bili_iframe"
/>
</template> </template>
<script setup> <script setup>
import { defineProps } from 'vue' import { defineProps } from "vue";
const { bvid } = defineProps({ const { bvid } = defineProps({
bvid: { bvid: {
type: String, type: String,
default: 'BV1GJ411x7h7', default: "BV1GJ411x7h7",
required: true, required: true,
validator: (value) => { validator: (value) => {
return value.trim() !== '' return value.trim() !== "";
} },
} },
}) });
</script> </script>
<style scoped> <style scoped>
.bili_iframe { .bili_iframe {
width: 100%; width: 100%;
height: 23rem; height: 23rem;
} }
</style> </style>

View File

@@ -1,20 +1,25 @@
<script setup> <script setup>
import { computed,ref } from 'vue' import { computed, ref } from "vue";
import VPTeamMembersItem from './BloggerItem.vue' import VPTeamMembersItem from "./BloggerItem.vue";
const size = 'small' const size = "small";
const props= {friends:(await (await fetch("https://ghproxy.com/https://raw.githubusercontent.com/NX-Official/friends-link-plus/main/output/friends.json")).json()).friends} const props = {
const isMore = ref(false) friends: (
const members = computed(() => { await (
await fetch(
"https://ghproxy.com/https://raw.githubusercontent.com/NX-Official/friends-link-plus/main/output/friends.json"
)
).json()
).friends,
};
const isMore = ref(false);
const members = computed(() => {
if (isMore.value) { if (isMore.value) {
return props.friends return props.friends;
} else { } else {
return props.friends.slice(0, 5) return props.friends.slice(0, 5);
} }
}) });
const classes = computed(() => [ const classes = computed(() => [size ?? "medium", `count-${members.length}`]);
size ?? 'medium',
`count-${members.length}`
])
</script> </script>
<template> <template>
@@ -24,7 +29,7 @@ const classes = computed(() => [
<VPTeamMembersItem :size="size" :member="member" /> <VPTeamMembersItem :size="size" :member="member" />
</div> </div>
<div v-if="!isMore"> <div v-if="!isMore">
<VPTeamMembersItem :blank="true" @click="isMore=true"/> <VPTeamMembersItem :blank="true" @click="isMore = true" />
</div> </div>
</div> </div>
</div> </div>
@@ -35,9 +40,15 @@ const classes = computed(() => [
grid-template-columns: repeat(auto-fit, minmax(224px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(224px, 1fr));
} }
.VPTeamMembers.small.count-1 .container { max-width: 276px; } .VPTeamMembers.small.count-1 .container {
.VPTeamMembers.small.count-2 .container { max-width: calc(276px * 2 + 24px); } max-width: 276px;
.VPTeamMembers.small.count-3 .container { max-width: calc(276px * 3 + 24px * 2); } }
.VPTeamMembers.small.count-2 .container {
max-width: calc(276px * 2 + 24px);
}
.VPTeamMembers.small.count-3 .container {
max-width: calc(276px * 3 + 24px * 2);
}
.VPTeamMembers.medium .container { .VPTeamMembers.medium .container {
grid-template-columns: repeat(auto-fit, minmax(256px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(256px, 1fr));
@@ -49,8 +60,12 @@ const classes = computed(() => [
} }
} }
.VPTeamMembers.medium.count-1 .container { max-width: 368px; } .VPTeamMembers.medium.count-1 .container {
.VPTeamMembers.medium.count-2 .container { max-width: calc(368px * 2 + 24px); } max-width: 368px;
}
.VPTeamMembers.medium.count-2 .container {
max-width: calc(368px * 2 + 24px);
}
.container { .container {
display: grid; display: grid;
@@ -58,4 +73,4 @@ const classes = computed(() => [
margin: 0 auto; margin: 0 auto;
max-width: 1152px; max-width: 1152px;
} }
</style> </style>

View File

@@ -1,22 +1,20 @@
<script setup > <script setup>
defineProps({ defineProps({
size: String, size: String,
member: Object, member: Object,
blank: Boolean blank: Boolean,
}) });
const goUrl = (url) => { const goUrl = (url) => {
window.open(url, "_blank") window.open(url, "_blank");
} };
</script> </script>
<template> <template>
<article class="VPTeamMembersItem" :class="[size ?? 'medium']"> <article class="VPTeamMembersItem" :class="[size ?? 'medium']">
<div class="profile" @click="goUrl(member.url)" v-if="!blank"> <div class="profile" @click="goUrl(member.url)" v-if="!blank">
<figure class="avatar"> <figure class="avatar">
<img class="avatar-img" :src="member.avatar" :alt="member.name"> <img class="avatar-img" :src="member.avatar" :alt="member.name" />
</figure> </figure>
<div class="data"> <div class="data">
<div class="author_name"> <div class="author_name">
@@ -26,18 +24,24 @@ const goUrl = (url) => {
<span v-if="member.title" class="title"> <span v-if="member.title" class="title">
{{ member.title }} {{ member.title }}
</span> </span>
<span v-if="member.title && member.org" class="at"> <span v-if="member.title && member.org" class="at"> @ </span>
@
</span>
</p> </p>
<p v-if="member.description" class="desc" @click.stop> <p v-if="member.description" class="desc" @click.stop>
{{ member.description }} {{ member.description }}
</p> </p>
<div v-if="member.url" class="links"> <div v-if="member.url" class="links">
<svg v-if="member.rss" @click="goUrl(member.rss)" xmlns="http://www.w3.org/2000/svg" width="16" height="16" <svg
viewBox="0 0 16 16"> v-if="member.rss"
<path fill="currentColor" @click="goUrl(member.rss)"
d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0a8 8 0 0 0-8-8a1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6a1 1 0 1 1-2 0a4 4 0 0 0-4-4a1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3z" /> xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
>
<path
fill="currentColor"
d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0a8 8 0 0 0-8-8a1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6a1 1 0 1 1-2 0a4 4 0 0 0-4-4a1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3z"
/>
</svg> </svg>
</div> </div>
</div> </div>
@@ -160,16 +164,21 @@ const goUrl = (url) => {
object-fit: cover; object-fit: cover;
} }
.author_name,.more { .author_name,
.more {
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
} }
.more{ .more {
color: var(--vp-c-text-2); color: var(--vp-c-text-2);
} }
.more:hover {
color: var(--vp-c-brand-lighter);
}
.affiliation { .affiliation {
margin: 0; margin: 0;
font-weight: 500; font-weight: 500;
@@ -211,11 +220,11 @@ const goUrl = (url) => {
} }
.links svg { .links svg {
margin: 0 .5rem; margin: 0 0.5rem;
} }
.links :hover { .links :hover {
fill: var(--vp-c-brand-lighter) fill: var(--vp-c-brand-lighter);
} }
.sp-link { .sp-link {
@@ -243,4 +252,5 @@ const goUrl = (url) => {
width: 16px; width: 16px;
height: 16px; height: 16px;
fill: currentColor; fill: currentColor;
}</style> }
</style>

View File

@@ -1,20 +1,20 @@
<template> <template>
<div class="download"> <div class="download">
<button @click='download()'>📎下载附件代码</button> <button @click="download()">📎下载附件代码</button>
</div> </div>
</template> </template>
<script setup> <script setup>
import { defineProps } from 'vue' import { defineProps } from "vue";
const url = defineProps({ const url = defineProps({
url: { url: {
type: String, type: String,
default: '' default: "",
} },
}) });
const download = () => { const download = () => {
window.open(url.url) window.open(url.url);
} };
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,77 +1,115 @@
<script setup> <script setup>
import { computed } from 'vue'; import { computed } from "vue";
const props = defineProps(['modelValue', 'pageTotal']) const props = defineProps(["modelValue", "pageTotal"]);
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(["update:modelValue"]);
const onPrev = () => { const onPrev = () => {
if (props.modelValue <= 1) return; // 限制上一页翻页按钮的边界 if (props.modelValue <= 1) return; // 限制上一页翻页按钮的边界
emit('update:modelValue', props.modelValue - 1) emit("update:modelValue", props.modelValue - 1);
} };
const onNext = () => { const onNext = () => {
if (props.modelValue >= props.pageTotal) return; // 限制下一页翻页按钮的边界 if (props.modelValue >= props.pageTotal) return; // 限制下一页翻页按钮的边界
emit('update:modelValue', props.modelValue + 1) emit("update:modelValue", props.modelValue + 1);
} };
const setPageNum = (pageNum) => { const setPageNum = (pageNum) => {
if (typeof pageNum !== 'number') return; //如果pageNum不是数值类型则返回 if (typeof pageNum !== "number") return; //如果pageNum不是数值类型则返回
if (pageNum < 1) return; // 限制上一页翻页按钮的边界 if (pageNum < 1) return; // 限制上一页翻页按钮的边界
if (pageNum > props.pageTotal) return; // 限制下一页翻页按钮的边界 if (pageNum > props.pageTotal) return; // 限制下一页翻页按钮的边界
emit('update:modelValue', pageNum) emit("update:modelValue", pageNum);
} };
const genPageArray = (current, total, size) => { const genPageArray = (current, total, size) => {
let arr = [] let arr = [];
if (total < size + 2) { if (total < size + 2) {
arr = Array.from({ length: total }, (v, k) => k + 1) arr = Array.from({ length: total }, (v, k) => k + 1);
} else if (current < size - 2) { } else if (current < size - 2) {
arr = Array.from(function* gen(i, l) { while (i < l) yield i++; }(1, size - 2 + 1)) arr = Array.from(
arr.push('...') (function* gen(i, l) {
arr.push(total) while (i < l) yield i++;
})(1, size - 2 + 1)
);
arr.push("...");
arr.push(total);
} else if (total - current < size - 2) { } else if (total - current < size - 2) {
arr.push(1) arr.push(1);
arr.push('...') arr.push("...");
arr = arr.concat(Array.from(function* gen(i, l) { while (i < l) yield i++; }(total - size + 2, total + 1))) arr = arr.concat(
Array.from(
(function* gen(i, l) {
while (i < l) yield i++;
})(total - size + 2, total + 1)
)
);
} else { } else {
arr.push(1) arr.push(1);
arr.push('...') arr.push("...");
arr = arr.concat(Array.from(function* gen(i, l) { while (i < l) yield i++; }(current - Math.floor((size - 4) / 2), current - Math.floor((size - 4) / 2) + size - 4 + 1))) arr = arr.concat(
arr.push('...') Array.from(
arr.push(total) (function* gen(i, l) {
while (i < l) yield i++;
})(
current - Math.floor((size - 4) / 2),
current - Math.floor((size - 4) / 2) + size - 4 + 1
)
)
);
arr.push("...");
arr.push(total);
} }
return arr return arr;
} };
const pageArrayLg = computed(() => { const pageArrayLg = computed(() => {
const current = props.modelValue const current = props.modelValue;
const total = props.pageTotal const total = props.pageTotal;
return genPageArray(current, total, 17) return genPageArray(current, total, 17);
}) });
const pageArrayMd = computed(() => { const pageArrayMd = computed(() => {
const current = props.modelValue const current = props.modelValue;
const total = props.pageTotal const total = props.pageTotal;
return genPageArray(current, total, 10) return genPageArray(current, total, 10);
}) });
const pageArraySm = computed(() => { const pageArraySm = computed(() => {
const current = props.modelValue const current = props.modelValue;
const total = props.pageTotal const total = props.pageTotal;
return genPageArray(current, total, 6) return genPageArray(current, total, 6);
}) });
</script> </script>
<template> <template>
<div> <div>
<div class="container" v-if="props.pageTotal > 1"> <div class="container" v-if="props.pageTotal > 1">
<div class="paper-item" @click="onPrev" style="display: flex;">&lt;</div> <div class="paper-item" @click="onPrev" style="display: flex">&lt;</div>
<div v-for="(item, index) in pageArrayLg" :key="index" @click="setPageNum(item)" class="paper-item paper-lg" <div
:class="{ 'active': item == props.modelValue }">{{ item }}</div> v-for="(item, index) in pageArrayLg"
<div v-for="(item, index) in pageArrayMd" :key="index" @click="setPageNum(item)" class="paper-item paper-md" :key="index"
:class="{ 'active': item == props.modelValue }">{{ item }}</div> @click="setPageNum(item)"
<div v-for="(item, index) in pageArraySm" :key="index" @click="setPageNum(item)" class="paper-item paper-sm" class="paper-item paper-lg"
:class="{ 'active': item == props.modelValue }">{{ item }}</div> :class="{ active: item == props.modelValue }"
<div class="paper-item" @click="onNext" style="display: flex;">></div> >
{{ item }}
</div>
<div
v-for="(item, index) in pageArrayMd"
:key="index"
@click="setPageNum(item)"
class="paper-item paper-md"
:class="{ active: item == props.modelValue }"
>
{{ item }}
</div>
<div
v-for="(item, index) in pageArraySm"
:key="index"
@click="setPageNum(item)"
class="paper-item paper-sm"
:class="{ active: item == props.modelValue }"
>
{{ item }}
</div>
<div class="paper-item" @click="onNext" style="display: flex">></div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.container { .container {
margin: 1rem 0; margin: 1rem 0;
@@ -82,7 +120,6 @@ const pageArraySm = computed(() => {
} }
@media screen and (min-width: 780px) { @media screen and (min-width: 780px) {
.paper-lg, .paper-lg,
.paper-sm { .paper-sm {
display: none; display: none;
@@ -94,7 +131,6 @@ const pageArraySm = computed(() => {
} }
@media screen and (min-width: 1024px) { @media screen and (min-width: 1024px) {
.paper-md, .paper-md,
.paper-sm { .paper-sm {
display: none; display: none;
@@ -106,7 +142,6 @@ const pageArraySm = computed(() => {
} }
@media screen and (max-width: 780px) { @media screen and (max-width: 780px) {
.paper-lg, .paper-lg,
.paper-md { .paper-md {
display: none; display: none;
@@ -120,12 +155,12 @@ const pageArraySm = computed(() => {
.paper-item { .paper-item {
height: 2rem; height: 2rem;
width: 2rem; width: 2rem;
margin: 0 .5rem; margin: 0 0.5rem;
padding: 0rem .25rem; padding: 0rem 0.25rem;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-color: #f6f6f7; background-color: #f6f6f7;
border-radius: .25rem; border-radius: 0.25rem;
cursor: pointer; cursor: pointer;
} }
@@ -142,8 +177,8 @@ const pageArraySm = computed(() => {
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.paper-item {
.paper-item { background-color: #9ca3af0d;
background-color: #9ca3af0d; }
} }
}</style> </style>

View File

@@ -1,99 +1,105 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, ref } from 'vue' import { computed, reactive, ref } from "vue";
import type { CSSProperties } from 'vue' import type { CSSProperties } from "vue";
import { useMounted, useParallax } from '@vueuse/core' import { useMounted, useParallax } from "@vueuse/core";
const target = ref(null) const target = ref(null);
const parallax = reactive(useParallax(target));
const parallax = reactive(useParallax(target))
const targetStyle: CSSProperties = { const targetStyle: CSSProperties = {
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'center', justifyContent: "center",
userSelect: 'none', userSelect: "none",
transition: '.3s ease-out all', transition: ".3s ease-out all",
} };
const cardWindowStyle: CSSProperties = { const cardWindowStyle: CSSProperties = {
overflow: 'hidden', overflow: "hidden",
fontSize: '6rem', fontSize: "6rem",
position: 'absolute', position: "absolute",
top: 'calc(50% - 1em)', top: "calc(50% - 1em)",
left: 'calc(50% - 1em)', left: "calc(50% - 1em)",
height: '2em', height: "2em",
width: '2em', width: "2em",
margin: 'auto', margin: "auto",
} };
const layerBase: CSSProperties = { const layerBase: CSSProperties = {
position: 'absolute', position: "absolute",
userSelect: 'none', userSelect: "none",
"-webkit-user-drag": "none", "-webkit-user-drag": "none",
height: '100%', height: "100%",
width: '100%', width: "100%",
transition: '.3s ease-out all', transition: ".3s ease-out all",
} };
const containerStyle: CSSProperties = { const containerStyle: CSSProperties = {
margin: '3em auto', margin: "3em auto",
perspective: '300px', perspective: "300px",
} };
const layer0 = computed(() => ({ const layer0 = computed(() => ({
...layerBase, ...layerBase,
content:'url(https://cdn.xyxsw.site/hdu-cs-wiki_main.png)', content: "url(https://cdn.xyxsw.site/hdu-cs-wiki_main.png)",
transform: `translateX(${parallax.tilt * 10}px) translateY(${parallax.roll * 10 transform: `translateX(${parallax.tilt * 10}px) translateY(${
}px) scale(1)`, parallax.roll * 10
})) }px) scale(1)`,
}));
const layer1 = computed(() => ({ const layer1 = computed(() => ({
content:'url(https://cdn.xyxsw.site/sparkles.gif)', content: "url(https://cdn.xyxsw.site/sparkles.gif)",
userSelect: 'none', userSelect: "none",
"-webkit-user-drag": "none", "-webkit-user-drag": "none",
transform: `translateX(${parallax.tilt * 20}px) translateY(${parallax.roll * 20 transform: `translateX(${parallax.tilt * 20}px) translateY(${
}px) scale(1.33)`, parallax.roll * 20
'background-blend-mode': 'overlay', }px) scale(1.33)`,
filter: 'brightness(1) contrast(1)', "background-blend-mode": "overlay",
'mix-blend-mode': 'color-dodge', filter: "brightness(1) contrast(1)",
opacity: '1', "mix-blend-mode": "color-dodge",
position: 'absolute', opacity: "1",
})) position: "absolute",
}));
const layer2 = computed(() => ({ const layer2 = computed(() => ({
transform: `translateX(${parallax.tilt * 30}px) translateY(${parallax.roll * 30 transform: `translateX(${parallax.tilt * 30}px) translateY(${
}px) scale(1.33)`, parallax.roll * 30
'background-image': 'linear-gradient(115deg,transparent 0%,#ec9bb6 25%,transparent 47%,transparent 53%,#ccac6f 75%,transparent 100%)', }px) scale(1.33)`,
opacity: '.18', "background-image":
filter: 'brightness(.5) contrast(1)', "linear-gradient(115deg,transparent 0%,#ec9bb6 25%,transparent 47%,transparent 53%,#ccac6f 75%,transparent 100%)",
width: '100%', opacity: ".18",
height: '100%', filter: "brightness(.5) contrast(1)",
position: 'absolute', width: "100%",
})) height: "100%",
position: "absolute",
}));
const layer3 = computed(() => ({ const layer3 = computed(() => ({
...layerBase, ...layerBase,
transform: `translateX(${parallax.tilt * 40}px) translateY(${parallax.roll * 40 transform: `translateX(${parallax.tilt * 40}px) translateY(${
}px) scale(1.33)`, parallax.roll * 40
})) }px) scale(1.33)`,
}));
const layer4 = layerBase const layer4 = layerBase;
const cardStyle = computed(() => ({ const cardStyle = computed(() => ({
background: '', background: "",
height: '14rem', height: "14rem",
width: '20rem', width: "20rem",
borderRadius: '20px', borderRadius: "20px",
border: '1px solid #000000', border: "1px solid #000000",
overflow: 'hidden', overflow: "hidden",
transition: '.3s ease-out all', transition: ".3s ease-out all",
boxShadow: '-20px -20px 30px -25px #11e8da, 20px 20px 30px -25px #1ea5e6, -7px -7px 10px -5px #11e8da, 7px 7px 10px -5px #1ea5e6, 0 0 13px 4px rgba(255,255,255,0.3),0 55px 35px -20px rgba(0, 0, 0, 0.5)', boxShadow:
transform: `rotateX(${parallax.roll * 20}deg) rotateY(${parallax.tilt * 20 "-20px -20px 30px -25px #11e8da, 20px 20px 30px -25px #1ea5e6, -7px -7px 10px -5px #11e8da, 7px 7px 10px -5px #1ea5e6, 0 0 13px 4px rgba(255,255,255,0.3),0 55px 35px -20px rgba(0, 0, 0, 0.5)",
}deg)`, transform: `rotateX(${parallax.roll * 20}deg) rotateY(${
})) parallax.tilt * 20
}deg)`,
}));
</script> </script>
<script lang="ts"> <script lang="ts">
export default { export default {
inheritAttrs: false inheritAttrs: false,
} };
</script> </script>
<template> <template>
@@ -103,10 +109,9 @@ export default {
<div :style="cardStyle"> <div :style="cardStyle">
<div id="kirakira"></div> <div id="kirakira"></div>
<div :style="layer2"></div> <div :style="layer2"></div>
<img :style="layer1" <img :style="layer1" class="image" />
class="image">
<div :style="cardWindowStyle"> <div :style="cardWindowStyle">
<img :style="layer0" class="image"> <img :style="layer0" class="image" />
</div> </div>
</div> </div>
</div> </div>
@@ -122,58 +127,60 @@ export default {
top: 0; top: 0;
left: 0; left: 0;
z-index: 1; z-index: 1;
transition: .3s ease-out; transition: 0.3s ease-out;
} }
#kirakira:hover { #kirakira:hover {
background-image: linear-gradient(110deg, background-image: linear-gradient(
transparent 25%, 110deg,
#ec9bb6 48%, transparent 25%,
#ccac6f 52%, #ec9bb6 48%,
transparent 75%); #ccac6f 52%,
transparent 75%
);
background-position: 50% 50%; background-position: 50% 50%;
background-size: 250% 250%; background-size: 250% 250%;
opacity: .18; opacity: 0.18;
filter: brightness(.66) contrast(1.33); filter: brightness(0.66) contrast(1.33);
transition: none; transition: none;
background-repeat: no-repeat; background-repeat: no-repeat;
mix-blend-mode: color-dodge; mix-blend-mode: color-dodge;
transition: all .33s ease; transition: all 0.33s ease;
animation: holoGradient 12s ease 0s 1, animation: holoGradient 12s ease 0s 1;
} }
@keyframes holoGradient { @keyframes holoGradient {
0%, 0%,
100% { 100% {
opacity: 0.3; opacity: 0.3;
background-position: 50% 50%; background-position: 50% 50%;
filter: brightness(.5) contrast(1); filter: brightness(0.5) contrast(1);
} }
5%, 5%,
9% { 9% {
background-position: 100% 100%; background-position: 100% 100%;
opacity: 0.1; opacity: 0.1;
filter: brightness(.75) contrast(1.25); filter: brightness(0.75) contrast(1.25);
} }
13%, 13%,
17% { 17% {
background-position: 0% 0%; background-position: 0% 0%;
opacity: .18; opacity: 0.18;
} }
35%, 35%,
39% { 39% {
background-position: 100% 100%; background-position: 100% 100%;
opacity: 0.2; opacity: 0.2;
filter: brightness(.5) contrast(1); filter: brightness(0.5) contrast(1);
} }
55% { 55% {
background-position: 0% 0%; background-position: 0% 0%;
opacity: 0.3; opacity: 0.3;
filter: brightness(.75) contrast(1.25); filter: brightness(0.75) contrast(1.25);
} }
}</style> }
</style>

View File

@@ -8,9 +8,16 @@
{{ Content }} {{ Content }}
</div> </div>
<div class="badge-list" v-if="!inMobile"> <div class="badge-list" v-if="!inMobile">
<a class="split" v-if="Author" :href="withBase(AuthorURL)" @click.stop target="_blank">{{ Author }}</a> <a
class="split"
v-if="Author"
:href="withBase(AuthorURL)"
@click.stop
target="_blank"
>{{ Author }}</a
>
<span class="split">{{ showTime }}</span> <span class="split">{{ showTime }}</span>
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span> <span class="split" v-if="tag?.length">{{ tag.join(" · ") }}</span>
</div> </div>
</div> </div>
<div <div
@@ -20,40 +27,47 @@
></div> ></div>
</div> </div>
<div class="badge-list" v-if="inMobile"> <div class="badge-list" v-if="inMobile">
<a class="split" v-if="Author" :href="withBase(AuthorURL)" @click.stop target="_blank">{{ Author }}</a> <a
class="split"
v-if="Author"
:href="withBase(AuthorURL)"
@click.stop
target="_blank"
>{{ Author }}</a
>
<span class="split">{{ showTime }}</span> <span class="split">{{ showTime }}</span>
<span class="split" v-if="tag?.length">{{ tag.join(' · ') }}</span> <span class="split" v-if="tag?.length">{{ tag.join(" · ") }}</span>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { withBase } from 'vitepress' import { withBase } from "vitepress";
import { computed } from 'vue' import { computed } from "vue";
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from "@vueuse/core";
const openBlog = (url) => { const openBlog = (url) => {
window.open(url, '_blank') window.open(url, "_blank");
} };
const { width } = useWindowSize() const { width } = useWindowSize();
const inMobile = computed(() => width.value <= 500) const inMobile = computed(() => width.value <= 500);
function formatDate(d, fmt = 'yyyy-MM-dd hh:mm:ss') { function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
if (!(d instanceof Date)) { if (!(d instanceof Date)) {
d = new Date(d) d = new Date(d);
} }
const o = { const o = {
'M+': d.getMonth() + 1, // 月份 "M+": d.getMonth() + 1, // 月份
'd+': d.getDate(), // 日 "d+": d.getDate(), // 日
'h+': d.getHours(), // 小时 "h+": d.getHours(), // 小时
'm+': d.getMinutes(), // 分 "m+": d.getMinutes(), // 分
's+': d.getSeconds(), // 秒 "s+": d.getSeconds(), // 秒
'q+': Math.floor((d.getMonth() + 3) / 3), // 季度 "q+": Math.floor((d.getMonth() + 3) / 3), // 季度
S: d.getMilliseconds() // 毫秒 S: d.getMilliseconds(), // 毫秒
} };
if (/(y+)/.test(fmt)) { if (/(y+)/.test(fmt)) {
fmt = fmt.replace( fmt = fmt.replace(
RegExp.$1, RegExp.$1,
`${d.getFullYear()}`.substr(4 - RegExp.$1.length) `${d.getFullYear()}`.substr(4 - RegExp.$1.length)
) );
} }
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
for (const k in o) { for (const k in o) {
@@ -61,33 +75,33 @@ function formatDate(d, fmt = 'yyyy-MM-dd hh:mm:ss') {
fmt = fmt.replace( fmt = fmt.replace(
RegExp.$1, RegExp.$1,
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length) RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
) );
} }
return fmt return fmt;
} }
function formatShowDate(date) { function formatShowDate(date) {
const source = date ? +new Date(date) : +new Date() const source = date ? +new Date(date) : +new Date();
const now = +new Date() const now = +new Date();
const diff = now - source const diff = now - source;
const oneSeconds = 1000 const oneSeconds = 1000;
const oneMinute = oneSeconds * 60 const oneMinute = oneSeconds * 60;
const oneHour = oneMinute * 60 const oneHour = oneMinute * 60;
const oneDay = oneHour * 24 const oneDay = oneHour * 24;
const oneWeek = oneDay * 7 const oneWeek = oneDay * 7;
if (diff < oneMinute) { if (diff < oneMinute) {
return `${Math.floor(diff / oneSeconds)}秒前` return `${Math.floor(diff / oneSeconds)}秒前`;
} }
if (diff < oneHour) { if (diff < oneHour) {
return `${Math.floor(diff / oneMinute)}分钟前` return `${Math.floor(diff / oneMinute)}分钟前`;
} }
if (diff < oneDay) { if (diff < oneDay) {
return `${Math.floor(diff / oneHour)}小时前` return `${Math.floor(diff / oneHour)}小时前`;
} }
if (diff < oneWeek) { if (diff < oneWeek) {
return `${Math.floor(diff / oneDay)}天前` return `${Math.floor(diff / oneDay)}天前`;
} }
return formatDate(new Date(date), 'yyyy-MM-dd') return formatDate(new Date(date), "yyyy-MM-dd");
} }
const props = defineProps({ const props = defineProps({
@@ -104,13 +118,12 @@ const props = defineProps({
required: false, required: false,
}, },
PostURL: String, PostURL: String,
AuthorURL: String AuthorURL: String,
}) });
const showTime = computed(() => { const showTime = computed(() => {
return formatShowDate(props.Date) return formatShowDate(props.Date);
}) });
</script> </script>
<style scoped> <style scoped>
@@ -127,7 +140,7 @@ const showTime = computed(() => {
opacity: 1; opacity: 1;
} }
.blog-item .pin::before { .blog-item .pin::before {
content: ''; content: "";
position: absolute; position: absolute;
width: 120%; width: 120%;
height: 30px; height: 30px;
@@ -145,8 +158,8 @@ const showTime = computed(() => {
.blog-item { .blog-item {
position: relative; position: relative;
border: 1px solid rgba(82, 82, 89, .32); border: 1px solid rgba(82, 82, 89, 0.32);
border-radius: .5rem; border-radius: 0.5rem;
padding: 1.5rem; padding: 1.5rem;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@@ -154,11 +167,10 @@ const showTime = computed(() => {
transition: all 0.3s; transition: all 0.3s;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
transition: border-color .25s; transition: border-color 0.25s;
cursor: pointer; cursor: pointer;
} }
.blog-item:hover { .blog-item:hover {
border: 1px solid #09f; border: 1px solid #09f;
} }
@@ -196,7 +208,7 @@ const showTime = computed(() => {
margin-top: 8px; margin-top: 8px;
} }
.badge-list .split:not(:last-child)::after { .badge-list .split:not(:last-child)::after {
content: ''; content: "";
display: inline-block; display: inline-block;
width: 1px; width: 1px;
height: 8px; height: 8px;
@@ -219,4 +231,4 @@ const showTime = computed(() => {
background-size: 100px 60px; background-size: 100px 60px;
} }
} }
</style> </style>

View File

@@ -1,23 +1,31 @@
<script setup> <script setup>
import { ref, computed } from 'vue'; import { ref, computed } from "vue";
import Pagination from './Pagination.vue'; import Pagination from "./Pagination.vue";
import PostItem from './PostItem.vue'; import PostItem from "./PostItem.vue";
const props= {data:(await (await fetch("https://ghproxy.com/https://raw.githubusercontent.com/NX-Official/friends-link-plus/main/output/friends.json")).json()).posts} const props = {
const pageSize = 10 data: (
await (
await fetch(
"https://ghproxy.com/https://raw.githubusercontent.com/NX-Official/friends-link-plus/main/output/friends.json"
)
).json()
).posts,
};
const pageSize = 10;
// const Date = ref('') // const Date = ref('')
const pageTotal = ref(Math.ceil(props.data.length / pageSize)) const pageTotal = ref(Math.ceil(props.data.length / pageSize));
const pageNum = ref(1) const pageNum = ref(1);
// const searchText = ref('') // const searchText = ref('')
// const onsearchText = ref('') // const onsearchText = ref('')
const pluginLists = computed(() => { const pluginLists = computed(() => {
let data_ = props.data//.filter(item => item.Date == Date.value) let data_ = props.data; //.filter(item => item.Date == Date.value)
// if (!!onsearchText.value) { // if (!!onsearchText.value) {
// data_ = data_.filter(item => item.name.indexOf(onsearchText.value) > -1) // data_ = data_.filter(item => item.name.indexOf(onsearchText.value) > -1)
// } // }
pageTotal.value = Math.ceil(data_.length / pageSize) pageTotal.value = Math.ceil(data_.length / pageSize);
data_ = data_.slice((pageNum.value - 1) * pageSize, pageNum.value * pageSize) data_ = data_.slice((pageNum.value - 1) * pageSize, pageNum.value * pageSize);
return data_ return data_;
}) });
// const setDate = (Date_) => { // const setDate = (Date_) => {
// Date.value = Date_ // Date.value = Date_
// searchText.value = "" // searchText.value = ""
@@ -59,43 +67,62 @@ const pluginLists = computed(() => {
</div> </div>
<div class="divider" style="margin-bottom: 1rem;" /> <div class="divider" style="margin-bottom: 1rem;" />
</div> --> </div> -->
<Pagination :pageTotal="pageTotal" v-model="pageNum" style="width: 100%;padding: 1rem 0;" key="0" /> <Pagination
:pageTotal="pageTotal"
v-model="pageNum"
style="width: 100%; padding: 1rem 0"
key="0"
/>
<div class="card-list"> <div class="card-list">
<PostItem v-for="(item, index) in pluginLists" :key="index" :Title="item.Title" :Content="item.Content" <PostItem
:Date="item.Date" :Author="item.Author" :tag="item?.tag" :cover="item?.cover" :PostURL="item.PostURL" :AuthorURL="item.AuthorURL"/> v-for="(item, index) in pluginLists"
:key="index"
:Title="item.Title"
:Content="item.Content"
:Date="item.Date"
:Author="item.Author"
:tag="item?.tag"
:cover="item?.cover"
:PostURL="item.PostURL"
:AuthorURL="item.AuthorURL"
/>
</div> </div>
<Pagination :pageTotal="pageTotal" v-model="pageNum" style="width: 100%;padding: 1rem 0;" key="1" /> <Pagination
:pageTotal="pageTotal"
v-model="pageNum"
style="width: 100%; padding: 1rem 0"
key="1"
/>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.tab { .tab {
grid-row-gap: .5rem; grid-row-gap: 0.5rem;
row-gap: .5rem; row-gap: 0.5rem;
margin-top: 2.5rem; margin-top: 2.5rem;
grid-template-columns: 80px auto; grid-template-columns: 80px auto;
display: grid; display: grid;
} }
.sm { .sm {
font-size: .875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
} }
.select-list { .select-list {
grid-gap: .5rem; grid-gap: 0.5rem;
gap: .5rem; gap: 0.5rem;
flex-wrap: wrap; flex-wrap: wrap;
display: flex; display: flex;
margin-bottom: .5rem; margin-bottom: 0.5rem;
} }
.select-button { .select-button {
border-radius: .25rem; border-radius: 0.25rem;
background-color: #9ca3af0d; background-color: #9ca3af0d;
padding: .125rem .5rem; padding: 0.125rem 0.5rem;
font-size: .875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
} }
@@ -111,17 +138,17 @@ const pluginLists = computed(() => {
} }
.divider { .divider {
background-color: rgba(60, 60, 67, .12); background-color: rgba(60, 60, 67, 0.12);
height: 1px; height: 1px;
} }
.search { .search {
padding: .5rem; padding: 0.5rem;
display: flex; display: flex;
} }
.icon { .icon {
margin-right: .5rem; margin-right: 0.5rem;
} }
.search-input { .search-input {
@@ -138,12 +165,12 @@ const pluginLists = computed(() => {
.card { .card {
display: block; display: block;
border: 1px solid rgba(82, 82, 89, .32); border: 1px solid rgba(82, 82, 89, 0.32);
border-radius: .5rem; border-radius: 0.5rem;
padding: 1.5rem; padding: 1.5rem;
width: 100%; width: 100%;
height: 100%; height: 100%;
transition: border-color .25s; transition: border-color 0.25s;
cursor: pointer; cursor: pointer;
} }
@@ -173,24 +200,24 @@ const pluginLists = computed(() => {
} }
.card-des { .card-des {
color: #3C3C43; color: #3c3c43;
opacity: .7; opacity: 0.7;
font-size: .875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
margin-top: .3rem; margin-top: 0.3rem;
} }
.card-tags { .card-tags {
display: flex; display: flex;
margin-top: .5rem; margin-top: 0.5rem;
margin-bottom: .5rem; margin-bottom: 0.5rem;
font-size: .875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
} }
.card-tag { .card-tag {
margin: 0 .5rem; margin: 0 0.5rem;
padding: 0 .5rem; padding: 0 0.5rem;
background-color: #9ca3af2b; background-color: #9ca3af2b;
} }
@@ -200,12 +227,12 @@ const pluginLists = computed(() => {
} }
.card-details { .card-details {
margin: .8rem 0 .5rem 0; margin: 0.8rem 0 0.5rem 0;
} }
.card-text { .card-text {
margin-left: .5rem; margin-left: 0.5rem;
font-size: .95rem; font-size: 0.95rem;
line-height: 1.5rem; line-height: 1.5rem;
} }
@@ -219,9 +246,9 @@ const pluginLists = computed(() => {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100%; width: 100%;
padding: .4rem 1rem; padding: 0.4rem 1rem;
background-color: #f6f6f7; background-color: #f6f6f7;
border-radius: .5rem; border-radius: 0.5rem;
cursor: pointer; cursor: pointer;
} }
@@ -234,7 +261,6 @@ const pluginLists = computed(() => {
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.card-button { .card-button {
background-color: #9ca3af0d; background-color: #9ca3af0d;
} }
@@ -242,6 +268,5 @@ const pluginLists = computed(() => {
.card-des { .card-des {
color: #9ca3af; color: #9ca3af;
} }
} }
</style> </style>

View File

@@ -1,21 +1,65 @@
<template> <template>
<div ref="NotebookFragment" class="notebook-fragment"> <div ref="NotebookFragment" class="notebook-fragment">
<div ref="loading" id="loading"> <div ref="loading" id="loading">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" id="loadingsvg"> <svg
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"> xmlns="http://www.w3.org/2000/svg"
<path stroke-dasharray="2 4" stroke-dashoffset="6" width="32"
d="M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3"> height="32"
<animate attributeName="stroke-dashoffset" dur="0.6s" repeatCount="indefinite" values="6;0" /> viewBox="0 0 24 24"
id="loadingsvg"
>
<g
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-width="2"
>
<path
stroke-dasharray="2 4"
stroke-dashoffset="6"
d="M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3"
>
<animate
attributeName="stroke-dashoffset"
dur="0.6s"
repeatCount="indefinite"
values="6;0"
/>
</path> </path>
<path stroke-dasharray="30" stroke-dashoffset="30" <path
d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21"> stroke-dasharray="30"
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.1s" dur="0.3s" values="30;0" /> stroke-dashoffset="30"
d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21"
>
<animate
fill="freeze"
attributeName="stroke-dashoffset"
begin="0.1s"
dur="0.3s"
values="30;0"
/>
</path> </path>
<path stroke-dasharray="10" stroke-dashoffset="10" d="M12 16v-7.5"> <path stroke-dasharray="10" stroke-dashoffset="10" d="M12 16v-7.5">
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.5s" dur="0.2s" values="10;0" /> <animate
fill="freeze"
attributeName="stroke-dashoffset"
begin="0.5s"
dur="0.2s"
values="10;0"
/>
</path> </path>
<path stroke-dasharray="6" stroke-dashoffset="6" d="M12 8.5l3.5 3.5M12 8.5l-3.5 3.5"> <path
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.7s" dur="0.2s" values="6;0" /> stroke-dasharray="6"
stroke-dashoffset="6"
d="M12 8.5l3.5 3.5M12 8.5l-3.5 3.5"
>
<animate
fill="freeze"
attributeName="stroke-dashoffset"
begin="0.7s"
dur="0.2s"
values="6;0"
/>
</path> </path>
</g> </g>
</svg> </svg>
@@ -49,11 +93,11 @@ onMounted(async () => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 2rem; gap: 2rem;
animation: rainbow 10s cubic-bezier(0.1, 0.7, 1.0, 0.1) infinite !important; animation: rainbow 10s cubic-bezier(0.1, 0.7, 1, 0.1) infinite !important;
} }
#loadingsvg { #loadingsvg {
width: 5rem; width: 5rem;
height: 5rem; height: 5rem;
} }
</style> </style>

View File

@@ -1,20 +1,20 @@
<template> <template>
<div class="pdf"> <div class="pdf">
<button @click='open'>打开PDF</button> <button @click="open">打开PDF</button>
</div> </div>
</template> </template>
<script setup> <script setup>
import { defineProps } from 'vue' import { defineProps } from "vue";
const url = defineProps({ const url = defineProps({
url: { url: {
type: String, type: String,
default: '' default: "",
} },
}) });
const open = () => { const open = () => {
window.open(url.url) window.open(url.url);
} };
</script> </script>
<style scoped> <style scoped>