feat:初始化工程
This commit is contained in:
5
.eslintrc.js
Normal file
5
.eslintrc.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'./node_modules/vue3-eslint'
|
||||||
|
]
|
||||||
|
}
|
||||||
24
.gitignore
vendored
Normal file
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
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
||||||
5
README.md
Normal file
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
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>Vite + Vue + TS</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6333
package-lock.json
generated
Normal file
6333
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "food-hub",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^13.1.0",
|
||||||
|
"element-plus": "^2.9.7",
|
||||||
|
"vue": "^3.5.13",
|
||||||
|
"vue3-common": "git+https://gitee.com/Cxx0822/vue3-common",
|
||||||
|
"vue3-emoji-picker": "^1.1.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^18.19.86",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
|
"@vitejs/plugin-vue": "^5.2.3",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-config-standard": "^16.0.3",
|
||||||
|
"eslint-plugin-import": "^2.31.0",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^5.2.0",
|
||||||
|
"eslint-plugin-vue": "^8.7.1",
|
||||||
|
"sass": "^1.86.3",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"vite": "^6.3.5",
|
||||||
|
"vue-tsc": "^2.2.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
3984
pnpm-lock.yaml
generated
Normal file
3984
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
public/vite.svg
Normal file
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 |
10
src/App.vue
Normal file
10
src/App.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use '@/styles/index.module.scss';
|
||||||
|
</style>
|
||||||
BIN
src/assets/font/霞鹜臻楷.ttf
Normal file
BIN
src/assets/font/霞鹜臻楷.ttf
Normal file
Binary file not shown.
BIN
src/assets/logo/logo.png
Normal file
BIN
src/assets/logo/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 KiB |
1
src/assets/vue.svg
Normal file
1
src/assets/vue.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="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 496 B |
45
src/components/MobileSetting.vue
Normal file
45
src/components/MobileSetting.vue
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="setting-container">
|
||||||
|
<div class="setting-item">
|
||||||
|
<span>主题色</span>
|
||||||
|
<el-color-picker v-model="appStore.themeColor"
|
||||||
|
@change="changeThemeColorEvent"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<span>模式</span>
|
||||||
|
<el-button circle @click="appStore.toggleDark" class="theme-button">
|
||||||
|
{{ appStore.isDark ? '🌙' : '☀️' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAppStore } from '@/store/app.ts'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
appStore.themeColor = appStore.getThemeColor()
|
||||||
|
})
|
||||||
|
|
||||||
|
const changeThemeColorEvent = (value: string) => {
|
||||||
|
appStore.setThemeColor(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.setting-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.setting-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
23
src/layout/TabBar.vue
Normal file
23
src/layout/TabBar.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tab-bar">
|
||||||
|
<router-link to="/app/record" @click="routerClick()" class="item">
|
||||||
|
<i class="fa-solid fa-house"></i>
|
||||||
|
<span>记录</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<router-link to="/app/stats" @click="routerClick()" class="item">
|
||||||
|
<i class="fa-solid fa-house"></i>
|
||||||
|
<span>统计</span>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useAppStore } from '@/store/app.ts'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const routerClick = () => {
|
||||||
|
appStore.isShowMobileBack = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
63
src/layout/TitleBar.vue
Normal file
63
src/layout/TitleBar.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="title-bar">
|
||||||
|
<div class="left-container">
|
||||||
|
<el-button v-if="appStore.isShowMobileBack"
|
||||||
|
type="info" :icon="Back" size="small"
|
||||||
|
circle color="#FFFFFF"
|
||||||
|
@click="goBackClick" />
|
||||||
|
<div v-else class="logo-container">
|
||||||
|
<img :src="getAssetsImageFile('logo/logo.png')" alt="暂无图片" class="logo"/>
|
||||||
|
<span>{{ appStore.appName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="title-container">{{ title }}</span>
|
||||||
|
|
||||||
|
<div class="right-container">
|
||||||
|
<svg-icon v-if="appStore.isWebSocket"
|
||||||
|
name="home-websocket" style="color: #FFFFFF;"></svg-icon>
|
||||||
|
<el-button type="info" :icon="Setting" size="small"
|
||||||
|
circle color="#FFFFFF" @click="openSettingClick"/>
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
v-model="appStore.isShowSetting"
|
||||||
|
title="系统设置" :show-close="false"
|
||||||
|
size="50%" class="setting-drawer">
|
||||||
|
<mobile-setting />
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { SvgIcon } from 'vue3-common'
|
||||||
|
import MobileSetting from '@/components/MobileSetting.vue'
|
||||||
|
import { Back } from '@element-plus/icons-vue'
|
||||||
|
import { Setting } from '@element-plus/icons-vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useAppStore } from '@/store/app.ts'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { getAssetsImageFile } from '@/utils'
|
||||||
|
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const title = computed(() => router.currentRoute.value.meta.title)
|
||||||
|
|
||||||
|
const goBackClick = () => {
|
||||||
|
appStore.isShowMobileBack = false
|
||||||
|
history.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
const openSettingClick = () => {
|
||||||
|
appStore.isShowSetting = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.setting-drawer {
|
||||||
|
.el-drawer__header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
18
src/layout/index.vue
Normal file
18
src/layout/index.vue
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<section class="layout-mobile-home">
|
||||||
|
<title-bar />
|
||||||
|
|
||||||
|
<router-view />
|
||||||
|
|
||||||
|
<tab-bar />
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import TitleBar from '@/layout/TitleBar.vue'
|
||||||
|
import TabBar from '@/layout/TabBar.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use "@/styles/mobile.home.layout.module.scss";
|
||||||
|
</style>
|
||||||
28
src/main.ts
Normal file
28
src/main.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
// 引入路由
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
// 引入element-ui组件
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/theme-chalk/src/index.scss'
|
||||||
|
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||||
|
import 'dayjs/locale/zh-cn'
|
||||||
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
|
||||||
|
// 引入font-awesome
|
||||||
|
// import 'font-awesome/css/font-awesome.min.css'
|
||||||
|
import '@fortawesome/fontawesome-free/css/all.css'
|
||||||
|
|
||||||
|
// 创建Vue3实例
|
||||||
|
const app = createApp(App)
|
||||||
|
// 使用路由
|
||||||
|
app.use(router)
|
||||||
|
// 使用Pinia 需要ts 4.5以上
|
||||||
|
app.use(createPinia())
|
||||||
|
// 使用Element UI Plus
|
||||||
|
app.use(ElementPlus, { locale: zhCn })
|
||||||
|
// 挂载到根组件上
|
||||||
|
app.mount('#app')
|
||||||
7
src/router/app.ts
Normal file
7
src/router/app.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Layout from '@/layout/index.vue'
|
||||||
|
import { getRoutersByModules } from 'vue3-common/utils/routerUtil'
|
||||||
|
import { routerMeta } from '@/views/meta.ts'
|
||||||
|
|
||||||
|
const routes = getRoutersByModules(import.meta.glob('@/views/app/**/*.vue'), Layout, routerMeta)
|
||||||
|
|
||||||
|
export default routes
|
||||||
15
src/router/constant.ts
Normal file
15
src/router/constant.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
const constantRoutes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/login'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
component: () => import('@/views/login/index.vue'),
|
||||||
|
meta: { hidden: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default constantRoutes
|
||||||
18
src/router/index.ts
Normal file
18
src/router/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
// 使用 import.meta.glob 自动导入所有 src/router 目录下的 .ts 文件
|
||||||
|
const routeModules = import.meta.glob('./*.ts', { eager: true })
|
||||||
|
|
||||||
|
// 将所有模块的默认导出(即路由配置)合并成一个路由数组
|
||||||
|
const routes: RouteRecordRaw[] = Object.values(routeModules)
|
||||||
|
.map((module: any) => module.default) // 获取每个模块的默认导出
|
||||||
|
.flat() // 扁平化数组,确保所有路由项都在一个数组中
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
scrollBehavior: () => ({ top: 0 }),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
79
src/store/app.ts
Normal file
79
src/store/app.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { getLocalStorage, setLocalStorage } from 'vue3-common/utils/storageUtil'
|
||||||
|
import { useDark, useToggle } from '@vueuse/core'
|
||||||
|
|
||||||
|
export const useAppStore = defineStore('ipp', {
|
||||||
|
state: () => ({
|
||||||
|
appName: '食光集',
|
||||||
|
homeTitle: '',
|
||||||
|
isLoading: false,
|
||||||
|
isLogin: false,
|
||||||
|
isShowMobileBack: false,
|
||||||
|
isShowSetting: false,
|
||||||
|
themeColor: '',
|
||||||
|
isDark: false,
|
||||||
|
isWebSocket: false,
|
||||||
|
serverIp: '14.103.235.151',
|
||||||
|
version: 'v1.0.0.2025042901'
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
initApp() {
|
||||||
|
this.setThemeColor(this.getThemeColor())
|
||||||
|
this.setDartModel()
|
||||||
|
},
|
||||||
|
setDartModel() {
|
||||||
|
const dark = useDark({
|
||||||
|
selector: 'html',
|
||||||
|
attribute: 'class',
|
||||||
|
valueDark: 'dark',
|
||||||
|
valueLight: ''
|
||||||
|
})
|
||||||
|
this.isDark = dark.value
|
||||||
|
},
|
||||||
|
toggleDark(e: MouseEvent) {
|
||||||
|
const toggleDark = useToggle(useDark())
|
||||||
|
|
||||||
|
if (!document.startViewTransition) {
|
||||||
|
toggleDark()
|
||||||
|
this.isDark = !this.isDark
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const transition = document.startViewTransition(() => {
|
||||||
|
toggleDark() // 实际切换主题
|
||||||
|
})
|
||||||
|
|
||||||
|
transition.ready.then(() => {
|
||||||
|
const x = e.clientX
|
||||||
|
const y = e.clientY
|
||||||
|
// 从点击点到窗口最远边缘的距离,这个距离即为圆的半径,用于确定一个圆形裁剪路径 (clip path) 的最大尺寸,以便覆盖整个视窗。
|
||||||
|
// 勾股定理:a² + b² = c²
|
||||||
|
const a = Math.max(x, window.innerWidth - x)
|
||||||
|
const b = Math.max(y, window.innerHeight - y)
|
||||||
|
const radius = Math.sqrt((a ** 2) + (b ** 2))
|
||||||
|
|
||||||
|
const clipPath = [`circle(0 at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)`]
|
||||||
|
// 实现过渡的过程 circle
|
||||||
|
document.documentElement.animate(
|
||||||
|
{
|
||||||
|
clipPath: this.isDark ? clipPath.reverse() : clipPath
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 500,
|
||||||
|
easing: 'ease-in',
|
||||||
|
pseudoElement: this.isDark ? '::view-transition-old(root)' : '::view-transition-new(root)'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.isDark = !this.isDark
|
||||||
|
},
|
||||||
|
getThemeColor(): string {
|
||||||
|
return getLocalStorage('theme-color') as string || '#009FA8'
|
||||||
|
},
|
||||||
|
setThemeColor(value: string) {
|
||||||
|
document.documentElement.style.setProperty('--el-color-primary', value)
|
||||||
|
setLocalStorage('theme-color', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
79
src/style.css
Normal file
79
src/style.css
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
:root {
|
||||||
|
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/styles/home.variable.module.scss
Normal file
15
src/styles/home.variable.module.scss
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
$theme-color: #009FA8;
|
||||||
|
|
||||||
|
$navbar-bg-color: #138BBF;
|
||||||
|
$body-bg-color: #F5F5F5;
|
||||||
|
$footer-bg-color: #F5F5DC;
|
||||||
|
$menu-active-color: #009FA8;
|
||||||
|
|
||||||
|
$navbar-height: 60px;
|
||||||
|
$foot-height: 50px;
|
||||||
|
$common-padding: 10px;
|
||||||
|
|
||||||
|
$query-height: 60px;
|
||||||
|
|
||||||
|
$main-top-padding: 2vh;
|
||||||
|
$main-left-padding: 0.5vw;
|
||||||
85
src/styles/index.module.scss
Normal file
85
src/styles/index.module.scss
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
body {
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
font-family: CustomFont, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus,
|
||||||
|
a:active {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:focus,
|
||||||
|
a:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span, input {
|
||||||
|
font-family: 'CustomFont', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 明亮主题变量
|
||||||
|
:root {
|
||||||
|
// 基础颜色
|
||||||
|
--color-bg: #F5F5F5;
|
||||||
|
--color-card-bg: #FFFFFF;
|
||||||
|
--color-record-card-bg: #F5F5DC;
|
||||||
|
--color-border-bg: #FFFFFF;
|
||||||
|
--color-text: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暗黑主题变量
|
||||||
|
.dark {
|
||||||
|
--color-bg: #000000;
|
||||||
|
--color-card-bg: #000000;
|
||||||
|
--color-record-card-bg: #4C4D4F;
|
||||||
|
--color-border-bg: #4C4D4F;
|
||||||
|
--color-text: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root),
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
.dark::view-transition-old(root) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
html {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'CustomFont';
|
||||||
|
src: url('@/assets/font/霞鹜臻楷.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "//at.alicdn.com/t/c/font_4793264_befv3sf3zdg.css";
|
||||||
154
src/styles/mobile.home.layout.module.scss
Normal file
154
src/styles/mobile.home.layout.module.scss
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
@use "home.variable.module" as home;
|
||||||
|
|
||||||
|
$titleBar-height: 40px;
|
||||||
|
$tabBar-height: 70px;
|
||||||
|
|
||||||
|
.layout-mobile-home {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.common-mobile-view {
|
||||||
|
height: calc(100vh - $tabBar-height - $titleBar-height);
|
||||||
|
overflow: auto;
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.common-query {
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-common-query {
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.query-item {
|
||||||
|
overflow-x: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.common-statistics {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
justify-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-bar {
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
height: $titleBar-height;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.left-container {
|
||||||
|
justify-self: flex-start;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-container {
|
||||||
|
justify-self: flex-end;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
height: $tabBar-height;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
background-color: var(--color-card-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
box-shadow: 0 -1px 2px var(--color-border-bg);
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.router-link-active {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new {
|
||||||
|
position: absolute;
|
||||||
|
right: 2%;
|
||||||
|
bottom: 9%;
|
||||||
|
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.vc-day {
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-day-box-center-bottom {
|
||||||
|
height: 105%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.common-section {
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-item {
|
||||||
|
background-color: var(--color-card-bg);
|
||||||
|
border: 1px solid var(--color-border-bg);
|
||||||
|
border-radius: 10px;
|
||||||
|
// box-shadow: 3px 3px 3px #CFCFCF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-card-item {
|
||||||
|
background-color: var(--color-record-card-bg);
|
||||||
|
border: 1px solid var(--color-record-card-bg);
|
||||||
|
border-radius: 10px;
|
||||||
|
// box-shadow: 3px 3px 3px #CFCFCF;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/utils/index.ts
Normal file
17
src/utils/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 文件服务器地址
|
||||||
|
*/
|
||||||
|
export const fileServiceUrl = '/home-service/file'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器文件路径根地址
|
||||||
|
*/
|
||||||
|
export const serviceFileRootPath = '/home-service/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取assets静态资源
|
||||||
|
* @param url 路径
|
||||||
|
*/
|
||||||
|
export const getAssetsImageFile = (url: string) => {
|
||||||
|
return new URL(`/src/assets/${url}`, import.meta.url).href
|
||||||
|
}
|
||||||
13
src/views/app/record/index.vue
Normal file
13
src/views/app/record/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="common-mobile-view">
|
||||||
|
记录
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
13
src/views/app/stats/index.vue
Normal file
13
src/views/app/stats/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="common-mobile-view">
|
||||||
|
统计
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
13
src/views/login/index.vue
Normal file
13
src/views/login/index.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
登录
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
12
src/views/meta.ts
Normal file
12
src/views/meta.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import type { IRouteMetaConfig } from 'vue3-common/types'
|
||||||
|
|
||||||
|
export const routerMeta: IRouteMetaConfig = {
|
||||||
|
'/record': {
|
||||||
|
path: '/record',
|
||||||
|
redirect: '/record/index'
|
||||||
|
},
|
||||||
|
'/stats': {
|
||||||
|
path: '/stats',
|
||||||
|
redirect: '/stats/index'
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
24
tsconfig.app.json
Normal file
24
tsconfig.app.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
|
||||||
|
// 设置 @ 路径
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
],
|
||||||
|
"vue3-common/*": ["node_modules/vue3-common/dist/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
|
}
|
||||||
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
25
tsconfig.node.json
Normal file
25
tsconfig.node.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"erasableSyntaxOnly": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
base: './',
|
||||||
|
resolve: {
|
||||||
|
// 配置路径别名
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user