前端页面添加上下Bar,路由简单配置

This commit is contained in:
xiaowang 2025-07-09 12:34:30 +08:00
parent 5845ed6c7b
commit 1d113e78ed
14 changed files with 863 additions and 161 deletions

View File

@ -1,5 +1,12 @@
module.exports = { module.exports = {
presets: [ presets: [
'@vue/cli-plugin-babel/preset' '@vue/cli-plugin-babel/preset'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
] ]
} }

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,12 @@
}, },
"dependencies": { "dependencies": {
"core-js": "^3.8.3", "core-js": "^3.8.3",
"vue": "^2.6.14" "element-ui": "^2.15.14",
"less-loader": "^12.3.0",
"vant": "^2.13.8",
"vue": "^2.6.14",
"vue-router": "^3.6.5",
"vuex": "^3.6.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@babel/core": "^7.12.16",
@ -17,6 +22,8 @@
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0", "@vue/cli-service": "~5.0.0",
"babel-plugin-component": "^1.1.1",
"babel-plugin-import": "^1.13.8",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3", "eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14" "vue-template-compiler": "^2.6.14"

View File

@ -1,28 +1,118 @@
<template> <template>
<div id="app"> <div id="app">
<img alt="Vue logo" src="./assets/logo.png"> <!-- 固定顶部导航栏 -->
<HelloWorld msg="Welcome to Your Vue.js App"/> <NavBar class="nav-bar" />
<!-- 中间内容区包裹路由视图控制动画范围 -->
<div class="content-container">
<!-- 根据路由方向动态切换过渡类 -->
<transition :name="transitionName" mode="out-in">
<router-view></router-view>
</transition>
</div>
<!-- 固定底部TabBar -->
<TabBar class="tab-bar" />
</div> </div>
</template> </template>
<script> <script>
import HelloWorld from './components/HelloWorld.vue' import TabBar from './components/TabBar.vue';
import NavBar from './components/NavBar.vue';
export default { export default {
name: 'App', name: 'App',
components: { components: {
HelloWorld TabBar,
NavBar
} }
} }
</script> </script>
<style> <style lang="less" scoped>
#app { #app {
font-family: Avenir, Helvetica, Arial, sans-serif; font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-align: center; background-color: #F8F8FF;
color: #2c3e50; min-height: 100vh;
margin-top: 60px; overflow: hidden;
min-width: 200px;
} }
</style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 固定顶部导航栏 */
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
height: 45px;
background: #fff;
}
/* 固定底部TabBar */
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
height: 50px;
background: #fff;
}
/* 中间内容区避开上下Bar的位置 */
.content-container {
position: relative;
min-height: 100vh;
padding-top: 50px;
padding-bottom: 60px;
overflow: hidden;
/* 防止内容溢出 */
}
/* 定义向左滑动动画(前进方向) */
.slide-left-enter-active,
.slide-left-leave-active {
transition: transform 0.5s ease;
position: absolute;
width: 100%;
height: 100%;
}
.slide-left-enter {
transform: translateX(100%);
/* 从右侧进入 */
}
.slide-left-leave-to {
transform: translateX(-100%);
/* 向左离开 */
}
/* 定义向右滑动动画(后退方向) */
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.5s ease;
position: absolute;
width: 100%;
height: 100%;
}
.slide-right-enter {
transform: translateX(-100%);
/* 从左侧进入 */
}
.slide-right-leave-to {
transform: translateX(100%);
/* 向右离开 */
}
</style>

View File

@ -1,58 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,28 @@
<template>
<div class="nav-bar">
<van-nav-bar title="标题" left-text="返回" :placeholder="true" :safe-area-inset-top="true" z-index="99">
<template #right>
<van-icon name="search" size="18" />
</template>
</van-nav-bar>
</div>
</template>
<script>
export default {
components: {},
props: {},
data() {
return {
};
},
watch: {},
computed: {},
methods: {
},
created() { },
mounted() { }
};
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,43 @@
<template>
<div class="tab-bar">
<van-tabbar v-model="active" route :placeholder="true">
<van-tabbar-item replace to="/">
<span>主页</span>
<template #icon="props">
<img :src="props.active ? icon.active : icon.inactive" />
</template>
</van-tabbar-item>
<van-tabbar-item replace to="/portfolio" icon="search"><span>作品集</span>
<template #icon="props">
<img :src="props.active ? icon.active : icon.inactive" />
</template></van-tabbar-item>
<van-tabbar-item replace to="/sampleCollection" icon="setting-o"><span>样品集</span>
<template #icon="props">
<img :src="props.active ? icon.active : icon.inactive" />
</template></van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
name: 'TabBar',
components: {},
props: {},
data() {
return {
active: 0,
icon: {
active: 'https://img01.yzcdn.cn/vant/user-active.png',
inactive: 'https://img01.yzcdn.cn/vant/user-inactive.png',
},
};
},
watch: {},
computed: {},
methods: {},
created() { },
mounted() { }
};
</script>
<style lang="less" scoped></style>

View File

@ -1,8 +1,28 @@
import Vue from 'vue' import Vue from "vue";
import App from './App.vue' import Vuex from 'vuex'
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false Vue.config.productionTip = false;
import { Tabbar, TabbarItem } from "vant";
import { NavBar } from "vant";
import { Toast } from 'vant';
import { Popup } from 'vant';
import { Icon } from 'vant';
import { Button } from 'vant';
Vue.use(Button);
Vue.use(Icon);
Vue.use(Popup);
Vue.use(Toast);
Vue.use(NavBar);
Vue.use(Tabbar);
Vue.use(TabbarItem);
Vue.use(Vuex);
new Vue({ new Vue({
render: h => h(App), store,
}).$mount('#app') router,
render: (h) => h(App),
}).$mount("#app");

View File

@ -0,0 +1,36 @@
<template>
<div>
<div class="homepage">
<h1>主页</h1>
</div>
</div>
</template>
<script>
export default {
name: 'HomePage',
components: {
},
props: {},
data() {
return {
};
},
watch: {},
computed: {},
methods: {},
created() {},
mounted() {}
};
</script>
<style lang="less" scoped>
.homepage {
width: 100vw;
height: 100%;
overflow: auto;
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<div class="portfolio-page">
<h1>作品集</h1>
</div>
</template>
<script>
export default {
name: "PortfolioPage",
components: {},
props: {},
data() {
return {
};
},
watch: {},
computed: {
},
methods: {},
created() { },
mounted() { }
};
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,25 @@
<template>
<div class="sample-collection-page">
<h1>Sample Collection Page</h1>
</div>
</template>
<script>
export default {
name: 'SampleCollectionPage',
components: {},
props: {},
data() {
return {
};
},
watch: {},
computed: {
},
methods: {},
created() { },
mounted() { }
};
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,44 @@
import Vue from "vue";
import VueRouter from "vue-router";
import HomePage from "@/pages/HomePage.vue";
import PortfolioPage from "@/pages/PortfolioPage.vue";
import SampleCollectionPage from "@/pages/SampleCollectionPage.vue";
import {store} from "@/store";
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "HomePage",
component: HomePage,
meta: { direction: "back" },
},
{
path: "/portfolio",
name: "PortfolioPage",
component: PortfolioPage,
meta: { direction: "forward" },
},
{
path: "/sampleCollection",
name: "SampleCollectionPage",
component: SampleCollectionPage,
meta: { direction: "forward" },
},
];
const router = new VueRouter({
mode: "history",
routes,
});
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 避免重复记录相同路径(如刷新页面)
if (to.path !== from.path) {
store.dispatch("routeHistory/pushRoute", to);
}
next();
});
export default router;

View File

@ -0,0 +1,52 @@
const state = {
history: [], // 存储路由历史
};
const mutations = {
// 添加路由到历史记录
PUSH_ROUTE(state, route) {
state.history.push(route);
},
// 重置历史记录
RESET_HISTORY(state) {
state.history = [];
},
};
const actions = {
pushRoute({ commit }, route) {
commit("PUSH_ROUTE", route);
},
resetHistory({ commit }) {
commit("RESET_HISTORY");
},
};
const getters = {
// 判断当前是否为后退操作
isBack(state) {
if (state.history.length < 2) return false;
const current = state.history[state.history.length - 1];
const previous = state.history[state.history.length - 2];
// 通过比较路径判断是否为后退
return previous.path === current.path;
},
// 获取历史记录长度
historyLength(state) {
return state.history.length;
},
};
export default {
namespaced: true, // 使用命名空间
state,
mutations,
actions,
getters,
};

View File

@ -1,4 +1,10 @@
const { defineConfig } = require('@vue/cli-service') const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({ module.exports = defineConfig({
transpileDependencies: true transpileDependencies: true,
devServer: {
host: 'localhost',
port: 8080,
historyApiFallback: true,
allowedHosts: "all"
}
}) })