Commit 5a0bff354f076d4945c9e2eb0c6435faf40b3f49

Authored by 杨泽宇
1 parent 0355c7d2

Basic Layout

README.md
@@ -37,14 +37,32 @@ cnlive-community-web-vue 银地智慧社区管理后台 @@ -37,14 +37,32 @@ cnlive-community-web-vue 银地智慧社区管理后台
37 * api 交互接口 37 * api 交互接口
38 * assets 静态资源 38 * assets 静态资源
39 * conponents 自定义组件 39 * conponents 自定义组件
  40 + * Pagination 分页组件
  41 + * layout 公共样式布局
  42 + * enter.vue 汇总入口
  43 + * header.vue 头
  44 + * menu.vue 左侧菜单
40 * router 路由管理 45 * router 路由管理
41 - * pinia 状态管理  
42 - * store 状态管理 46 + * pinia 状态管理 新包 使用中
  47 + * store 状态管理 旧包 备用中
43 * style 样式 48 * style 样式
  49 + * element.scss 覆盖默认element样式
  50 + * global.module.scss scss中变量用在模板中
  51 + * menu.scss 菜单相关样式
  52 + * status.scss 状态变量
  53 + * reset.scss html原生标签样式统一重写
  54 + * variable.scss 主题色相关变量
44 * utils 全局js 55 * utils 全局js
  56 + * progress.ts 页面切换进度条
  57 + * varible.ts 全局静态变量
45 * views 业务逻辑页面 58 * views 业务逻辑页面
46 59
47 ## 功能 60 ## 功能
  61 + * 机构管理
  62 + * 活动管理
  63 + * 缴费管理
  64 + * 社区管理
  65 + * 小区管理等
48 66
49 ## 常见问题 67 ## 常见问题
50 * **登录时localhost cookie失效导致登录失败的问题:** 68 * **登录时localhost cookie失效导致登录失败的问题:**
src/App.vue
@@ -2,25 +2,25 @@ @@ -2,25 +2,25 @@
2 // This starter template is using Vue 3 <script setup> SFCs 2 // This starter template is using Vue 3 <script setup> SFCs
3 // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup 3 // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
4 // import HelloWorld from './components/HelloWorld.vue' 4 // import HelloWorld from './components/HelloWorld.vue'
5 -import Tengxi from '@/components/tengxi.vue'  
6 -import {home_list, getUserInfo} from '@/api/tengxi/index'  
7 -console.log(home_list)  
8 -console.log(getUserInfo)  
9 -home_list({}).then(res => {  
10 - console.log(res)  
11 -}).catch(error => {  
12 - console.log(error)  
13 -})  
14 -getUserInfo(1).then(res => {  
15 - console.log(res)  
16 -}).catch(error => {  
17 - console.log(error)  
18 -}) 5 +// import Tengxi from '@/components/tengxi.vue'
  6 +// import {home_list, getUserInfo} from '@/api/tengxi/index'
  7 +// console.log(home_list)
  8 +// console.log(getUserInfo)
  9 +// home_list({}).then(res => {
  10 +// console.log(res)
  11 +// }).catch(error => {
  12 +// console.log(error)
  13 +// })
  14 +// getUserInfo(1).then(res => {
  15 +// console.log(res)
  16 +// }).catch(error => {
  17 +// console.log(error)
  18 +// })
19 </script> 19 </script>
20 20
21 <template> 21 <template>
22 - <img alt="Vue logo" src="./assets/logo.png" />  
23 - <Tengxi /> 22 + <!-- <img alt="Vue logo" src="./assets/logo.png" /> -->
  23 + <!-- <Tengxi /> -->
24 <router-view></router-view> 24 <router-view></router-view>
25 <!-- <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> --> 25 <!-- <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> -->
26 </template> 26 </template>
@@ -32,6 +32,6 @@ getUserInfo(1).then(res =&gt; { @@ -32,6 +32,6 @@ getUserInfo(1).then(res =&gt; {
32 -moz-osx-font-smoothing: grayscale; 32 -moz-osx-font-smoothing: grayscale;
33 text-align: center; 33 text-align: center;
34 color: #2c3e50; 34 color: #2c3e50;
35 - margin-top: 60px; 35 + /* margin-top: 60px; */
36 } 36 }
37 </style> 37 </style>
src/components/Pagination/index.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { defineEmits } from "vue"; 2 import { defineEmits } from "vue";
3 3
4 -// 每个属性都必传,不带默认值 4 +// TIP 每个属性都必传,不带默认值
5 // defineProps<{ 5 // defineProps<{
6 // total: number; 6 // total: number;
7 // currentPage: number; 7 // currentPage: number;
@@ -9,7 +9,7 @@ import { defineEmits } from &quot;vue&quot;; @@ -9,7 +9,7 @@ import { defineEmits } from &quot;vue&quot;;
9 // pageSize: number; 9 // pageSize: number;
10 // }>(); 10 // }>();
11 11
12 -// 可以带默认值 12 +// TIP 可以带默认值
13 interface Props { 13 interface Props {
14 total: number; 14 total: number;
15 pageSizes?: number[]; 15 pageSizes?: number[];
src/layout/enter.vue 0 → 100644
  1 +<script setup lang="ts">
  2 +import { ref, computed } from "vue";
  3 +import Header from "./header.vue";
  4 +import Menu from "./menu.vue";
  5 +import * as store from "@/pinia/index";
  6 +let key = ref("/")
  7 +</script>
  8 +
  9 +<template>
  10 + <!-- TIP 页面头部,含logo、功能按钮(用户信息、退出登录、帮助) -->
  11 + <Header />
  12 + <!-- TIP 页面头部,含logo、功能按钮(用户信息、退出登录、帮助)结束 -->
  13 + <div class="main-container">
  14 + <!-- TIP 左侧菜单 -->
  15 + <div class="sidebar-container">
  16 + <Menu />
  17 + </div>
  18 + <!-- TIP 左侧菜单结束 -->
  19 + <!-- TIP 右侧具体页面内容 -->
  20 + <div class="page-container" :class="store.family.appStore().isCollapse? 'thinMenu': 'fatMenu'">
  21 + <router-view v-slot="{ Component, route }">
  22 + <transition :name="route.meta.transitionName">
  23 + <keep-alive v-if="route.meta.keepAlive">
  24 + <component :is="Component" />
  25 + </keep-alive>
  26 + <component :is="Component" v-else />
  27 + </transition>
  28 + </router-view>
  29 + </div>
  30 + <!-- TIP 右侧具体页面内容结束 -->
  31 + </div>
  32 +</template>
  33 +
  34 +<style scoped lang="scss">
  35 + .fatMenu {
  36 + margin-left: $sideBarWidth!important;
  37 + }
  38 + .thinMenu {
  39 + margin-left: $sideBarWidthThin!important;
  40 + }
  41 +</style>
src/layout/header.vue 0 → 100644
  1 +<script setup lang="ts">
  2 +import { getCurrentInstance, computed } from "vue";
  3 +import * as store from "@/pinia/index";
  4 +import { Fold, Expand } from "@element-plus/icons-vue";
  5 +import styles from '@/style/global.module.scss';
  6 +// TIP 获取全局变量,暂时只有坏图片占位图
  7 +const {
  8 + appContext: {
  9 + config: { globalProperties },
  10 + },
  11 +} = getCurrentInstance();
  12 +// TIP 侧边菜单collapse状态切换
  13 +const toggleMenu = () => {
  14 + store.family.appStore().toggleCollapse();
  15 +};
  16 +</script>
  17 +
  18 +<template>
  19 + <div class="header-container clearfix">
  20 + <!-- TIP logo -->
  21 + <div class="left-logo">
  22 + <el-image
  23 + :src="globalProperties.$defaultImage"
  24 + fit="contain"
  25 + style="height: 40px"
  26 + ></el-image>
  27 + </div>
  28 + <!-- TIP logo结束 -->
  29 + <!-- TIP 控制菜单collapse的按钮 -->
  30 + <div class="menu-collapse-button" @click="toggleMenu">
  31 + <Expand class="font-style" v-if="store.family.appStore().isCollapse" />
  32 + <Fold class="font-style" v-if="!store.family.appStore().isCollapse" />
  33 + </div>
  34 + <!-- TIP 控制菜单collapse的按钮结束 -->
  35 + <!-- TIP 功能键 -->
  36 + <div class="right-buttons">
  37 + <el-button type="text">帮助</el-button>
  38 + <el-button type="text">用户:管理员</el-button>
  39 + <el-button type="text">退出</el-button>
  40 + </div>
  41 + <!-- TIP 功能键结束 -->
  42 + </div>
  43 +</template>
  44 +
  45 +<style scoped lang="scss">
  46 +.header-container {
  47 + padding: 0 20px;
  48 + height: 60px;
  49 + line-height: 60px;
  50 + background-color: white;
  51 + box-shadow: 0 5px 10px -5px #888888;
  52 + .left-logo {
  53 + float: left;
  54 + margin-top: 10px;
  55 + }
  56 + .menu-collapse-button {
  57 + float: left;
  58 + margin-left: 20px;
  59 + cursor: hand;
  60 + cursor: pointer;
  61 + .font-style {
  62 + width: 1.2em;
  63 + height: 1.2em;
  64 + margin-right: 8px;
  65 + margin-top: 20px;
  66 + }
  67 + }
  68 + .right-buttons {
  69 + float: right;
  70 + }
  71 +}
  72 +</style>
src/layout/menu.vue 0 → 100644
  1 +<script setup lang="ts">
  2 +import { ref, computed } from "vue";
  3 +import * as store from "@/pinia/index";
  4 +import styles from '@/style/global.module.scss';
  5 +// TIP 使用element plus ICON
  6 +import {
  7 + Location,
  8 + Document,
  9 + Menu as IconMenu,
  10 + Setting,
  11 +} from "@element-plus/icons-vue";
  12 +// TIP 获取scss样式变量
  13 +let styleData = computed(()=>{
  14 + return styles
  15 +});
  16 +</script>
  17 +
  18 +<template>
  19 + <el-scrollbar wrap-class="scrollbar-wrapper">
  20 + <el-menu
  21 + default-active="2"
  22 + :background-color="styleData.menuBg"
  23 + :text-color="styleData.menuText"
  24 + :active-text-color="styleData.menuActiveText"
  25 + class="el-menu-vertical-demo"
  26 + :collapse-transition="false"
  27 + :collapse="store.family.appStore().isCollapse"
  28 + >
  29 + <el-sub-menu index="1">
  30 + <template #title>
  31 + <el-icon><location /></el-icon>
  32 + <span>Navigator One</span>
  33 + </template>
  34 + <el-menu-item-group>
  35 + <template #title><span>Group One</span></template>
  36 + <el-menu-item index="1-1">item one</el-menu-item>
  37 + <el-menu-item index="1-2">item two</el-menu-item>
  38 + </el-menu-item-group>
  39 + <el-menu-item-group title="Group Two">
  40 + <el-menu-item index="1-3">item three</el-menu-item>
  41 + </el-menu-item-group>
  42 + <el-sub-menu index="1-4">
  43 + <template #title><span>item four</span></template>
  44 + <el-menu-item index="1-4-1">item one</el-menu-item>
  45 + </el-sub-menu>
  46 + </el-sub-menu>
  47 + <el-menu-item index="2">
  48 + <el-icon><icon-menu /></el-icon>
  49 + <template #title>Navigator Two</template>
  50 + </el-menu-item>
  51 + <el-menu-item index="3" disabled>
  52 + <el-icon><document /></el-icon>
  53 + <template #title>Navigator Three</template>
  54 + </el-menu-item>
  55 + <el-menu-item index="4">
  56 + <el-icon><setting /></el-icon>
  57 + <template #title>Navigator Four</template>
  58 + </el-menu-item>
  59 + </el-menu>
  60 + </el-scrollbar>
  61 +</template>
  62 +<style lang="scss" scoped>
  63 +.el-menu-vertical-demo:not(.el-menu--collapse) {
  64 + width: $sideBarWidth;
  65 + min-height: calc(100vh - 60px);
  66 +}
  67 +</style>
src/main.ts
@@ -3,14 +3,23 @@ import App from &#39;./App.vue&#39; @@ -3,14 +3,23 @@ import App from &#39;./App.vue&#39;
3 import router from '@/router/index' 3 import router from '@/router/index'
4 import promise from 'es6-promise' 4 import promise from 'es6-promise'
5 // import store from '@/store/index' 5 // import store from '@/store/index'
  6 +
6 import ElementPlus from 'element-plus' 7 import ElementPlus from 'element-plus'
7 import 'element-plus/dist/index.css' 8 import 'element-plus/dist/index.css'
8 -import zhCn from 'element-plus/es/locale/lang/zh-cn' // elementUI配置中文环境 9 +import zhCn from 'element-plus/es/locale/lang/zh-cn' // TIP elementUI配置中文环境
  10 +
9 import '@/utils/progress' 11 import '@/utils/progress'
10 import '@/style/reset.scss' 12 import '@/style/reset.scss'
  13 +import * as globalVariable from '@/utils/variable'
  14 +
  15 +// TIP store 管理,含存数据到本地缓存
11 import { createPinia } from 'pinia' 16 import { createPinia } from 'pinia'
12 import { createPersistedState } from 'pinia-persistedstate-plugin' 17 import { createPersistedState } from 'pinia-persistedstate-plugin'
13 const pinia = createPinia() 18 const pinia = createPinia()
  19 +pinia.use(createPersistedState({
  20 + key: 'store-data',
  21 + logger: process.env.NODE_ENV === 'production'? false: true
  22 +}))
14 23
15 promise.polyfill() 24 promise.polyfill()
16 25
@@ -18,13 +27,14 @@ promise.polyfill() @@ -18,13 +27,14 @@ promise.polyfill()
18 console.log('current env: ', process.env.NODE_ENV) 27 console.log('current env: ', process.env.NODE_ENV)
19 console.log('current api url: ', import.meta.env.VITE_BASE_URL) 28 console.log('current api url: ', import.meta.env.VITE_BASE_URL)
20 29
21 -pinia.use(createPersistedState({  
22 - key: 'store-data',  
23 - logger: process.env.NODE_ENV === 'production'? false: true  
24 -})) 30 +const app = createApp(App)
  31 +
  32 +// TIP 全局静态变量挂载
  33 +for(const i in globalVariable) {
  34 + app.config.globalProperties[i] = globalVariable[i]
  35 +}
25 36
26 -createApp(App)  
27 -.use(router) 37 +app.use(router)
28 // .use(store) 38 // .use(store)
29 .use(pinia) 39 .use(pinia)
30 .use(ElementPlus, { 40 .use(ElementPlus, {
src/pinia/modules/app.ts 0 → 100644
  1 +const id = 'app'
  2 +const state = () => ({
  3 + isCollapse: false,
  4 + })
  5 + const getters = {
  6 + getCollapseStatus () {
  7 + return this.isCollapse
  8 + }
  9 + }
  10 + const actions = {
  11 + toggleCollapse() {
  12 + this.isCollapse = !this.isCollapse
  13 + },
  14 + }
  15 +
  16 +export default {
  17 + namespace: true,
  18 + actions,
  19 + getters,
  20 + state,
  21 + id
  22 +}
0 \ No newline at end of file 23 \ No newline at end of file
src/router/index.ts
1 import { createRouter, createWebHashHistory } from 'vue-router' 1 import { createRouter, createWebHashHistory } from 'vue-router'
  2 +import Layout from '@/layout/enter.vue'
2 const routes: Array<any> = [ 3 const routes: Array<any> = [
3 { path: '/', redirect: { name: 'Home' } }, 4 { path: '/', redirect: { name: 'Home' } },
4 { 5 {
5 path: '/home', 6 path: '/home',
6 name: 'Home', 7 name: 'Home',
7 component: () => import(/* webpackChunkName: "Home" */ '@/views/tengxi/index.vue') 8 component: () => import(/* webpackChunkName: "Home" */ '@/views/tengxi/index.vue')
  9 + // component: () => import(/* webpackChunkName: "Home" */ '@/layout/enter.vue')
8 }, 10 },
9 { 11 {
10 - path: '/about', 12 + path: '/',
  13 + component: Layout,
11 name: 'About', 14 name: 'About',
12 - component: () => import(/* webpackChunkName: "About" */ '@/views/tengxi/about.vue') 15 + children: [{
  16 + path: 'About',
  17 + name: 'About',
  18 + component: () => import(/* webpackChunkName: "About" */ '@/views/tengxi/about.vue'),
  19 + meta: { title: 'About' }
  20 + }]
  21 + // component: () => import(/* webpackChunkName: "About" */ '@/views/tengxi/about.vue')
13 }, 22 },
14 { 23 {
15 path: '/login', 24 path: '/login',
src/style/element.scss
1 -// elementUI 部分样式重写 1 +// TIP elementUI 部分样式重写
2 2
3 -// 解决表格闪烁 3 +// TIP 解决表格闪烁
4 .el-table{ 4 .el-table{
5 max-width: 99%; 5 max-width: 99%;
  6 +}
  7 +
  8 +// TIP 解决左侧菜单scrollbar左侧有间隔
  9 +.el-scrollbar {
  10 + width: 100%;
6 } 11 }
7 \ No newline at end of file 12 \ No newline at end of file
src/style/global.module.scss 0 → 100644
  1 +// TIP 引入全局scss变量
  2 +@import './status.scss';
0 \ No newline at end of file 3 \ No newline at end of file
src/style/menu.scss 0 → 100644
  1 +// TIP 左侧菜单固定宽度,右边页面部分自适应
  2 +.main-container {
  3 + *zoom: 1; position: relative;
  4 + .sidebar-container {
  5 + width: $sideBarWidth;
  6 + position: absolute;
  7 + left: 0;
  8 + top: 0;
  9 + }
  10 + .page-container {
  11 + padding: 20px;
  12 + margin-left: $sideBarWidthThin;
  13 + }
  14 +}
0 \ No newline at end of file 15 \ No newline at end of file
src/style/reset.scss
1 -// html原生标签样式统一重写 1 +// TIP html原生标签样式统一重写
2 2
  3 +@import './menu.scss';
3 @import './element.scss'; 4 @import './element.scss';
4 -@import './variable.scss';  
5 -@import './status.scss';  
6 5
7 body { 6 body {
8 height: 100%; 7 height: 100%;
@@ -10,6 +9,8 @@ body { @@ -10,6 +9,8 @@ body {
10 -webkit-font-smoothing: antialiased; 9 -webkit-font-smoothing: antialiased;
11 text-rendering: optimizeLegibility; 10 text-rendering: optimizeLegibility;
12 font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 11 font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
  12 + margin: 0;
  13 + padding: 0;
13 } 14 }
14 15
15 label { 16 label {
src/style/status.scss
1 -// 状态颜色 1 +// TIP 状态颜色
2 2
3 $danger: #F56C6C; 3 $danger: #F56C6C;
4 $success: #67C23A; 4 $success: #67C23A;
src/style/variable.scss
1 -// 主题色变量 1 +// TIP 主题色变量
2 $menuText:#bfcbd9; 2 $menuText:#bfcbd9;
3 $menuActiveText:#409EFF; 3 $menuActiveText:#409EFF;
4 $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951 4 $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
@@ -10,6 +10,7 @@ $subMenuBg:#1f2d3d; @@ -10,6 +10,7 @@ $subMenuBg:#1f2d3d;
10 $subMenuHover:#001528; 10 $subMenuHover:#001528;
11 11
12 $sideBarWidth: 210px; 12 $sideBarWidth: 210px;
  13 +$sideBarWidthThin: 63px;
13 14
14 // the :export directive is the magic sauce for webpack 15 // the :export directive is the magic sauce for webpack
15 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 16 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
@@ -22,4 +23,5 @@ $sideBarWidth: 210px; @@ -22,4 +23,5 @@ $sideBarWidth: 210px;
22 subMenuBg: $subMenuBg; 23 subMenuBg: $subMenuBg;
23 subMenuHover: $subMenuHover; 24 subMenuHover: $subMenuHover;
24 sideBarWidth: $sideBarWidth; 25 sideBarWidth: $sideBarWidth;
  26 + sideBarWidthThin: $sideBarWidthThin;
25 } 27 }
src/utils/progress.ts
@@ -9,7 +9,7 @@ router.beforeEach((to: any, from: any, next: any) =&gt; { @@ -9,7 +9,7 @@ router.beforeEach((to: any, from: any, next: any) =&gt; {
9 const hasToken = 1 && 1 9 const hasToken = 1 && 1
10 const whiteList = ['/login'] 10 const whiteList = ['/login']
11 11
12 - // 页面标题设置 12 + // TIP 页面标题设置
13 globalThis.document.title = to.meta.title? to.meta.title: import.meta.env.VITE_APP_TITLE 13 globalThis.document.title = to.meta.title? to.meta.title: import.meta.env.VITE_APP_TITLE
14 14
15 if (hasToken) { 15 if (hasToken) {
src/utils/variable.ts 0 → 100644
  1 +const $defaultImage = 'https://wjj.ys2.cnliveimg.com/769/img_new/1593746590552.png' // TIP 坏图片占位图
  2 +
  3 +export {
  4 + $defaultImage
  5 +}
0 \ No newline at end of file 6 \ No newline at end of file
src/views/tengxi/about.vue
@@ -39,9 +39,9 @@ const data = reactive([ @@ -39,9 +39,9 @@ const data = reactive([
39 </script> 39 </script>
40 40
41 <template> 41 <template>
42 - <p class="tengxi">Tengxi About Page</p> 42 + <!-- <p class="tengxi">Tengxi About Page</p>
43 <el-button type="primary">成功</el-button> 43 <el-button type="primary">成功</el-button>
44 - <p>测试pinia数据:{{all.family.dataStore().data1}}</p> 44 + <p>测试pinia数据:{{all.family.dataStore().data1}}</p> -->
45 <!-- <el-button @click="addAge">长一岁</el-button> 45 <!-- <el-button @click="addAge">长一岁</el-button>
46 <el-button @click="initPinia">初始化pinia</el-button> --> 46 <el-button @click="initPinia">初始化pinia</el-button> -->
47 47
vite.config.ts
1 import { defineConfig, loadEnv } from 'vite' 1 import { defineConfig, loadEnv } from 'vite'
2 import vue from '@vitejs/plugin-vue' 2 import vue from '@vitejs/plugin-vue'
3 -import viteCompression from 'vite-plugin-compression' // gzip压缩  
4 -import legacy from '@vitejs/plugin-legacy' // 兼容性 3 +import viteCompression from 'vite-plugin-compression' // TIP gzip压缩
  4 +import legacy from '@vitejs/plugin-legacy' // TIP 兼容性
5 const { resolve } = require('path') 5 const { resolve } = require('path')
6 6
7 // https://vitejs.dev/config/ 7 // https://vitejs.dev/config/
@@ -33,7 +33,7 @@ export default ({command, mode}) =&gt; { @@ -33,7 +33,7 @@ export default ({command, mode}) =&gt; {
33 css: { 33 css: {
34 postcss: { 34 postcss: {
35 plugins: [ 35 plugins: [
36 - // 关打包@charset must be the first rule in the file警告 36 + // TIP 关打包@charset must be the first rule in the file警告
37 { 37 {
38 postcssPlugin: 'internal:charset-removal', 38 postcssPlugin: 'internal:charset-removal',
39 AtRule: { 39 AtRule: {
@@ -48,8 +48,10 @@ export default ({command, mode}) =&gt; { @@ -48,8 +48,10 @@ export default ({command, mode}) =&gt; {
48 }, 48 },
49 preprocessorOptions: { 49 preprocessorOptions: {
50 scss: { 50 scss: {
51 - // 关打包@charset must be the first rule in the file警告  
52 - charset: false 51 + // TIP 关打包@charset must be the first rule in the file警告
  52 + charset: false,
  53 + // TIP 全局使用scss变量
  54 + additionalData: '@import "@/style/variable.scss";'
53 } 55 }
54 } 56 }
55 }, 57 },