1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-07-04 18:39:22 +00:00
This commit is contained in:
SunWuyuan 2025-03-22 18:21:51 +08:00
parent c0d235a0fe
commit 16da372bbc
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
6 changed files with 1587 additions and 47 deletions

View File

@ -15,6 +15,7 @@
"idb": "^8.0.2", "idb": "^8.0.2",
"pinyin-pro": "^3.26.0", "pinyin-pro": "^3.26.0",
"roboto-fontface": "*", "roboto-fontface": "*",
"typewriter-effect": "^2.21.0",
"vue": "^3.4.31", "vue": "^3.4.31",
"vue-masonry-wall": "^0.3.2", "vue-masonry-wall": "^0.3.2",
"vue-waterfall-plugin-next": "^2.6.5", "vue-waterfall-plugin-next": "^2.6.5",

87
pnpm-lock.yaml generated
View File

@ -23,6 +23,9 @@ importers:
roboto-fontface: roboto-fontface:
specifier: '*' specifier: '*'
version: 0.10.0 version: 0.10.0
typewriter-effect:
specifier: ^2.21.0
version: 2.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
vue: vue:
specifier: ^3.4.31 specifier: ^3.4.31
version: 3.5.13 version: 3.5.13
@ -1105,6 +1108,9 @@ packages:
isexe@2.0.0: isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-tokens@9.0.0: js-tokens@9.0.0:
resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==}
@ -1151,6 +1157,10 @@ packages:
lodash@4.17.21: lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
magic-string-ast@0.6.2: magic-string-ast@0.6.2:
resolution: {integrity: sha512-oN3Bcd7ZVt+0VGEs7402qR/tjgjbM7kPlH/z7ufJnzTLVBzXJITRHOJiwMmmYMgZfdoWQsfQcY+iKlxiBppnMA==} resolution: {integrity: sha512-oN3Bcd7ZVt+0VGEs7402qR/tjgjbM7kPlH/z7ufJnzTLVBzXJITRHOJiwMmmYMgZfdoWQsfQcY+iKlxiBppnMA==}
engines: {node: '>=16.14.0'} engines: {node: '>=16.14.0'}
@ -1205,6 +1215,10 @@ packages:
nth-check@2.1.1: nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-inspect@1.13.3: object-inspect@1.13.3:
resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1259,6 +1273,9 @@ packages:
pathe@1.1.2: pathe@1.1.2:
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@ -1304,6 +1321,9 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
proxy-from-env@1.1.0: proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@ -1314,6 +1334,21 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
raf@3.4.1:
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
react-dom@18.3.1:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies:
react: ^18.3.1
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
readdirp@3.6.0: readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}
@ -1493,6 +1528,9 @@ packages:
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
hasBin: true hasBin: true
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
scule@1.3.0: scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
@ -1605,6 +1643,12 @@ packages:
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
typewriter-effect@2.21.0:
resolution: {integrity: sha512-Y3VL1fuJpUBj0gS4OTXBLzy1gnYTYaBuVuuO99tGNyTkkub5CXi+b/hsV7Og9fp6HlhogOwWJwgq7iXI5sQlEg==}
peerDependencies:
react: ^17.x || ^18.x
react-dom: ^17.x || ^18.x
ufo@1.5.4: ufo@1.5.4:
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
@ -2864,6 +2908,8 @@ snapshots:
isexe@2.0.0: {} isexe@2.0.0: {}
js-tokens@4.0.0: {}
js-tokens@9.0.0: {} js-tokens@9.0.0: {}
js-yaml@4.1.0: js-yaml@4.1.0:
@ -2904,6 +2950,10 @@ snapshots:
lodash@4.17.21: {} lodash@4.17.21: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
magic-string-ast@0.6.2: magic-string-ast@0.6.2:
dependencies: dependencies:
magic-string: 0.30.12 magic-string: 0.30.12
@ -2954,6 +3004,8 @@ snapshots:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
object-assign@4.1.1: {}
object-inspect@1.13.3: {} object-inspect@1.13.3: {}
object-keys@1.1.1: {} object-keys@1.1.1: {}
@ -3013,6 +3065,8 @@ snapshots:
pathe@1.1.2: {} pathe@1.1.2: {}
performance-now@2.1.0: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@2.3.1: {} picomatch@2.3.1: {}
@ -3048,12 +3102,34 @@ snapshots:
prelude-ls@1.2.1: {} prelude-ls@1.2.1: {}
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
proxy-from-env@1.1.0: {} proxy-from-env@1.1.0: {}
punycode@2.3.1: {} punycode@2.3.1: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
raf@3.4.1:
dependencies:
performance-now: 2.1.0
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
react: 18.3.1
scheduler: 0.23.2
react-is@16.13.1: {}
react@18.3.1:
dependencies:
loose-envify: 1.4.0
readdirp@3.6.0: readdirp@3.6.0:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
@ -3224,6 +3300,10 @@ snapshots:
immutable: 4.3.7 immutable: 4.3.7
source-map-js: 1.2.1 source-map-js: 1.2.1
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
scule@1.3.0: {} scule@1.3.0: {}
semver@6.3.1: {} semver@6.3.1: {}
@ -3355,6 +3435,13 @@ snapshots:
is-typed-array: 1.1.13 is-typed-array: 1.1.13
possible-typed-array-names: 1.0.0 possible-typed-array-names: 1.0.0
typewriter-effect@2.21.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
prop-types: 15.8.1
raf: 3.4.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
ufo@1.5.4: {} ufo@1.5.4: {}
unbox-primitive@1.0.2: unbox-primitive@1.0.2:

View File

@ -8,57 +8,162 @@
</v-card-item> </v-card-item>
<v-card-text> <v-card-text>
<v-row justify="center" align="center"> <v-row>
<v-col cols="12" md="8" class="text-center"> <v-col cols="12" md="8" class="mx-auto">
<v-avatar size="120" class="mb-4"> <div class="d-flex flex-column align-start">
<v-img <v-avatar size="120" class="mb-4">
src="https://avatars.githubusercontent.com/u/88357633?v=4" <v-img
alt="Sunwuyuan" src="https://github.com/Sunwuyuan.png"
/> alt="Sunwuyuan"
</v-avatar> />
</v-avatar>
<h2 class="text-h5 mb-2">Classworks</h2> <h2 class="text-h5 mb-2">Classworks</h2>
<p class="text-body-1 mb-4"> <p class="text-body-1 mb-4">适用于班级大屏的作业板小工具</p>
<a
href="https://github.com/sunwuyuan" <div class="d-flex gap-2 flex-wrap mb-6">
target="_blank" <v-btn
class="text-decoration-none font-weight-medium" color="red"
>Sunwuyuan</a> 开发 variant="tonal"
</p> href="https://github.com/ClassworksDev/Classworks/issues"
target="_blank"
prepend-icon="mdi-bug"
>
报告问题
</v-btn>
<v-btn
variant="text"
href="https://github.com/ClassworksDev/Classworks"
target="_blank"
prepend-icon="mdi-github"
>
前端
</v-btn>
<v-btn
variant="text"
href="https://github.com/ClassworksDev/ClassworksServer"
target="_blank"
prepend-icon="mdi-github"
>
后端
</v-btn>
</div>
<v-divider class="mb-4 w-100"></v-divider>
<h3 class="text-h6 mb-2">备注与致谢</h3>
<v-list class="mb-4 bg-transparent">
<v-list-item
href="https://github.com/EnderWolf006/HomeworkBoard"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
本项目受到 HomeworkBoard 的启发而开发
</v-list-item-title>
<v-list-item-subtitle>
感谢 EnderWolf006 (@EnderWolf) fhzit(@Hellofhz) KeyFac
等人的贡献
</v-list-item-subtitle>
</v-list-item>
<v-list-item
href="https://houlangs.com"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
Classworks <strong>厚浪云</strong>提供
</v-list-item-title>
<v-list-item-subtitle>
长江后浪推前浪 浮事新人换旧人
</v-list-item-subtitle>
</v-list-item>
<v-list-item
href="https://zerocat.houlangs.com"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
感谢 ZeroCat 社区的开发者们
</v-list-item-title>
<v-list-item-subtitle>
新一代开源编程社区
</v-list-item-subtitle>
</v-list-item>
<v-divider class="ma-1"></v-divider>
<v-list-item
href="https://github.com/HUSX100/IslandCaller"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
本项目与 IslandCaller 无关
</v-list-item-title>
<v-list-item-subtitle>
IslandCaller 是由 HUSX100 开发的基于 ClassIsland
提醒服务的轻量级点名器
</v-list-item-subtitle>
</v-list-item>
<v-list-item
href="https://classisland.tech"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
本项目与 ClassIsland 无关
</v-list-item-title>
<v-list-item-subtitle>
ClassIsland 是由 HelloWRC
开发的适用于班级大屏的课表信息显示工具
</v-list-item-subtitle>
</v-list-item>
</v-list>
<div class="d-flex justify-center gap-2 flex-wrap">
<v-btn <v-btn
color="primary" variant="text"
variant="outlined" class="mb-4"
href="https://github.com/ClassworksDev/Classworks" prepend-icon="mdi-package-variant"
target="_blank" @click="showDeps = true"
prepend-icon="mdi-github"
> >
前端 GitHub 查看使用的第三方库
</v-btn> </v-btn>
<v-btn
color="primary" <v-dialog
variant="outlined" v-model="showDeps"
href="https://github.com/ClassworksDev/ClassworksServer" transition="dialog-bottom-transition"
target="_blank" fullscreen
prepend-icon="mdi-github"
> >
后端 GitHub <v-card
</v-btn> ><v-toolbar>
<v-btn <v-btn icon="mdi-close" @click="showDeps = false"></v-btn>
color="primary" <v-toolbar-title>使用的第三方库</v-toolbar-title>
variant="outlined" <v-spacer></v-spacer>
href="https://github.com/ClassworksDev/Classworks/issues" </v-toolbar>
target="_blank" <v-card-text>
prepend-icon="mdi-bug" <v-list>
> <v-list-item
报告问题 v-for="dep in Dependencies"
</v-btn> :key="dep.name"
:href="'https://www.npmjs.com/package/' + dep.name"
target="_blank"
append-icon="mdi-link"
>
<v-list-item-title>
{{ dep.name }}
</v-list-item-title>
<v-list-item-subtitle>
v{{ dep.version }}
</v-list-item-subtitle>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-dialog>
<p class="text-caption text-medium-emphasis">
Copyright © {{ new Date().getFullYear() }} Sunwuyuan
</p>
</div> </div>
<p class="mt-4 text-caption text-medium-emphasis">
GPL License © 2024
</p>
</v-col> </v-col>
</v-row> </v-row>
</v-card-text> </v-card-text>
@ -66,7 +171,57 @@
</template> </template>
<script> <script>
import { ref, onMounted } from "vue";
import packageJson from "../../../package.json";
export default { export default {
name: 'AboutCard' name: "AboutCard",
} setup() {
const Dependencies = ref([]);
const showDeps = ref(false); //
const loadDependencies = () => {
try {
// dependencies devDependencies
const allDependencies = {
...(packageJson.dependencies || {}),
...(packageJson.devDependencies || {}),
};
//
const deps = Object.entries(allDependencies).map(([name, version]) => ({
name,
version: version.replace(/[\^~]/g, ""),
description: getDependencyDescription(name),
}));
Dependencies.value = deps;
} catch (error) {
console.error("加载依赖信息失败:", error);
Dependencies.value = [];
}
};
const getDependencyDescription = (name) => {
const descriptions = {
vue: "渐进式 JavaScript 框架",
vuetify: "材料设计组件框架",
axios: "Promise 基础的 HTTP 客户端",
pinia: "Vue 状态管理库",
"vue-router": "Vue.js 官方路由管理器",
"@vitejs/plugin-vue": "Vite 的 Vue 插件",
};
return descriptions[name] || "";
};
onMounted(() => {
loadDependencies();
});
return {
Dependencies,
showDeps,
};
},
};
</script> </script>

View File

@ -0,0 +1,158 @@
<template>
<settings-card
border
title="回声洞"
icon="mdi-thought-bubble"
@click="handleClick"
>
<v-card-text>
<div ref="typewriter" class="typewriter-text"></div>
<div ref="sourceWriter" class="source-text text-caption text-grey"></div>
</v-card-text>
<div class="d-flex align-center">
<transition name="fade">
<div v-if="currentQuote?.contributor" class="contributor">
<v-chip>
<v-avatar start>
<v-img
:src="`https://github.com/${currentQuote.contributor}.png`"
></v-img> </v-avatar
>{{ currentQuote.contributor }}
</v-chip>
</div>
</transition>
</div>
</settings-card>
</template>
<script>
import Typewriter from "typewriter-effect/dist/core";
import quotes from "@/data/echoChamber.json";
import SettingsCard from "@/components/SettingsCard.vue";
export default {
name: "EchoChamberCard",
components: { SettingsCard },
data() {
return {
typewriter: null,
sourceWriter: null,
currentQuote: {
text: this.INITIAL_TEXT,
author: this.INITIAL_SOURCE,
},
showCopySuccess: false,
hasClicked: false,
INITIAL_TEXT: "点击此处可以查看 Classworks 用户群里沙雕群友们的发言",
INITIAL_SOURCE: "点击后会复制当前句子到剪贴板中",
};
},
mounted() {
this.initTypewriters();
},
methods: {
getRandomQuote() {
const index = Math.floor(Math.random() * quotes.quotes.length);
return quotes.quotes[index];
},
initTypewriters() {
//
this.typewriter = new Typewriter(this.$refs.typewriter, {
delay: 50,
deleteSpeed: 30,
loop: false,
});
//
this.sourceWriter = new Typewriter(this.$refs.sourceWriter, {
delay: 30,
deleteSpeed: 20,
loop: false,
cursor: "",
});
//
this.typewriter.typeString(this.INITIAL_TEXT).start();
this.sourceWriter.typeString(this.INITIAL_SOURCE).start();
},
typeQuote(quote) {
this.typewriter.deleteAll(30).typeString(quote.text).start();
if (quote.author) {
this.sourceWriter.deleteAll(20).typeString(`${quote.author}`).start();
}
},
refreshQuote() {
this.currentQuote = this.getRandomQuote();
this.typeQuote(this.currentQuote);
},
async handleClick() {
if (!this.hasClicked) {
this.hasClicked = true;
this.currentQuote = this.getRandomQuote();
}
await this.copyToClipboard();
this.refreshQuote();
},
async copyToClipboard() {
if (!this.currentQuote) return;
const quote = this.currentQuote;
const text = `${quote.text}\n${
quote.author ? `作者:${quote.author}` : ""
}\n${quote.contributor ? `贡献者:${quote.contributor}` : ""}${
quote.link
? `\n来源${quote.link}`
: quote.contributor
? "\n来源https://github.com/" + quote.contributor
: ""
}`;
try {
await navigator.clipboard.writeText(text);
this.showCopySuccess = true;
} catch (err) {
console.error("复制失败:", err);
}
},
},
beforeUnmount() {
if (this.typewriter) {
this.typewriter.stop();
}
if (this.sourceWriter) {
this.sourceWriter.stop();
}
},
};
</script>
<style scoped>
.cursor-pointer {
cursor: pointer;
}
.source-text {
opacity: 0.7;
font-size: 0.9em;
}
.contributor {
opacity: 0.7;
font-size: 0.9em;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

1132
src/data/echoChamber.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,7 @@
<theme-settings-card border /> <theme-settings-card border />
</v-col> </v-col>
<!-- 开发者选项卡片 --> <!-- 开发者选项卡片 -->
<v-col :cols="12" :md="settings.developer.enabled ? 12 : 6"> <v-col :cols="12" :md="settings.developer.enabled ? 12 : 6">
<settings-card <settings-card
@ -133,6 +134,10 @@
/> />
</v-col> </v-col>
<!-- 添加回声洞卡片 -->
<v-col cols="12">
<echo-chamber-card border />
</v-col>
<!-- 关于卡片 --> <!-- 关于卡片 -->
<v-col cols="12"> <v-col cols="12">
<about-card /> <about-card />
@ -152,6 +157,7 @@ import RefreshSettingsCard from '@/components/settings/cards/RefreshSettingsCard
import DisplaySettingsCard from '@/components/settings/cards/DisplaySettingsCard.vue'; import DisplaySettingsCard from '@/components/settings/cards/DisplaySettingsCard.vue';
import DataProviderSettingsCard from '@/components/settings/cards/DataProviderSettingsCard.vue'; import DataProviderSettingsCard from '@/components/settings/cards/DataProviderSettingsCard.vue';
import ThemeSettingsCard from '@/components/settings/cards/ThemeSettingsCard.vue'; import ThemeSettingsCard from '@/components/settings/cards/ThemeSettingsCard.vue';
import EchoChamberCard from '@/components/settings/cards/EchoChamberCard.vue';
import { import {
getSetting, getSetting,
setSetting, setSetting,
@ -177,7 +183,8 @@ export default {
StudentListCard, StudentListCard,
AboutCard, AboutCard,
DataProviderSettingsCard, DataProviderSettingsCard,
ThemeSettingsCard ThemeSettingsCard,
EchoChamberCard
}, },
setup() { setup() {
const { mobile } = useDisplay(); const { mobile } = useDisplay();