diff --git a/f/web-kboss/src/components/FloatingBox/FloatingBox.vue b/f/web-kboss/src/components/FloatingBox/FloatingBox.vue index 40cf304..1cc6dd2 100644 --- a/f/web-kboss/src/components/FloatingBox/FloatingBox.vue +++ b/f/web-kboss/src/components/FloatingBox/FloatingBox.vue @@ -6,7 +6,7 @@
-
+
@@ -789,7 +789,7 @@ export default { top: -28px; width: 40px; height: 40px; - z-index: 9999; + z-index: 99; } position: relative; diff --git a/f/web-kboss/src/main.js b/f/web-kboss/src/main.js index 77e80ea..272443e 100644 --- a/f/web-kboss/src/main.js +++ b/f/web-kboss/src/main.js @@ -171,7 +171,7 @@ Vue.use(HappyScroll) // }); // console.log(element); -// console.clear(); // 清除测试日志 +// console.clear(); // 清除测试日志 // } // // 方法4: 检查Eruda等移动端调试工具 @@ -345,6 +345,77 @@ window.addEventListener('beforeunload', function () { Object.keys(filters).forEach(key => { Vue.filter(key, filters[key]) }) +// 在 main.js 的 router.beforeEach 中添加 +router.beforeEach((to, from, next) => { + // 清空面包屑状态的代码 + // store.commit('tagsView/resetBreadcrumbState'); + + // 新增:检测是否为移动设备 + const userAgent = navigator.userAgent; + const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent); + + // 如果是移动设备且访问的是根路径,重定向到移动端首页 + if (isMobile && to.path === '/') { + next('/h5HomePage'); + return; + } + + // 如果是移动设备且访问的不是移动端页面,重定向到移动端首页 + if (isMobile && !to.meta?.isMobile && to.path !== '/h5HomePage' && !to.path.startsWith('/h5HomePage/')) { + next('/h5HomePage'); + return; + } + + // 如果已登录且有token,但Vuex状态丢失,从sessionStorage恢复 + if (store.getters.token && (!store.getters.user || !store.getters.userType)) { + console.log("检测到状态丢失,从sessionStorage恢复用户状态"); + + const user = sessionStorage.getItem('user'); + const auths = sessionStorage.getItem('auths'); + const userType = sessionStorage.getItem('userType'); + const orgType = sessionStorage.getItem('orgType'); + + if (user) { + store.commit('user/SET_USER', user); + } + if (auths) { + store.commit('user/SET_AUTHS', JSON.parse(auths)); + } + if (userType) { + store.commit('user/SET_USER_TYPE', userType); + } + if (orgType) { + store.commit('user/SET_ORG_TYPE', parseInt(orgType)); + } + + // 重新生成路由 + try { + const accessRoutes = store.dispatch('permission/generateRoutes', { + user: store.getters.user, + auths: store.getters.auths, + userType: store.getters.userType, + orgType: store.getters.orgType + }); + + // 重新添加路由 + router.addRoutes(accessRoutes); + + // 重定向到当前路由以确保路由更新 + next({ ...to, replace: true }); + return; + } catch (error) { + console.error('重新生成路由失败:', error); + } + } + + onOverflow.forEach(element => { + if (to.path == element) { + document.querySelector("body").setAttribute("style", "overflow: auto !important;") + } + }); + + next(); +}); Vue.config.productionTip = false diff --git a/f/web-kboss/src/store/modules/permission.js b/f/web-kboss/src/store/modules/permission.js index 9b9188d..dfa8adf 100644 --- a/f/web-kboss/src/store/modules/permission.js +++ b/f/web-kboss/src/store/modules/permission.js @@ -6,8 +6,83 @@ const userAgent = window.navigator.userAgent; // 判断是否为移动设备 const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent); +// 如果是移动设备,添加移动端首页路由和根路径重定向 +if (isMobile) { + console.log("检测到移动设备,添加移动端路由"); + + // 先添加根路径重定向到移动端首页 + constantRoutes.unshift({ + path: '/', + redirect: '/h5HomePage', + hidden: true + }); + + // 添加移动端首页路由 + constantRoutes.push({ + path: '/h5HomePage', + name: 'H5HomePage', + title: 'H5首页', + component: () => import('@/views/H5/index.vue'), + hidden: true, + redirect: "/h5HomePage/index", + meta: { isMobile: true }, + children: [ + { + path: "index", + title: 'H5首页', + component: () => import('@/views/H5/official/index.vue'), + meta: { + title: "H5首页", + fullPath: "/h5HomePage/index", + isMobile: true + }, + }, + { + path: "cloud", + title: '云', + component: () => import('@/views/H5/cloud/index.vue'), + meta: { + title: "云", + fullPath: "/h5HomePage/cloud", + isMobile: true + }, + }, + { + path: "calculate", + title: '算', + component: () => import('@/views/H5/calculate/index.vue'), + meta: { + title: "算", + fullPath: "/h5HomePage/calculate", + isMobile: true + }, + }, + { + path: "net", + title: '网', + component: () => import('@/views/H5/net/index.vue'), + meta: { + title: "网", + fullPath: "/h5HomePage/net", + isMobile: true + }, + }, + { + path: "use", + title: '用', + component: () => import('@/views/H5/use/index.vue'), + meta: { + title: "用", + fullPath: "/h5HomePage/use", + isMobile: true + }, + }, + ] + }); +} + // 修复:更全面的路由过滤逻辑 -function filterAsyncRoutes(routes, permissions, userRoles = []) { +function filterAsyncRoutes(routes, permissions, userRoles = [], deviceType = 'pc') { const res = []; // 定义需要客户角色才能访问的路由 @@ -35,6 +110,14 @@ function filterAsyncRoutes(routes, permissions, userRoles = []) { return; // 跳过当前路由 } + // 新增:根据设备类型过滤路由 + if (deviceType === 'mobile' && !(route.meta?.isMobile || route.meta?.isMobile === true)) { + return; // 移动设备跳过非移动端路由 + } + if (deviceType === 'pc' && route.meta?.isMobile === true) { + return; // PC设备跳过移动端路由 + } + // 如果当前路由有权限,则加入结果 if (hasPermission) { res.push(tmpRoute); @@ -45,7 +128,7 @@ function filterAsyncRoutes(routes, permissions, userRoles = []) { } // 如果没有直接权限,但有子路由,递归处理子路由 else if (tmpRoute.children) { - const filteredChildren = filterAsyncRoutes(tmpRoute.children, permissions, userRoles); + const filteredChildren = filterAsyncRoutes(tmpRoute.children, permissions, userRoles, deviceType); if (filteredChildren.length > 0) { tmpRoute.children = filteredChildren; res.push(tmpRoute); // 即使父路由本身没有权限,只要有子路由有权限,也要保留父路由 @@ -57,7 +140,7 @@ function filterAsyncRoutes(routes, permissions, userRoles = []) { } // 新增:为普通用户添加订单管理和资源管理路由 -function addUserRoutes(routes, userType, orgType, userRoles = []) { +function addUserRoutes(routes, userType, orgType, userRoles = [], deviceType = 'pc') { console.log("addUserRoutes - userType:", userType, "orgType:", orgType, "userRoles:", userRoles); const userRoutes = []; @@ -67,12 +150,13 @@ function addUserRoutes(routes, userType, orgType, userRoles = []) { const orderManagementRoute = routes.find(route => route.path === "/orderManagement"); const resourceManagementRoute = routes.find(route => route.path === "/resourceManagement"); - if (orderManagementRoute) { + // 新增:根据设备类型过滤 + if (orderManagementRoute && (deviceType === 'pc' || orderManagementRoute.meta?.isMobile === true)) { console.log("添加订单管理路由"); userRoutes.push(JSON.parse(JSON.stringify(orderManagementRoute))); // 深拷贝 } - if (resourceManagementRoute) { + if (resourceManagementRoute && (deviceType === 'pc' || resourceManagementRoute.meta?.isMobile === true)) { console.log("添加资源管理路由"); userRoutes.push(JSON.parse(JSON.stringify(resourceManagementRoute))); // 深拷贝 } @@ -85,10 +169,10 @@ function addUserRoutes(routes, userType, orgType, userRoles = []) { routes.find(route => route.path === "/rechargeManagement"), routes.find(route => route.path === "/invoiceManagement"), routes.find(route => route.path === "/workOrderManagement") - ].filter(route => { // 过滤掉undefined,并且只有客户角色才能看到这些路由 - return route && userRoles.includes('客户'); + return route && userRoles.includes('客户') && + (deviceType === 'pc' || route.meta?.isMobile === true); }); console.log("添加新的客户菜单路由:", newCustomerRoutes.map(r => r.path)); @@ -97,38 +181,11 @@ function addUserRoutes(routes, userType, orgType, userRoles = []) { return userRoutes; } -function filterRoutesMobile(routes) { - return routes.filter(route => { - if (route.children && route.children.length) { - route.children = filterRoutesMobile(route.children); - return route.children.length > 0; - } - if (route.meta?.isMobile || route.meta?.isMobile === true) { - return true; - } else { - return false; - } - }); -} - -function filterRoutesPc(routes) { - return routes.filter(route => { - if (route.children && route.children.length) { - route.children = filterRoutesPc(route.children); - return route.children.length > 0; - } - if (!route.meta?.isMobile || route.meta?.isMobile === false) { - return true; - } else { - return false; - } - }); -} - const state = { routes: [], addRoutes: [], - users: [] + users: [], + isMobile: isMobile // 保存设备类型状态 }; const mutations = { @@ -136,11 +193,15 @@ const mutations = { console.log("MUTATION SET_ROUTES - received routes:", routes); state.addRoutes = routes; sessionStorage.setItem("routes", JSON.stringify(routes)); + // 将移动端首页路由也包含在内 state.routes = constantRoutes.concat(routes); console.log("MUTATION SET_ROUTES - final state.routes:", state.routes); }, SETUSERS: (state, user) => { state.users = user; + }, + SET_DEVICE_TYPE: (state, isMobile) => { + state.isMobile = isMobile; } }; @@ -161,7 +222,7 @@ const actions = { * @param {Object} [params.user] - 用户信息对象 * @returns {Promise} 解析后的动态路由数组 */ - generateRoutes({ commit, rootState }, params) { + generateRoutes({ commit, rootState, state }, params) { console.log("ACTION generateRoutes - params:", params); return new Promise((resolve) => { let accessedRoutes; @@ -176,10 +237,18 @@ const actions = { console.log("用户类型:", userType, "orgType:", orgType); + // 确定设备类型 + const deviceType = state.isMobile ? 'mobile' : 'pc'; + console.log("设备类型:", deviceType); + // 修复:包含 orgType 为 2 和 3 的情况 if (params.user && params.user.includes("admin") && orgType != 2 && orgType != 3) { - // 管理员:只显示超级管理员菜单 - accessedRoutes = asyncRoutes.filter(item => item.path === '/superAdministrator'); + // 管理员:只显示超级管理员菜单(仅PC端) + if (deviceType === 'pc') { + accessedRoutes = asyncRoutes.filter(item => item.path === '/superAdministrator'); + } else { + accessedRoutes = []; + } } else { const auths = params.auths ? JSON.parse(JSON.stringify(params.auths)) : []; console.log("ACTION generateRoutes - auths:", auths); @@ -195,8 +264,8 @@ const actions = { // 如果权限列表包含空路径,认为用户有所有权限 accessedRoutes = asyncRoutes || []; } else { - // 使用修复后的过滤函数,传入用户角色 - accessedRoutes = filterAsyncRoutes(asyncRoutes, auths, userRoles); + // 使用修复后的过滤函数,传入用户角色和设备类型 + accessedRoutes = filterAsyncRoutes(asyncRoutes, auths, userRoles, deviceType); } } else { // 如果没有权限列表,不显示任何动态路由 @@ -205,7 +274,7 @@ const actions = { // 新增:为普通用户添加订单管理和资源管理路由以及新的五个客户菜单 console.log("为用户添加特定路由"); - const userSpecificRoutes = addUserRoutes(asyncRoutes, userType, orgType, userRoles); + const userSpecificRoutes = addUserRoutes(asyncRoutes, userType, orgType, userRoles, deviceType); // 确保不重复添加路由,同时检查角色权限 userSpecificRoutes.forEach(route => { diff --git a/f/web-kboss/src/views/H5/images/bg.png b/f/web-kboss/src/views/H5/images/bg.png new file mode 100644 index 0000000..b083db8 Binary files /dev/null and b/f/web-kboss/src/views/H5/images/bg.png differ diff --git a/f/web-kboss/src/views/H5/index.vue b/f/web-kboss/src/views/H5/index.vue index b9fc999..847b6cb 100644 --- a/f/web-kboss/src/views/H5/index.vue +++ b/f/web-kboss/src/views/H5/index.vue @@ -2,19 +2,29 @@
- + + +
- +
-
-
顶部
+
+ + + +
+
回顶部
@@ -27,14 +37,20 @@ @click="switchTab('index')" >
- 首页 + + 首页 +
- 首页 + + 首页 +
+
@@ -44,14 +60,20 @@ @click="switchTab('cloud')" >
- 云 + + 云 +
- 云 + + +
+
@@ -61,14 +83,20 @@ @click="switchTab('calculate')" >
- 算 + + 算 +
- 算 + + +
+
@@ -78,14 +106,20 @@ @click="switchTab('net')" >
- 网 + + 网 +
- 网 + + +
+
@@ -95,14 +129,20 @@ @click="switchTab('use')" >
- 用 + + 用 +
- 用 + + +
+
@@ -124,6 +164,8 @@ export default { ], showBackToTop: false, // 是否显示返回顶部按钮 scrollThreshold: 200, // 滚动多少距离后显示按钮(可根据需要调整) + transitionName: 'fade', // 页面切换动画名称 + isSwitching: false, // 是否正在切换 }; }, watch: { @@ -142,8 +184,11 @@ export default { * @param {string} tabId - 标签ID */ switchTab(tabId) { - // 如果点击的是当前已激活的标签,不进行操作 - if (this.activeTab === tabId) return; + // 如果正在切换或点击的是当前已激活的标签,不进行操作 + if (this.isSwitching || this.activeTab === tabId) return; + + // 设置正在切换状态 + this.isSwitching = true; // 更新激活状态 this.activeTab = tabId; @@ -151,7 +196,39 @@ export default { // 根据tabId跳转到对应的路由 const tab = this.tabList.find(item => item.id === tabId); if (tab) { - this.$router.push(tab.path); + // 添加点击反馈动画 + this.animateTabClick(tabId); + + // 延迟路由跳转,让动画先执行 + setTimeout(() => { + this.$router.push(tab.path); + // 重置切换状态 + setTimeout(() => { + this.isSwitching = false; + }, 300); + }, 150); + } else { + this.isSwitching = false; + } + }, + + /** + * 执行Tab点击动画 + * @param {string} tabId - 标签ID + */ + animateTabClick(tabId) { + // 获取点击的tab元素 + const tabIndex = this.tabList.findIndex(item => item.id === tabId); + const tabs = document.querySelectorAll('.tabBar-item'); + + if (tabs[tabIndex]) { + // 添加点击动画类 + tabs[tabIndex].classList.add('click-animation'); + + // 动画结束后移除类 + setTimeout(() => { + tabs[tabIndex].classList.remove('click-animation'); + }, 300); } }, @@ -186,6 +263,15 @@ export default { */ scrollToTop() { if (this.$refs.mainContent) { + // 添加点击反馈效果 + const btn = document.querySelector('.back-to-top'); + if (btn) { + btn.classList.add('clicked'); + setTimeout(() => { + btn.classList.remove('clicked'); + }, 300); + } + this.$refs.mainContent.scrollTo({ top: 0, behavior: 'smooth' // 平滑滚动 @@ -208,120 +294,5 @@ window.onresize = adapter() diff --git a/f/web-kboss/src/views/H5/less/dialog/index.css b/f/web-kboss/src/views/H5/less/dialog/index.css index 6ae7f57..95e73fe 100644 --- a/f/web-kboss/src/views/H5/less/dialog/index.css +++ b/f/web-kboss/src/views/H5/less/dialog/index.css @@ -95,7 +95,7 @@ width: var(--dialog-width, 6rem) !important; max-width: 90%; border-radius: 0.08rem; - z-index: 99999; + z-index: 9999; } ::v-deep .product-consult-dialog.el-dialog .el-dialog__header { padding: 0.2rem 0.3rem 0.1rem; diff --git a/f/web-kboss/src/views/H5/less/dialog/index.less b/f/web-kboss/src/views/H5/less/dialog/index.less index 0cabb2b..499d4cc 100644 --- a/f/web-kboss/src/views/H5/less/dialog/index.less +++ b/f/web-kboss/src/views/H5/less/dialog/index.less @@ -108,7 +108,7 @@ width: var(--dialog-width, 6rem) !important; max-width: 90%; border-radius: .08rem; - z-index: 99999; + z-index: 9999; .el-dialog__header { padding: .2rem .3rem .1rem; diff --git a/f/web-kboss/src/views/H5/less/home/index.css b/f/web-kboss/src/views/H5/less/home/index.css new file mode 100644 index 0000000..1f1eefd --- /dev/null +++ b/f/web-kboss/src/views/H5/less/home/index.css @@ -0,0 +1,365 @@ +.h5-container { + display: flex; + flex-direction: column; + height: 100vh; + overflow: hidden; + position: relative; +} +.main { + flex: 1; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + /* 为iOS添加平滑滚动 */ +} +/* 页面切换动画 */ +.fade-enter-active, +.fade-leave-active { + transition: all 0.3s ease; +} +.fade-enter-from { + opacity: 0; + transform: translateY(0.2rem); +} +.fade-leave-to { + opacity: 0; + transform: translateY(-0.2rem); +} +/* 返回顶部按钮样式 */ +.back-to-top { + position: fixed; + right: 0.4rem; + bottom: 1.5rem; + /* 在底部导航栏上方 */ + z-index: 999; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 1.1rem; + height: 1.1rem; + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + border-radius: 50%; + box-shadow: 0 0.08rem 0.24rem rgba(102, 126, 234, 0.4); + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + user-select: none; + -webkit-tap-highlight-color: transparent; + /* 悬停效果 */ + /* 激活效果 */ + /* 点击动画 */ +} +.back-to-top:hover { + transform: translateY(-0.1rem); + box-shadow: 0 0.12rem 0.36rem rgba(102, 126, 234, 0.6); + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); +} +.back-to-top:active { + transform: scale(0.95); +} +.back-to-top.clicked { + animation: pulse 0.3s ease; +} +.back-to-top .back-to-top-icon { + width: 0.45rem; + height: 0.45rem; + color: white; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.04rem; +} +.back-to-top .back-to-top-icon svg { + width: 100%; + height: 100%; + stroke: currentColor; + transition: transform 0.3s ease; +} +.back-to-top .back-to-top-text { + color: white; + font-size: 0.24rem; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02rem; + text-shadow: 0 0.02rem 0.04rem rgba(0, 0, 0, 0.2); +} +/* 脉冲动画 */ +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(0.9); + } + 100% { + transform: scale(1); + } +} +/* 返回顶部按钮动画 */ +.fade-scale-enter-active, +.fade-scale-leave-active { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} +.fade-scale-enter-from, +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8) translateY(0.2rem); +} +/* TabBar 样式 */ +.tabBar { + padding: 0.2rem 0; + background-color: rgba(255, 255, 255, 0.95); + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + box-shadow: 0 -0.02rem 0.2rem rgba(0, 0, 0, 0.08); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-top: 1px solid rgba(0, 0, 0, 0.05); +} +.tabBar .tabBar-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + padding: 0.1rem 0.15rem; + border-radius: 0.3rem; + position: relative; + overflow: hidden; + user-select: none; + /* 悬停效果 */ + /* 激活状态 */ + /* 点击动画 */ + /* 激活指示器 */ +} +.tabBar .tabBar-item:hover { + background-color: rgba(24, 144, 255, 0.08); + transform: translateY(-0.05rem); +} +.tabBar .tabBar-item.active .item-text { + color: #1890ff; + font-weight: 600; +} +.tabBar .tabBar-item.click-animation { + animation: tabClick 0.3s ease; +} +.tabBar .tabBar-item .active-indicator { + position: absolute; + bottom: 0.05rem; + left: 50%; + transform: translateX(-50%); + width: 0.3rem; + height: 0.04rem; + border-radius: 0.02rem; + animation: indicatorAppear 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} +.tabBar .item-img { + margin-bottom: 0.08rem; + position: relative; + width: 0.5rem; + height: 0.5rem; + display: flex; + align-items: center; + justify-content: center; +} +.tabBar .item-img img { + width: 100%; + height: 100%; + display: block; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} +.tabBar .item-text { + font-size: 0.24rem; + color: #666; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + height: 0.3rem; + display: flex; + align-items: center; + justify-content: center; +} +/* 图标动画 */ +.icon-bounce-enter-active { + animation: iconBounceIn 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} +.icon-bounce-leave-active { + animation: iconBounceOut 0.3s ease; +} +@keyframes iconBounceIn { + 0% { + opacity: 0; + transform: scale(0.3) rotate(-30deg); + } + 50% { + transform: scale(1.2) rotate(10deg); + } + 70% { + transform: scale(0.9) rotate(-5deg); + } + 100% { + opacity: 1; + transform: scale(1) rotate(0deg); + } +} +@keyframes iconBounceOut { + 0% { + opacity: 1; + transform: scale(1) rotate(0deg); + } + 100% { + opacity: 0; + transform: scale(0.5) rotate(30deg); + } +} +/* 文字动画 */ +.text-slide-enter-active { + animation: textSlideIn 0.3s ease-out; +} +.text-slide-leave-active { + animation: textSlideOut 0.2s ease-in; +} +@keyframes textSlideIn { + 0% { + opacity: 0; + transform: translateY(0.1rem); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} +@keyframes textSlideOut { + 0% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(-0.1rem); + } +} +/* Tab点击动画 */ +@keyframes tabClick { + 0% { + transform: scale(1); + } + 50% { + transform: scale(0.95); + } + 100% { + transform: scale(1); + } +} +/* 指示器出现动画 */ +@keyframes indicatorAppear { + 0% { + opacity: 0; + transform: translateX(-50%) scaleX(0); + } + 100% { + opacity: 1; + transform: translateX(-50%) scaleX(1); + } +} +/* 响应式调整 */ +@media (max-width: 480px) { + .back-to-top { + right: 0.3rem; + bottom: 1.6rem; + width: 1rem; + height: 1rem; + } + .back-to-top .back-to-top-icon { + width: 0.4rem; + height: 0.4rem; + margin-bottom: 0.03rem; + } + .back-to-top .back-to-top-text { + font-size: 0.22rem; + } + .tabBar { + padding: 0.15rem 0; + } + .tabBar .tabBar-item { + padding: 0.08rem 0.12rem; + } + .tabBar .item-img { + width: 0.45rem; + height: 0.45rem; + } + .tabBar .item-text { + font-size: 0.22rem; + } +} +@media (min-width: 768px) { + .back-to-top { + right: 0.5rem; + bottom: 2rem; + width: 1.2rem; + height: 1.2rem; + } + .back-to-top .back-to-top-icon { + width: 0.5rem; + height: 0.5rem; + margin-bottom: 0.05rem; + } + .back-to-top .back-to-top-text { + font-size: 0.26rem; + } +} +/* 深色模式支持 */ +@media (prefers-color-scheme: dark) { + .back-to-top { + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + box-shadow: 0 0.08rem 0.24rem rgba(138, 43, 226, 0.4); + } + .back-to-top:hover { + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + box-shadow: 0 0.12rem 0.36rem rgba(138, 43, 226, 0.6); + } + .tabBar { + background-color: rgba(30, 30, 30, 0.95); + border-top: 1px solid rgba(255, 255, 255, 0.05); + } + .tabBar .tabBar-item .item-text { + color: #b0b0b0; + } + .tabBar .tabBar-item:hover { + background-color: rgba(100, 181, 246, 0.08); + } + .tabBar .tabBar-item.active .item-text { + color: #64b5f6; + } + .tabBar .tabBar-item.active .active-indicator { + background: linear-gradient(90deg, #64b5f6, #81c784); + } +} +/* 安全区域适配(iPhone X及以上机型) */ +@supports (padding: max(0px)) { + .back-to-top { + bottom: calc(1.5rem + env(safe-area-inset-bottom, 0)); + } + .tabBar { + padding-bottom: calc(0.2rem + env(safe-area-inset-bottom, 0)); + } + @media (max-width: 480px) { + .back-to-top { + bottom: calc(1.6rem + env(safe-area-inset-bottom, 0)); + } + .tabBar { + padding-bottom: calc(0.15rem + env(safe-area-inset-bottom, 0)); + } + } + @media (min-width: 768px) { + .back-to-top { + bottom: calc(2rem + env(safe-area-inset-bottom, 0)); + } + } +} diff --git a/f/web-kboss/src/views/H5/less/home/index.less b/f/web-kboss/src/views/H5/less/home/index.less new file mode 100644 index 0000000..d127c06 --- /dev/null +++ b/f/web-kboss/src/views/H5/less/home/index.less @@ -0,0 +1,438 @@ +.h5-container { + display: flex; + flex-direction: column; + height: 100vh; + overflow: hidden; + position: relative; +} + +.main { + flex: 1; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + /* 为iOS添加平滑滚动 */ +} + +/* 页面切换动画 */ +.fade-enter-active, +.fade-leave-active { + transition: all 0.3s ease; +} + +.fade-enter-from { + opacity: 0; + transform: translateY(0.2rem); +} + +.fade-leave-to { + opacity: 0; + transform: translateY(-0.2rem); +} + +/* 返回顶部按钮样式 */ +.back-to-top { + position: fixed; + right: 0.4rem; + bottom: 1.5rem; + /* 在底部导航栏上方 */ + z-index: 999; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 1.1rem; + height: 1.1rem; + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + border-radius: 50%; + box-shadow: 0 0.08rem 0.24rem rgba(102, 126, 234, 0.4); + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + user-select: none; + -webkit-tap-highlight-color: transparent; + + /* 悬停效果 */ + &:hover { + transform: translateY(-0.1rem); + box-shadow: 0 0.12rem 0.36rem rgba(102, 126, 234, 0.6); + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + } + + /* 激活效果 */ + &:active { + transform: scale(0.95); + } + + /* 点击动画 */ + &.clicked { + animation: pulse 0.3s ease; + } + + .back-to-top-icon { + width: 0.45rem; + height: 0.45rem; + color: white; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.04rem; + + svg { + width: 100%; + height: 100%; + stroke: currentColor; + transition: transform 0.3s ease; + } + } + + .back-to-top-text { + color: white; + font-size: 0.24rem; + font-weight: 500; + line-height: 1; + letter-spacing: 0.02rem; + text-shadow: 0 0.02rem 0.04rem rgba(0, 0, 0, 0.2); + } +} + +/* 脉冲动画 */ +@keyframes pulse { + 0% { + transform: scale(1); + } + + 50% { + transform: scale(0.9); + } + + 100% { + transform: scale(1); + } +} + +/* 返回顶部按钮动画 */ +.fade-scale-enter-active, +.fade-scale-leave-active { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.fade-scale-enter-from, +.fade-scale-leave-to { + opacity: 0; + transform: scale(0.8) translateY(0.2rem); +} + +/* TabBar 样式 */ +.tabBar { + padding: 0.2rem 0; + background-color: rgba(255, 255, 255, 0.95); + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + box-shadow: 0 -0.02rem 0.2rem rgba(0, 0, 0, 0.08); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-top: 1px solid rgba(0, 0, 0, 0.05); + + .tabBar-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + padding: 0.1rem 0.15rem; + border-radius: 0.3rem; + position: relative; + overflow: hidden; + user-select: none; + + /* 悬停效果 */ + &:hover { + background-color: rgba(24, 144, 255, 0.08); + transform: translateY(-0.05rem); + } + + /* 激活状态 */ + &.active { + .item-text { + color: #1890ff; + font-weight: 600; + } + } + + /* 点击动画 */ + &.click-animation { + animation: tabClick 0.3s ease; + } + + /* 激活指示器 */ + .active-indicator { + position: absolute; + bottom: 0.05rem; + left: 50%; + transform: translateX(-50%); + width: 0.3rem; + height: 0.04rem; + // background: linear-gradient(90deg, #1890ff); + border-radius: 0.02rem; + animation: indicatorAppear 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); + } + } + + .item-img { + margin-bottom: 0.08rem; + position: relative; + width: 0.5rem; + height: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + + img { + width: 100%; + height: 100%; + display: block; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + } + + .item-text { + font-size: 0.24rem; + color: #666; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + height: 0.3rem; + display: flex; + align-items: center; + justify-content: center; + } +} + +/* 图标动画 */ +.icon-bounce-enter-active { + animation: iconBounceIn 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +.icon-bounce-leave-active { + animation: iconBounceOut 0.3s ease; +} + +@keyframes iconBounceIn { + 0% { + opacity: 0; + transform: scale(0.3) rotate(-30deg); + } + + 50% { + transform: scale(1.2) rotate(10deg); + } + + 70% { + transform: scale(0.9) rotate(-5deg); + } + + 100% { + opacity: 1; + transform: scale(1) rotate(0deg); + } +} + +@keyframes iconBounceOut { + 0% { + opacity: 1; + transform: scale(1) rotate(0deg); + } + + 100% { + opacity: 0; + transform: scale(0.5) rotate(30deg); + } +} + +/* 文字动画 */ +.text-slide-enter-active { + animation: textSlideIn 0.3s ease-out; +} + +.text-slide-leave-active { + animation: textSlideOut 0.2s ease-in; +} + +@keyframes textSlideIn { + 0% { + opacity: 0; + transform: translateY(0.1rem); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes textSlideOut { + 0% { + opacity: 1; + transform: translateY(0); + } + + 100% { + opacity: 0; + transform: translateY(-0.1rem); + } +} + +/* Tab点击动画 */ +@keyframes tabClick { + 0% { + transform: scale(1); + } + + 50% { + transform: scale(0.95); + } + + 100% { + transform: scale(1); + } +} + +/* 指示器出现动画 */ +@keyframes indicatorAppear { + 0% { + opacity: 0; + transform: translateX(-50%) scaleX(0); + } + + 100% { + opacity: 1; + transform: translateX(-50%) scaleX(1); + } +} + +/* 响应式调整 */ +@media (max-width: 480px) { + .back-to-top { + right: 0.3rem; + bottom: 1.6rem; + width: 1rem; + height: 1rem; + + .back-to-top-icon { + width: 0.4rem; + height: 0.4rem; + margin-bottom: 0.03rem; + } + + .back-to-top-text { + font-size: 0.22rem; + } + } + + .tabBar { + padding: 0.15rem 0; + + .tabBar-item { + padding: 0.08rem 0.12rem; + } + + .item-img { + width: 0.45rem; + height: 0.45rem; + } + + .item-text { + font-size: 0.22rem; + } + } +} + +@media (min-width: 768px) { + .back-to-top { + right: 0.5rem; + bottom: 2rem; + width: 1.2rem; + height: 1.2rem; + + .back-to-top-icon { + width: 0.5rem; + height: 0.5rem; + margin-bottom: 0.05rem; + } + + .back-to-top-text { + font-size: 0.26rem; + } + } +} + +/* 深色模式支持 */ +@media (prefers-color-scheme: dark) { + .back-to-top { + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + box-shadow: 0 0.08rem 0.24rem rgba(138, 43, 226, 0.4); + + &:hover { + background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%); + box-shadow: 0 0.12rem 0.36rem rgba(138, 43, 226, 0.6); + } + } + + .tabBar { + background-color: rgba(30, 30, 30, 0.95); + border-top: 1px solid rgba(255, 255, 255, 0.05); + + .tabBar-item { + .item-text { + color: #b0b0b0; + } + + &:hover { + background-color: rgba(100, 181, 246, 0.08); + } + + &.active { + .item-text { + color: #64b5f6; + } + + .active-indicator { + background: linear-gradient(90deg, #64b5f6, #81c784); + } + } + } + } +} + +/* 安全区域适配(iPhone X及以上机型) */ +@supports (padding: max(0px)) { + .back-to-top { + bottom: calc(1.5rem + env(safe-area-inset-bottom, 0)); + } + + .tabBar { + padding-bottom: calc(0.2rem + env(safe-area-inset-bottom, 0)); + } + + @media (max-width: 480px) { + .back-to-top { + bottom: calc(1.6rem + env(safe-area-inset-bottom, 0)); + } + + .tabBar { + padding-bottom: calc(0.15rem + env(safe-area-inset-bottom, 0)); + } + } + + @media (min-width: 768px) { + .back-to-top { + bottom: calc(2rem + env(safe-area-inset-bottom, 0)); + } + } +} diff --git a/f/web-kboss/src/views/H5/less/official/index.css b/f/web-kboss/src/views/H5/less/official/index.css index 8771e43..2915393 100644 --- a/f/web-kboss/src/views/H5/less/official/index.css +++ b/f/web-kboss/src/views/H5/less/official/index.css @@ -72,13 +72,23 @@ .journey-box .content .item-box, .latitude-box .content .item-box { width: 48%; - background-color: #fff; border-radius: 0.1rem; padding: 0.2rem; - box-shadow: 0 0.02rem 0.08rem rgba(0, 0, 0, 0.05); + box-shadow: 0 0.02rem 0.08rem rgba(39, 90, 255, 0.08); margin-bottom: 0.2rem; display: flex; flex-direction: column; + background: linear-gradient(135deg, #f0f7ff 0%, #ffffff 100%); + border: 1px solid rgba(39, 90, 255, 0.1); + transition: all 0.3s ease; +} +.base-box .content .item-box:hover, +.journey-box .content .item-box:hover, +.latitude-box .content .item-box:hover { + box-shadow: 0 0.04rem 0.16rem rgba(39, 90, 255, 0.12); + transform: translateY(-2px); + border-color: rgba(39, 90, 255, 0.2); + background: linear-gradient(135deg, #e8f2ff 0%, #ffffff 100%); } .base-box .content .item-box .item-title, .journey-box .content .item-box .item-title, diff --git a/f/web-kboss/src/views/H5/less/official/index.less b/f/web-kboss/src/views/H5/less/official/index.less index 35f68c2..010f8df 100644 --- a/f/web-kboss/src/views/H5/less/official/index.less +++ b/f/web-kboss/src/views/H5/less/official/index.less @@ -78,13 +78,22 @@ .item-box { width: 48%; - background-color: #fff; border-radius: 0.1rem; padding: 0.2rem; - box-shadow: 0 .02rem .08rem rgba(0, 0, 0, 0.05); + box-shadow: 0 .02rem .08rem rgba(39, 90, 255, 0.08); margin-bottom: 0.2rem; display: flex; flex-direction: column; + background: linear-gradient(135deg, #f0f7ff 0%, #ffffff 100%); + border: 1px solid rgba(39, 90, 255, 0.1); + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 .04rem .16rem rgba(39, 90, 255, 0.12); + transform: translateY(-2px); + border-color: rgba(39, 90, 255, 0.2); + background: linear-gradient(135deg, #e8f2ff 0%, #ffffff 100%); + } .item-title { font-size: 0.26rem; @@ -107,8 +116,6 @@ display: block; font-size: 0.156rem; color: #666; - // margin-bottom: 0.08rem; - // line-height: 1.4; .advantage-icon { width: 0.18rem; diff --git a/f/web-kboss/src/views/H5/official/index.vue b/f/web-kboss/src/views/H5/official/index.vue index e8d6f10..8b6b9c4 100644 --- a/f/web-kboss/src/views/H5/official/index.vue +++ b/f/web-kboss/src/views/H5/official/index.vue @@ -21,6 +21,7 @@
+
{{ item.title }}
{{ item.description }}
diff --git a/f/web-kboss/src/views/operation/consultingMangement/index.vue b/f/web-kboss/src/views/operation/consultingMangement/index.vue index 647174e..7a76f82 100644 --- a/f/web-kboss/src/views/operation/consultingMangement/index.vue +++ b/f/web-kboss/src/views/operation/consultingMangement/index.vue @@ -13,13 +13,11 @@ - + - - - +