first commit
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>CANTYONION.SITE</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
3040
package-lock.json
generated
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "webindex",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.8",
|
||||
"axios": "^1.7.7",
|
||||
"lxgw-wenkai-lite-webfont": "^1.7.0",
|
||||
"mitt": "^3.0.1",
|
||||
"vue": "^3.4.37",
|
||||
"vue-router": "^4.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.5.5",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"typescript": "^5.5.3",
|
||||
"vite": "^5.4.1",
|
||||
"vue-tsc": "^2.0.29"
|
||||
}
|
||||
}
|
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
11
src/App.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
8
src/api/blog.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import {request} from "@/utils/network";
|
||||
|
||||
export async function getBlogRecentPost(): Promise<IBlogResponse<IPostsData>> {
|
||||
return request({
|
||||
url: "/blog/index.php/api/posts",
|
||||
method: "get"
|
||||
})
|
||||
}
|
BIN
src/assets/1.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
src/assets/2.jpg
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
src/assets/3.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/4.jpg
Normal file
After Width: | Height: | Size: 172 KiB |
BIN
src/assets/5.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
src/assets/6.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
src/assets/7.jpg
Normal file
After Width: | Height: | Size: 119 KiB |
52
src/components/card/ArticleCard.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed} from "vue";
|
||||
import {getAssetURL} from "@/utils/function.ts";
|
||||
import emitter from "@/utils/mitt.ts";
|
||||
|
||||
const props = defineProps<{
|
||||
post: IPost
|
||||
}>()
|
||||
|
||||
const thumbUrl = computed(() => {
|
||||
// 如果文章缩略图为空,则从本地随机获取默认缩略图
|
||||
props.post.fields.thumb.value = props.post.fields.thumb.value ? props.post.fields.thumb.value : getAssetURL(
|
||||
`${Math.floor(Math.random() * (7 - 1 + 1)) + 1}.jpg`
|
||||
)
|
||||
return props.post.fields.thumb.value
|
||||
})
|
||||
|
||||
const handleOnCardClick = () => {
|
||||
emitter.emit('openPost', props.post)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :title="post.title"
|
||||
class="container relative overflow-hidden rounded-2xl hover:cursor-pointer text-white hover:text-amber-200 h-56"
|
||||
@click="handleOnCardClick">
|
||||
<!-- 图片 -->
|
||||
<div class="container h-full">
|
||||
<img :src="thumbUrl" alt="" class="object-cover h-full w-full object-center transition-all hover:scale-110">
|
||||
</div>
|
||||
<!-- 文字部分 -->
|
||||
<div class="container relative">
|
||||
<div class="absolute h-12 bottom-0 w-full backdrop-blur-3xl bg-black bg-opacity-60 leading-8 p-2 truncate">
|
||||
<span class="text-xl font-bold transition-all px-1">{{ post.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分类 -->
|
||||
<div class="absolute top-0 left-0 backdrop-blur-3xl bg-black bg-opacity-60 rounded-lg m-2 px-2 text-white">
|
||||
<span>{{ post.category }}</span>
|
||||
</div>
|
||||
<!-- 日期 -->
|
||||
<div class="absolute bottom-12 right-0 backdrop-blur-3xl bg-black bg-opacity-60 rounded-lg m-2 px-2 text-white">
|
||||
<font-awesome-icon :icon="['far', 'clock']" class="text-sm mr-1"/>
|
||||
<span>{{ post.date.year }}-{{ post.date.month }}-{{ post.date.day }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
70
src/components/card/ArticleDigestViewer.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, onUnmounted, ref} from "vue";
|
||||
import emitter from "@/utils/mitt.ts";
|
||||
import {getAssetURL} from "@/utils/function.ts";
|
||||
|
||||
const isShow = ref(false)
|
||||
const currentPost = ref<IPost | null>(null)
|
||||
|
||||
const onOpenPost = (e: IPost) => {
|
||||
isShow.value = true
|
||||
currentPost.value = e;
|
||||
}
|
||||
|
||||
const onClosePost = () => {
|
||||
isShow.value = false
|
||||
currentPost.value = null
|
||||
}
|
||||
|
||||
const thumbUrl = computed(() => {
|
||||
if (currentPost.value && currentPost.value.fields.thumb.value) {
|
||||
return currentPost.value.fields.thumb.value
|
||||
}
|
||||
return getAssetURL(`${Math.floor(Math.random() * (7 - 1 + 1)) + 1}.jpg`)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on('openPost', onOpenPost)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off('openPost', onOpenPost)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-show="isShow" class="absolute top-0 left-0 right-0 bottom-0 bg-black bg-opacity-60">
|
||||
|
||||
<div class="container bg-white mx-auto my-10 rounded-3xl overflow-hidden max-h-full">
|
||||
<!-- 标题栏 -->
|
||||
<div class="w-full relative border-b-2 border-b-gray-200">
|
||||
<!-- 文章头图 -->
|
||||
<div class="min-h-52">
|
||||
<img :src="thumbUrl" alt="" class="object-cover object-center w-full max-h-64"/>
|
||||
</div>
|
||||
<!-- 预览标题 -->
|
||||
<div
|
||||
class="h-12 pl-8 py-1.5 pr-16 truncate absolute top-0 left-0 right-0 bg-black bg-opacity-60 backdrop-blur-3xl text-white">
|
||||
<span v-if="currentPost" :title="currentPost.title" class="text-3xl font-bold">
|
||||
文章预览 /
|
||||
</span>
|
||||
</div>
|
||||
<!-- 关闭按钮 -->
|
||||
<div
|
||||
class="absolute top-0 right-2 text-white hover:bg-gray-200 hover:text-black transition-all w-9 h-9 my-1.5 rounded-full flex justify-center items-center cursor-pointer"
|
||||
@click="onClosePost"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'xmark']" class="p-2"/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 正文 -->
|
||||
<div class="container p-8">
|
||||
<div v-if="currentPost" class="container" v-html="currentPost.digest"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
73
src/components/loader/LoaderView.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div class="loader">
|
||||
<svg
|
||||
class="icon"
|
||||
height="200"
|
||||
p-id="1166"
|
||||
t="1676267704832"
|
||||
version="1.1"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="200"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M787.009641 293.979897c21.792821-0.183795 51.922051-0.459487 53.39241-0.722051a12.826256 12.826256 0 0 0 6.642872-3.610256l58.092308-57.737846a12.589949 12.589949 0 0 0 3.662769-6.603488c0.393846-2.376205-0.131282-4.424205-1.496615-5.802666l-67.872821-67.413334c-1.352205-1.378462-3.452718-1.890462-5.77641-1.522871a12.616205 12.616205 0 0 0-6.695385 3.610256l-58.092307 57.737846a12.826256 12.826256 0 0 0-3.675898 6.669128c-0.236308 1.575385 4.030359 35.997538 3.977846 57.803488-54.350769-40.539897-114.346667-68.096-184.595692-78.336V116.36841h25.915077c2.691282 0 10.660103-3.990974 12.524308-5.356307 2.021744-1.378462 8.388923-6.445949 8.388923-8.375795V7.286154c0-1.929846-6.419692-3.807179-8.310154-5.172513-1.929846-1.352205-9.885538-2.113641-12.603077-2.113641H419.052308c-2.743795 0-11.106462 0.774564-13.049436 2.139897-1.890462 1.352205-8.756513 3.21641-8.756513 5.146257v95.310769c0 1.969231 6.892308 7.036718 8.795897 8.402051 1.929846 1.339077 10.266256 5.369436 13.036308 5.369436h24.996103v81.657436C256.761436 227.511795 91.897436 400.108308 91.897436 608.518564 91.897436 837.618872 281.888821 1024 512.380718 1024 742.859487 1024 931.577436 837.632 931.577436 608.518564c-0.026256-125.702564-55.492923-238.316308-144.567795-314.538667z m-67.413333 146.116924L530.825846 627.698872a25.665641 25.665641 0 0 1-35.551179-0.590769 25.337436 25.337436 0 0 1-0.577641-35.341129l188.783589-187.602051a25.521231 25.521231 0 0 1 18.064411-7.443692 25.665641 25.665641 0 0 1 18.051282 7.443692 25.403077 25.403077 0 0 1 0 35.905641z"
|
||||
fill="#ffffff"
|
||||
p-id="1167"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.loading,
|
||||
(v) => {
|
||||
const el: HTMLElement = document.querySelector("body") as HTMLElement;
|
||||
if (v) {
|
||||
el.classList.add("loading");
|
||||
} else {
|
||||
el.classList.remove("loading");
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.loader::before {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: 2;
|
||||
content: "";
|
||||
width: 100vmax;
|
||||
height: 100vmax;
|
||||
position: fixed;
|
||||
border-radius: 66%;
|
||||
background: var(--lime-soap);
|
||||
transition: transform 0.5s cubic-bezier(0, 0, 0.5, 1.25);
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
}
|
||||
|
||||
.loader svg {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
height: 8em;
|
||||
color: white;
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
transition: opacity 0.3s, transform 0.5s cubic-bezier(0.5, 0, 0.5, 1.5),
|
||||
visibility 0.3s;
|
||||
}
|
||||
</style>
|
12
src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import {createApp} from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import router from "@/router";
|
||||
import {library} from '@fortawesome/fontawesome-svg-core'
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||
import {faClock} from '@fortawesome/free-regular-svg-icons'
|
||||
import {faXmark, faBlog} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faClock, faXmark, faBlog)
|
||||
|
||||
createApp(App).use(router).component('font-awesome-icon', FontAwesomeIcon).mount('#app')
|
13
src/router/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {createRouter, createWebHashHistory} from "vue-router";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import('@/views/home/HomeView.vue')
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
export default router;
|
13
src/style.css
Normal file
@ -0,0 +1,13 @@
|
||||
@import 'lxgw-wenkai-lite-webfont/style.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
font-family: "LXGW WenKai Lite", sans-serif;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: "LXGW WenKai Mono Lite", sans-serif;
|
||||
}
|
59
src/types/blog.d.ts
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
declare interface IPostsData {
|
||||
page: number
|
||||
pageSize: number
|
||||
pages: number
|
||||
count: number
|
||||
dataSet: []
|
||||
}
|
||||
|
||||
declare interface IPost {
|
||||
cid: string
|
||||
title: string
|
||||
created: string
|
||||
modified: string
|
||||
slug: string
|
||||
commentsNum: string
|
||||
type: string
|
||||
digest: string
|
||||
password: string
|
||||
categories: ICategory[]
|
||||
category: string
|
||||
directory: string[]
|
||||
date: IDate
|
||||
year: string
|
||||
month: string
|
||||
day: string
|
||||
hidden: boolean
|
||||
pathinfo: string
|
||||
permalink: string
|
||||
url: string
|
||||
isMarkdown: boolean
|
||||
feedUrl: string
|
||||
feedRssUrl: string
|
||||
feedAtomUrl: string
|
||||
fields: any
|
||||
}
|
||||
|
||||
declare interface ICategory {
|
||||
mid: string
|
||||
name: string
|
||||
slug: string
|
||||
type: string
|
||||
description: string
|
||||
count: string
|
||||
order: string
|
||||
parent: string
|
||||
cid: string
|
||||
directory: string[]
|
||||
url: string
|
||||
feedUrl: string
|
||||
feedRssUrl: string
|
||||
feedAtomUrl: string
|
||||
}
|
||||
|
||||
declare interface IDate {
|
||||
timestamp: number
|
||||
year: string
|
||||
month: string
|
||||
day: string
|
||||
}
|
5
src/types/index.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare interface IBlogResponse<T = null> {
|
||||
status: string
|
||||
message: string
|
||||
data: T
|
||||
}
|
5
src/utils/function.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const getAssetURL = (image: string) => {
|
||||
// 参数一: 相对路径
|
||||
// 参数二: 当前路径的URL
|
||||
return new URL(`../assets/${image}`, import.meta.url).href
|
||||
}
|
8
src/utils/mitt.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import mitt from 'mitt'
|
||||
|
||||
type Events = {
|
||||
openPost: IPost
|
||||
}
|
||||
|
||||
const emitter = mitt<Events>()
|
||||
export default emitter
|
23
src/utils/network.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import axios, {AxiosError, AxiosRequestConfig} from 'axios'
|
||||
|
||||
export async function request<T = unknown>(config: AxiosRequestConfig<any>): Promise<T> {
|
||||
const instance = axios.create({
|
||||
baseURL: "/api",
|
||||
timeout: 5000,
|
||||
headers: {},
|
||||
})
|
||||
|
||||
instance.interceptors.request.use(config => {
|
||||
return config
|
||||
}, error => error)
|
||||
|
||||
// bug fixed on csdn https://blog.csdn.net/qq_45325810/article/details/120704910
|
||||
instance.interceptors.response.use(resource => {
|
||||
if (resource.status === 200) return resource
|
||||
return Promise.reject(new Error(resource.data))
|
||||
}, (error: AxiosError) => {
|
||||
return Promise.reject(error.response ? error.response.data : error.code)
|
||||
})
|
||||
|
||||
return instance.request<T>(config).then(res => res.data)
|
||||
}
|
41
src/views/home/HomeView.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<script lang="ts" setup>
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import ArticleCard from "@/components/card/ArticleCard.vue";
|
||||
import {getBlogRecentPost} from "@/api/blog.ts";
|
||||
import ArticleDigestViewer from "@/components/card/ArticleDigestViewer.vue";
|
||||
|
||||
const recentPosts = ref<IPost[] | null>(null);
|
||||
const hasPosts = computed(() => (recentPosts.value ?? []).length > 0);
|
||||
|
||||
onMounted(async () => {
|
||||
const postData = await getBlogRecentPost();
|
||||
recentPosts.value = postData.data.dataSet
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mx-auto">
|
||||
<!-- 标题部分 -->
|
||||
<div class="text-2xl mb-4 text-white font-bold">
|
||||
<div class="bg-pink-500 w-max rounded-2xl overflow-hidden">
|
||||
<div class="inline-block py-2 px-4 bg-pink-700 rounded-2xl">
|
||||
<font-awesome-icon :icon="['fas', 'blog']"/>
|
||||
</div>
|
||||
<span class="px-4">最新博文</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 内容 -->
|
||||
<div :class="{'justify-center': !hasPosts}" class="container flex">
|
||||
<span v-if="!hasPosts" class="text-2xl font-bold">最近没有动态</span>
|
||||
<div v-else class="grid grid-cols-4 gap-4 w-full">
|
||||
<article-card v-for="item in recentPosts" :key="item.cid" :post="item"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article-digest-viewer/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
12
tailwind.config.js
Normal file
@ -0,0 +1,12 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
37
tsconfig.app.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"src/**/**/*.vue"
|
||||
]
|
||||
}
|
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
22
tsconfig.node.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
24
vite.config.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
define: {
|
||||
'process.env': {...process.env}
|
||||
},
|
||||
resolve: {
|
||||
alias: {'@': path.resolve(__dirname, 'src')},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://cantyonion.site',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|