jwyu 1 year ago
parent
commit
9fd422b48f

+ 2 - 1
README.md

@@ -1,2 +1,3 @@
 # 移动ETA
-
+1. 图片等静态资源如果要放在oss中请放入 oss://hzchart/static/ETA_mobile/ 目录下。
+2. 

+ 1 - 1
index.html

@@ -3,7 +3,7 @@
   <head>
     <meta charset="UTF-8" />
     <link rel="icon" type="image/x-icon" href="/fa.ico"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
     <title>移动ETA</title>
   </head>
   <body>

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^4.1.0",
+    "postcss-px-to-viewport": "^1.1.1",
     "sass": "^1.44.0",
     "unplugin-vue-components": "^0.24.1",
     "vite": "^4.2.0"

+ 21 - 0
postcss.config.cjs

@@ -0,0 +1,21 @@
+module.exports = {
+  plugins: {
+    "postcss-px-to-viewport": {
+      unitToConvert: "px", // 需要转换的单位,默认为"px"
+      viewportWidth: 750, //  设计稿的视口宽度
+      unitPrecision: 5, // 单位转换后保留的精度
+      propList: ["*"], // 能转化为vw的属性列表
+      viewportUnit: "vw", //  希望使用的视口单位
+      fontViewportUnit: "vw", // 字体使用的视口单位
+      selectorBlackList: [], // 需要忽略的CSS选择器
+      minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
+      mediaQuery: false, // 媒体查询里的单位是否需要转换单位
+      replace: true, // 是否直接更换属性值,而不添加备用属性
+      exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件
+      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
+      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
+      landscapeUnit: "px", // 横屏时使用的单位
+      landscapeWidth: 568, // 横屏时使用的视口宽度
+    },
+  },
+};

+ 24 - 0
src/api/ppt.js

@@ -11,6 +11,30 @@ export function apiPPTClassify(){
     return get('/pptv2/groups',{})
 }
 
+/**
+ * 复制ppt目录
+ * @param GroupId 目录id
+ */
+export function apiPPTCatalogueCopy(params){
+    return post('/pptv2/group/copy',params)
+}
+
+/**
+ * 删除ppt目录
+ * @param GroupId 目录id
+ */
+export function apiPPTCatalogueDel(params){
+    return post('/pptv2/group/delete',params)
+}
+
+/**
+ * 删除ppt
+ * @param PptId pptid
+ */
+export function apiPPTDel(params){
+    return post('/pptv2/delete',params)
+}
+
 /**
  * 获取ppt详情
  * @param PptId

BIN
src/assets/imgs/icon_del.png


BIN
src/assets/imgs/logo_icon.png


BIN
src/assets/imgs/ppt/icon01.png


BIN
src/assets/imgs/ppt/ppt_icon_copy.png


BIN
src/assets/imgs/ppt/ppt_icon_en.png


BIN
src/assets/imgs/ppt/ppt_icon_file.png


BIN
src/assets/imgs/ppt/ppt_icon_menu.png


BIN
src/assets/imgs/ppt/ppt_icon_write.png


BIN
src/assets/imgs/ppt/ppt_icon_zh.png


BIN
src/assets/imgs/tabbar/home-s.png


BIN
src/assets/imgs/tabbar/home.png


BIN
src/assets/imgs/tabbar/user-s.png


BIN
src/assets/imgs/tabbar/user.png


BIN
src/assets/imgs/userLogo.png


+ 12 - 1
src/assets/styles/common.scss

@@ -1,9 +1,20 @@
+:root:root {
+    --van-blue: #0052D9;
+    --van-gray-3:#DCDFE6;
+}
 
 html,body{
-    font-size: 14px;
+    font-size: 28px;
     color: #333;
 }
 
+@media screen and (min-width:650px){
+    html,body{
+        font-size: 14px;
+        color: #333;
+    }
+}
+
 div{
     box-sizing: border-box;
 }

+ 5 - 0
src/assets/styles/var.scss

@@ -0,0 +1,5 @@
+
+$theme-color:#0052D9;//主题色
+$font-grey:#666666;
+$border-color:#DCDFE6;
+$theme-red:#C54322;

+ 5 - 0
src/hooks/common.js

@@ -0,0 +1,5 @@
+
+// 导出静态图片资源
+export function getStaticImg(path){
+    return new URL(`../assets/imgs/${path}`, import.meta.url).href
+}

+ 101 - 1
src/layouts/Index.vue

@@ -1,13 +1,113 @@
 <script setup>
+import {ref} from 'vue'
+import {Base64} from 'js-base64'
+
+let userInfo=ref(null)
+let showUserInfo=ref(false)
+function getUserInfo(){
+    const val=localStorage.getItem('userInfo')?JSON.parse(Base64.decode(localStorage.getItem('userInfo'))):null
+    userInfo.value=val
+    console.log(val);
+}
+getUserInfo()
+
+
+function handleLoginOut(){
+    localStorage.removeItem('token')
+    localStorage.removeItem('userInfo')
+    router.replace('/login')
+}
+
+
 
 </script>
 
 <template>
     <div class="layout-wrap">
-
+        <div class="pad-header-box">
+            <div></div>
+            <van-popover v-model:show="showUserInfo" placement="bottom-end">
+                <div class="userinfo-box_pad" v-if="userInfo">
+                    <div class="info-item">
+                        <span>姓名:</span>
+                        <span>{{userInfo.RealName}}</span>
+                    </div>
+                    <div class="info-item">
+                        <span>ID:</span>
+                        <span>{{userInfo.AdminName}}</span>
+                    </div>
+                    <div class="info-item">
+                        <span>公司名称:</span>
+                        <span>弘则弥道(上海)投资咨询有限公司</span>
+                    </div>
+                    <div class="info-item">
+                        <span>所属部门:</span>
+                        <span>产品技术部</span>
+                    </div>
+                    <div class="info-item">
+                        <span>角色:</span>
+                        <span>{{userInfo.RoleName}}</span>
+                    </div>
+                    <van-button class="login_out_btn" block type="primary" round @click="handleLoginOut">退出登录</van-button>
+                </div>
+                <template #reference>
+                    <div class="user-box" v-if="userInfo">
+                        <img class="avatar" src="@/assets/imgs/userLogo.png" alt="">
+                        <span>{{userInfo.RealName}},欢迎您!</span>
+                    </div>
+                </template>
+            </van-popover>
+        </div>
+        <router-view />
     </div>
 </template>
 
+<style lang="scss">
+@media screen and (min-width:650px){
+    .userinfo-box_pad{
+        padding: 20px;
+        .info-item{
+            padding: 5px 0;
+        }
+        .login_out_btn{
+            width: 50%;
+            margin-top: 20px;
+            margin-left: auto;
+            margin-right: auto;
+        }
+    }
+}
+
+</style>
+
 <style lang="scss" scoped>
+.pad-header-box{
+    display: none;
+}
 
+@media screen and (min-width:650px){
+    .pad-header-box{
+        display: block;
+        height: 60px;
+        position: sticky;
+        top: 0;
+        width: 100%;
+        padding:0 30px;
+        border-bottom: 1px solid $border-color;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .user-box{
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            font-size: 16px;
+            width: 350px;
+            .avatar{
+                width: 40px;
+                margin-right: 5px;
+            }
+        }
+    }
+}
 </style>

+ 0 - 1
src/main.js

@@ -4,7 +4,6 @@ import router from "./router";
 import './plugin/vant'
 import 'normalize.css'
 import './assets/styles/common.scss'
-// import './style.css'
 
 
 

+ 63 - 53
src/router/index.js

@@ -1,67 +1,77 @@
 import { createRouter, createWebHistory } from "vue-router";
-import {pptRoutes} from './ppt'
+import { pptRoutes } from "./ppt";
 /**
  * 说明
  * 此文件为路由配置入口文件
  * 路由子模块请按模块拆分到同文件夹下其他文件中
- * meta中
- * keepAlive表示是否要缓存
- * hasBack 控制是否有返回按钮
+ * meta
  */
 
 const routes = [
-  {
-    path:"/login",
-    name:"Login",
-    component: () => import("@/views/Login.vue"),
-    meta: { title: "登录" },
-  },
-  // tabbar模块
-  {
-    path:'/',
-    name:"Tabbar",
-    redirect: '/home',
-    component: () => import("@/views/tabbar/Index.vue"),
-    meta: { title: "移动ETA" },
-    children:[
-      {
-        path:"home",
-        name:"Home",
-        component: () => import("@/views/tabbar/Home.vue"),
-        meta: { title: "移动ETA" },
-      },
-      {
-        path:"user",
-        name:"User",
-        component: () => import("@/views/tabbar/User.vue"),
-        meta: { title: "移动ETA" },
-      }
-    ]
-  },
-  // ppt模块
-  ...pptRoutes,
-  {
-    path: "/:pathMatch(.*)",
-    name: "error",
-    component: () => import("@/views/404.vue"),
-    meta: { title: "404" },
-  },
+  	{
+		path: "/login",
+		name: "Login",
+		component: () => import("@/views/Login.vue"),
+		meta: { title: "登录" },
+  	},
+  	
+	{
+		path:"/",
+		name:"Layouts",
+		redirect: "/tabbar/home",
+		component:()=>import('@/layouts/Index.vue'),
+		children:[
+			// tabbar模块
+			{
+				path: "/tabbar",
+				name: "Tabbar",
+				redirect: "/tabbar/home",
+				component: () => import("@/views/tabbar/Index.vue"),
+				meta: { title: "移动ETA" },
+				children: [
+					{
+						path: "/tabbar/home",
+						name: "Home",
+						component: () => import("@/views/tabbar/Home.vue"),
+						meta: { title: "移动ETA" },
+					},
+					{
+						path: "/tabbar/user",
+						name: "User",
+						component: () => import("@/views/tabbar/User.vue"),
+						meta: { title: "移动ETA" },
+					},
+				],
+			},
+
+			// ppt模块
+			...pptRoutes,
+		]
+	},
+	
+	
+	{
+		path: "/:pathMatch(.*)",
+		name: "error",
+		component: () => import("@/views/404.vue"),
+		meta: { title: "404" },
+	},
 ];
 
 const router = createRouter({
-  history: createWebHistory(import.meta.env.VITE_APP_BASE_URL),
-  routes,
-  scrollBehavior(to, from, savedPosition) {
-    if (savedPosition && to.meta.keepAlive) {
-      return savedPosition;
-    } else {
-      return new Promise((resolve) => {
-        setTimeout(() => {
-          resolve({ left: 0, top: 0 });
-        }, 0);
-      });
-    }
-  },
+	history: createWebHistory(import.meta.env.VITE_APP_BASE_URL),
+  	routes,
+	scrollBehavior(to, from, savedPosition) {
+		if (savedPosition && to.meta.keepAlive) {
+			return savedPosition;
+		} else {
+			return new Promise((resolve) => {
+				setTimeout(() => {
+					resolve({ left: 0, top: 0 });
+				}, 0);
+			});
+		}
+	},
 });
 
 // router.beforeEach((to, from, next) => {

+ 138 - 23
src/views/Login.vue

@@ -23,7 +23,17 @@ const onSubmit = (values) => {
             localStorage.setItem('token',res.Data.Authorization)
             localStorage.setItem('account',Base64.encode(values.username))
 			localStorage.setItem('checkPass',Base64.encode(values.password))
-            router.replace('/home')
+            const userInfo={
+                AdminName:res.Data.AdminName,
+                Authority:res.Data.Authority,
+                ProductName:res.Data.ProductName,
+                RealName:res.Data.RealName,
+                RoleName:res.Data.RoleName,
+                RoleTypeCode:res.Data.RoleTypeCode,
+                SysRoleTypeCode:res.Data.SysRoleTypeCode
+            }
+            localStorage.setItem('userInfo',Base64.encode(JSON.stringify(userInfo)))
+            router.replace('/')
         }
     })
 };
@@ -31,32 +41,137 @@ const onSubmit = (values) => {
 
 <template>
     <div class="login-page">
-        <van-form @submit="onSubmit">
-            <van-cell-group inset>
-                <van-field
-                    v-model="username"
-                    name="username"
-                    label="用户名"
-                    placeholder="用户名"
-                    :rules="[{ required: true, message: '请填写用户名' }]"
-                />
-                <van-field
-                    v-model="password"
-                    type="password"
-                    name="password"
-                    label="密码"
-                    placeholder="密码"
-                    :rules="[{ required: true, message: '请填写密码' }]"
-                />
-            </van-cell-group>
-            <div style="margin: 16px;">
-                <van-button round block type="primary" native-type="submit">
-                提交
-                </van-button>
+        <van-form class="form-box" @submit="onSubmit">
+            <div class="logo-wrap">
+                <img class="logo" src="@/assets/imgs/logo_icon.png" alt="">
+                <div class="title">Bind on account,</div>
+                <div class="sub-title">sign in to continue</div>
+            </div>
+            <van-field
+                class="form-input-box"
+                v-model="username"
+                name="username"
+                placeholder="请输入管理后台账号"
+                :rules="[{ required: true, message: '请填写账号' }]"
+            />
+            <van-field
+                class="form-input-box"
+                v-model="password"
+                type="password"
+                name="password"
+                placeholder="请输入密码"
+                :rules="[{ required: true, message: '请填写密码' }]"
+            />
+            <div class="btn-box">
+                <van-button 
+                    round 
+                    block 
+                    type="primary" 
+                    native-type="submit"
+                >登录</van-button>
             </div>
         </van-form>
+        <img class="pad-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/login_img.png" alt="">
+        <div class="mobile-bot-text">Long time&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;More profit</div>
     </div>
 </template>
 
 <style lang="scss" scoped>
+.login-page{
+    padding: 0 80px;
+}
+.logo-wrap{
+    margin-top: 220px;
+    margin-bottom: 180px;
+    .logo{
+        width: 225px;
+    }
+    .title{
+        font-size: 58px;
+        margin-top: 41px;
+        margin-bottom: 30px;
+        font-weight: bold;
+    }
+    .sub-title{
+        color: #999;
+    }
+    
+}
+
+.form-input-box{
+    padding-left: 0;
+    padding-right: 0;
+    border-bottom: 1px solid $border-color;
+    margin-bottom: 40px;
+    &::after{
+        display: none;
+    }
+}
+
+.mobile-bot-text{
+    position: fixed;
+    left: 50%;
+    transform: translateX(-50%);
+    bottom: 90px;
+    color: #999;
+}
+.pad-img{
+    display: none;
+}
+
+@media screen and (min-width:650px) {
+    .login-page{
+        margin-top: auto;
+        padding: 30px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .form-box{
+            flex-shrink: 0;
+            width: 47%;
+            max-width: 400px;
+        }
+        .logo-wrap{
+            margin-top: 0;
+            margin-bottom: 40px;
+            .logo{
+                width: 160px;
+            }
+            .title{
+                font-size: 32px;
+                margin-top: 41px;
+                margin-bottom: 20px;
+                font-weight: bold;
+            }
+            .sub-title{
+                color: #999;
+            }
+            
+        }
+        .form-input-box{
+            padding-left: 0;
+            padding-right: 0;
+            border: 1px solid $border-color;
+            border-radius: 4px;
+            padding: 12px 20px;
+            margin-bottom: 40px;
+            &::after{
+                display: none;
+            }
+        }
+
+        .pad-img{
+            display: block;
+            width: 48%;
+            height: 80vh;
+            max-height: 500px;
+            border-radius: 40px;
+            object-fit: cover;
+        }
+    }
+
+    .mobile-bot-text{
+        display: none;
+    }
+}
 </style>

+ 4 - 45
src/views/ppt/Index.vue

@@ -1,25 +1,11 @@
 <script setup>
 import {ref,reactive} from 'vue'
 import {apiPPTClassify} from '@/api/ppt'
+import MobileClassifyWrap from './components/MobileClassifyWrap.vue'
 
 const searchVal=ref('')
 
-// 获取ppt分类数据
-const classifyState = reactive({
-    privateList:[],
-    publicList:[],
-    activeType:['1','2'],//一级分类是否展开
-    myActiveType:[],//我的ppt文件夹是否展开
-    pubActiveType:[],//公共ppt文件夹是否展开
-})
-async function getPPTClassify(){
-    const res=await apiPPTClassify()
-    if(res.Ret===200){
-        classifyState.privateList=res.Data.PrivateList||[]
-        classifyState.publicList=res.Data.PublicList||[]
-    }
-}
-getPPTClassify()
+
 
 
 </script>
@@ -27,36 +13,9 @@ getPPTClassify()
 <template>
     <div class="ppt-index-page">
         <div class="search-box">
-            <van-search v-model="searchVal" placeholder="请输入搜索关键词" />
-        </div>
-        <div class="classify-list-wrap">
-            <van-collapse v-model="classifyState.activeType">
-                <van-collapse-item title="我的PPT" name="1">
-                    <van-collapse v-model="classifyState.myActiveType">
-                        <van-collapse-item 
-                            :title="item.GroupName" 
-                            :name="item.GroupId" 
-                            v-for="item in classifyState.privateList"
-                            :key="item.GroupId"
-                        >   
-                            <div v-for="_item in item.PptList" :key="_item.GroupId">{{_item.Title}}</div>
-                        </van-collapse-item>
-                    </van-collapse>
-                </van-collapse-item>
-                <van-collapse-item title="公共PPT" name="2">
-                    <van-collapse v-model="classifyState.pubActiveType">
-                        <van-collapse-item 
-                            :title="item.GroupName" 
-                            :name="item.GroupId" 
-                            v-for="item in classifyState.publicList"
-                            :key="item.GroupId"
-                        >   
-                            <div v-for="_item in item.PptList" :key="_item.GroupId">{{_item.Title}}</div>
-                        </van-collapse-item>
-                    </van-collapse>
-                </van-collapse-item>
-            </van-collapse>
+            <van-search shape="round" readonly placeholder="请输入搜索关键词" />
         </div>
+        <MobileClassifyWrap/>
     </div>
 </template>
 

+ 256 - 0
src/views/ppt/components/MobileClassifyWrap.vue

@@ -0,0 +1,256 @@
+<script setup>
+import {useClassify} from '../hooks/useClassify'
+
+const {
+    classifyState,
+    getPPTClassifyData,
+    fileOptState,
+    handleShowFileOpt,
+    handlePPTCatalogueCopy,
+    handlePPTCatalogueDel,
+    
+    PPTOptState,
+    handleShowPPTOpt,
+    handlePPTDel
+}=useClassify()
+
+getPPTClassifyData()
+
+
+
+</script>
+
+<template>
+    <div class="mobile-classify-wrap">
+        <van-collapse v-model="classifyState.activeType" :border="false">
+            <van-collapse-item class="level-one" name="myPPT" :is-link="false">
+                <template #title>
+                    <div class="title-first">我的PPT<van-icon name="arrow-down" /></div>
+                </template>
+                <van-collapse class="level-two" v-model="classifyState.myActiveType" :border="false">
+                    <van-collapse-item
+                        :name="item.GroupId"
+                        v-for="item in classifyState.privateList"
+                        :key="item.GroupId"
+                        :is-link="false"
+                    >
+                        <template #title>
+                            <div class="title-second">
+                                <img src="@/assets/imgs/ppt/ppt_icon_file.png" alt="">
+                                <span>{{item.GroupName}}</span>
+                                <img @click.stop="handleShowFileOpt(item)" class="menu-icon" src="@/assets/imgs/ppt/ppt_icon_menu.png" alt="">
+                            </div>
+                        </template>
+                        <div 
+                            class="ppt-item"
+                            v-for="_item in item.PptList" 
+                            :key="_item.GroupId"
+                        >
+                            <span>{{ _item.Title }}</span>
+                            <img @click.stop="handleShowPPTOpt(_item)" class="menu-icon" src="@/assets/imgs/ppt/ppt_icon_menu.png" alt="">
+                        </div>
+                    </van-collapse-item>
+                </van-collapse>
+            </van-collapse-item>
+            <van-collapse-item class="level-one" name="pubPPT" :is-link="false">
+                <template #title>
+                    <div class="title-first">公共PPT<van-icon name="arrow-down" /></div>
+                </template>
+                <van-collapse class="level-two" v-model="classifyState.pubActiveType" :border="false">
+                    <van-collapse-item
+                        :name="item.GroupId"
+                        v-for="item in classifyState.publicList"
+                        :key="item.GroupId"
+                        :is-link="false"
+                    >
+                        <template #title>
+                            <div class="title-second">
+                                <img src="@/assets/imgs/ppt/ppt_icon_file.png" alt="">
+                                <span>{{item.GroupName}}</span>
+                                <!-- <img class="menu-icon" src="@/assets/imgs/ppt/ppt_icon_menu.png" alt=""> -->
+                            </div>
+                        </template>
+                        <div 
+                            class="ppt-item"
+                            v-for="_item in item.PptList" 
+                            :key="_item.GroupId"
+                        >
+                            <span>{{ _item.Title }}</span>
+                            <img class="menu-icon" src="@/assets/imgs/ppt/ppt_icon_menu.png" alt="">
+                        </div>
+                    </van-collapse-item>
+                </van-collapse>
+            </van-collapse-item>
+        </van-collapse>
+    </div>
+
+    <!-- 目录编辑弹窗 -->
+    <van-popup 
+        v-model:show="fileOptState.show" 
+        round
+        position="bottom"
+    >
+        <div class="file-opt-wrap">
+            <div class="item border">
+                <img src="@/assets/imgs/ppt/ppt_icon_file.png" alt="">
+                <span>{{fileOptState.data.GroupName}}</span>
+            </div>
+            <div class="item border blue" @click="handlePPTCatalogueCopy">
+                <img src="@/assets/imgs/ppt/ppt_icon_copy.png" alt="">
+                <span>复制</span>
+            </div>
+            <div class="item border blue">
+                <img src="@/assets/imgs/ppt/ppt_icon_write.png" alt="">
+                <span>重命名</span>
+            </div>
+            <div class="item border red" @click="handlePPTCatalogueDel">
+                <img src="@/assets/imgs/icon_del.png" alt="">
+                <span>删除</span>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- ppt编辑弹窗 -->
+    <van-popup 
+        v-model:show="PPTOptState.show" 
+        round
+        position="bottom"
+    >
+        <div class="file-opt-wrap ppt-opt-wrap">
+            <div class="item border top-box">
+                <div class="flex">
+                    <img class="ppt-icon" src="@/assets/imgs/ppt/icon01.png" alt="">
+                    <div >
+                        <div class="title">{{PPTOptState.data.Title}}</div>
+                        <div class="name">
+                            {{PPTOptState.data.AdminRealName}}
+                            {{PPTOptState.data.PptCreateTime.split(' ')[0]}}
+                            创建
+                        </div>
+                    </div>
+                </div>
+                <div class="flex">
+                    <span style="margin-right:4px">公开</span>
+                    <van-switch size="22px" v-model="PPTOptState.data.IsSingleShareBoolean"/>
+                </div>
+                
+            </div>
+            <div class="item border blue" @click="handlePPTCatalogueCopy">
+                <img src="@/assets/imgs/ppt/ppt_icon_copy.png" alt="">
+                <span>复制</span>
+            </div>
+            <div class="item border red" @click="handlePPTCatalogueDel">
+                <img src="@/assets/imgs/icon_del.png" alt="">
+                <span>删除</span>
+            </div>
+        </div>
+    </van-popup>
+</template>
+
+<style lang="scss">
+.level-one{
+    .van-collapse-item__title--expanded:after{
+        display: none !important;
+    }
+}
+.van-collapse-item__content{
+    padding: 0;
+}
+.van-collapse-item--border:after{
+    display: none;
+}
+.van-collapse-item__title--expanded{
+    padding-bottom: 0;
+}
+.van-collapse-item__wrapper{
+    margin-bottom: 20px;
+}
+</style>
+<style lang="scss" scoped>
+
+.mobile-classify-wrap{
+    .level-one{
+        margin-bottom: 30px;
+        &::after{
+            display: none;
+        }
+    }
+    .level-two{
+        padding-top: 20px;
+    }
+    .title-first{
+        font-size: 32px;
+    }
+    .title-second{
+        display: flex;
+        align-items: center;
+        position: relative;
+        img{
+            width: 32px;
+            margin-right: 20px;
+        }
+        .menu-icon{
+            width: 4px;
+            position: absolute;
+            right: 0;
+            margin-right: 0;
+        }
+    }
+    .ppt-item{
+        border-bottom: 1px solid $border-color;
+        margin-left: 86px;
+        margin-right: var(--van-cell-horizontal-padding);
+        padding-top: 30px;
+        padding-bottom: 10px;
+        position: relative;
+        color: $font-grey;
+        .menu-icon{
+            width: 4px;
+            position: absolute;
+            right: 0vh;
+        }
+    }
+}
+.file-opt-wrap{
+    .item{
+        padding: 42px;
+        display: flex;
+        align-items: center;
+        img{
+            width: 32px;
+            margin-right: 20px;
+        }
+    }
+    .border{
+        border-bottom: 1px solid $border-color;
+    }
+    .blue{
+        color: $theme-color;
+    }
+    .red{
+        color: $theme-red;
+    }
+}
+
+.ppt-opt-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        .ppt-icon{
+            width: 80px;
+        }
+        .flex{
+            display: flex;
+            align-items: center;
+            .name{
+                color: #999;
+                font-size: 24px;
+                margin-top: 5px;
+            }
+        }
+    }
+}
+
+
+
+</style>

+ 119 - 0
src/views/ppt/hooks/useClassify.js

@@ -0,0 +1,119 @@
+//ppt分类模块
+
+import {ref,reactive} from 'vue'
+import { showToast,showDialog } from 'vant';
+import {
+    apiPPTClassify,
+    apiPPTCatalogueCopy,
+    apiPPTCatalogueDel,
+    apiPPTDel
+} from '@/api/ppt'
+
+
+export function useClassify(){
+    // ppt分类数据
+    const classifyState = reactive({
+        privateList:[],
+        publicList:[],
+        activeType:['myPPT','pubPPT'],//一级分类是否展开
+        myActiveType:[],//我的ppt文件夹是否展开
+        pubActiveType:[],//公共ppt文件夹是否展开
+    })
+    // 获取ppt分类数据
+    const getPPTClassifyData=async()=>{
+        const res=await apiPPTClassify()
+        if(res.Ret===200){
+            const temPriArr=res.Data.PrivateList||[]
+            classifyState.privateList=temPriArr.map(item=>{
+                 item.PptList=item.PptList.map(_item=>{
+                    return {
+                        ..._item,
+                        IsSingleShareBoolean:_item.IsSingleShare?true:false
+                    }
+                })
+                return item
+            })
+            classifyState.myActiveType=classifyState.privateList.map(item=>{
+                return item.GroupId
+            })
+            classifyState.publicList=res.Data.PublicList||[]
+        }
+    }
+
+    // 显示目录编辑弹窗
+    const fileOptState=reactive({
+        show:false,
+        data:{},//当前点击的目录数据
+    })
+    const handleShowFileOpt=e=>{
+        fileOptState.data=e
+        fileOptState.show=true
+    }
+    // 复制ppt目录
+    const handlePPTCatalogueCopy=async()=>{
+        const res=await apiPPTCatalogueCopy({GroupId:fileOptState.data.GroupId})
+        if(res.Ret===200){
+            getPPTClassifyData()
+            fileOptState.show=false
+        }
+    }
+    // 删除ppt目录
+    const handlePPTCatalogueDel=()=>{
+        if(fileOptState.data.PptList&&fileOptState.data.PptList.length!==0){
+            showToast('该目录下有关联PPT,不允许删除');
+            return
+        }
+        showDialog({
+            title: '提示',
+            message: '删除操作不可恢复,确认删除吗?',
+        }).then(() => {
+            // on close
+            apiPPTCatalogueDel({GroupId:fileOptState.data.GroupId}).then(res=>{
+                if(res.Ret===200){
+                    getPPTClassifyData()
+                    fileOptState.show=false
+                }
+            })
+        });
+    }
+
+    // 显示ppt录编辑弹窗
+    const PPTOptState=reactive({
+        show:false,
+        data:{}
+    })
+    const handleShowPPTOpt=(e)=>{
+        PPTOptState.data=e
+        PPTOptState.show=true
+    }
+    // 删除ppt
+    const handlePPTDel=()=>{
+        showDialog({
+            title: '提示',
+            message: '删除操作不可恢复,若该PPT被共享,则同步删除共享PPT,确认删除吗?',
+        }).then(() => {
+            // on close
+            apiPPTDel({GroupId:PPTOptState.data.PptId}).then(res=>{
+                if(res.Ret===200){
+                    getPPTClassifyData()
+                    PPTOptState.show=false
+                }
+            })
+        });
+    }
+
+
+    return {
+        classifyState,
+        getPPTClassifyData,
+
+        fileOptState,
+        handleShowFileOpt,
+        handlePPTCatalogueCopy,
+        handlePPTCatalogueDel,
+
+        PPTOptState,
+        handleShowPPTOpt,
+        handlePPTDel
+    }
+}

+ 62 - 2
src/views/tabbar/Home.vue

@@ -1,14 +1,74 @@
 <script setup>
+import { useRouter } from "vue-router";
+
+const router=useRouter()
+
+function goNext(path){
+    router.push(path)
+}
 
 </script>
 
 <template>
     <div class="home-page">
-        <van-button>智能PPT</van-button>
-        <van-button>英文PPT</van-button>
+        <div class="list">
+            <div class="item-box" @click="goNext('/ppt/index')">
+                <img src="@/assets/imgs/ppt/ppt_icon_zh.png" alt="">
+                <div>智能PPT</div>
+            </div>
+            <div class="item-box">
+                <img src="@/assets/imgs/ppt/ppt_icon_en.png" alt="">
+                <div>英文PPT</div>
+            </div>
+        </div>
+        
     </div>
 </template>
 
 <style lang="scss" scoped>
+.home-page{
+    padding: 30px 34px;
+    .list{
+        display: flex;
+        justify-content: space-between;
+        .item-box{
+            width: 326px;
+            height: 326px;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            font-size: 32px;
+            border: 1px solid $border-color;
+            border-radius: 32px;
+            filter: drop-shadow(3px 4px 12px rgba(0, 0, 0, 0.08));
+            img{
+                width: 140px;
+                height: 140px;
+                margin-bottom: 40px;
+            }
+
+        }
+    }
+}
 
+@media screen and (min-width:650px){
+    .home-page{
+        padding: 30px 34px;
+        .list{
+            justify-content: center;
+            .item-box{
+                width: 326px;
+                height: 326px;
+                margin: 0 20px;
+                font-size: 20px;
+                img{
+                    width: 120px;
+                    height: 120px;
+                    margin-bottom: 40px;
+                }
+            }
+        }
+    }
+}
 </style>

+ 19 - 7
src/views/tabbar/Index.vue

@@ -1,18 +1,21 @@
 <script setup>
 import {ref} from 'vue'
+import {getStaticImg} from '@/hooks/common.js'
 
 const tabbarList=ref([
     {
         name:"home",
-        icon:'home-o',
+        icon:getStaticImg('tabbar/home.png'),
+        iconActive:getStaticImg('tabbar/home-s.png'),
         text:'首页',
-        path:"/home"
+        path:"/tabbar/home"
     },
     {
         name:"user",
-        icon:'friends-o',
+        icon:getStaticImg('tabbar/user.png'),
+        iconActive:getStaticImg('tabbar/user-s.png'),
         text:'我的',
-        path:"/user"
+        path:"/tabbar/user"
     }
 ])
 const active = ref('home');
@@ -22,7 +25,7 @@ const active = ref('home');
 <template>
     <div class="tabbar-page">
         <router-view></router-view>
-        <van-tabbar v-model="active" z-index='99' route>
+        <van-tabbar class="tabbar-box" v-model="active" z-index='99' route>
             <van-tabbar-item 
                 :name="item.name" 
                 :icon="item.icon"
@@ -30,11 +33,20 @@ const active = ref('home');
                 replace
                 v-for="item in tabbarList"
                 :key="item.name"
-            >{{item.text}}</van-tabbar-item>
+            >
+                <span>{{item.text}}</span>
+                <template #icon="props">
+                    <img :src="props.active ? item.iconActive : item.icon" />
+                </template>
+            </van-tabbar-item>
         </van-tabbar>
     </div>
 </template>
 
 <style lang="scss" scoped>
-
+@media screen and (min-width:650px){
+    .tabbar-box{
+        display: none;
+    }
+}
 </style>

+ 81 - 2
src/views/tabbar/User.vue

@@ -1,13 +1,92 @@
 <script setup>
+import {Base64} from 'js-base64'
+import  {ref} from 'vue'
+import { useRouter } from 'vue-router'
+
+const router=useRouter()
+
+let userInfo=ref(null)
+function getUserInfo(){
+    const val=localStorage.getItem('userInfo')?JSON.parse(Base64.decode(localStorage.getItem('userInfo'))):null
+    userInfo.value=val
+    console.log(val);
+}
+getUserInfo()
+
+function handleLoginOut(){
+    localStorage.removeItem('token')
+    localStorage.removeItem('userInfo')
+    router.replace('/login')
+}
 
 </script>
 
 <template>
-    <div class="user-page">
-        useer
+    <div class="user-page" v-if="userInfo">
+        <div class="top-box">
+            <img class="avatar" src="@/assets/imgs/userLogo.png" alt="">
+            <div>
+                <div class="real-name">{{userInfo.RealName}}</div>
+                <div  class="admin-name">{{userInfo.AdminName}}</div>
+            </div>
+        </div>
+        <div class="info-list">
+            <div class="item">
+                <span>公司名称:</span>
+                <span>弘则弥道(上海)投资咨询有限公司</span>
+            </div>
+            <div class="item">
+                <span>所属部门:</span>
+                <span>产品技术部</span>
+            </div>
+            <div class="item">
+                <span>角色:</span>
+                <span>admin</span>
+            </div>
+        </div>
+        <van-button 
+            class="login_out_btn"
+            round 
+            block 
+            type="primary"
+            @click="handleLoginOut"
+        >退出登录</van-button>
     </div>
 </template>
 
 <style lang="scss" scoped>
+.top-box{
+    padding: 80px 40px;
+    display: flex;
+    align-items: center;
+    border-bottom: 10px solid #F2F6FA;
+    .avatar{
+        width: 136px;
+        height: 136px;
+        margin-right: 24px;
+    }
+    .real-name{
+        font-size: 32px;
+    }
+    .admin-name{
+        font-size: 32px;
+        color: $font-grey;
+        margin-top: 18px;
+    }
+}
 
+.info-list{
+    padding: 40px;
+    .item{
+        padding: 30px 0;
+        border-bottom: 1px solid $border-color;
+        color: $font-grey;
+    }
+}
+.login_out_btn{
+    width: 590px;
+    margin-left: auto;
+    margin-right: auto;
+    margin-top: 200px;
+}
 </style>

+ 32 - 21
vite.config.js

@@ -1,25 +1,36 @@
-import { defineConfig,loadEnv } from 'vite'
-import vue from '@vitejs/plugin-vue'
+import { defineConfig, loadEnv } from "vite";
+import vue from "@vitejs/plugin-vue";
 import path from "path";
-import Components from 'unplugin-vue-components/vite';
-import { VantResolver } from 'unplugin-vue-components/resolvers';
+import Components from "unplugin-vue-components/vite";
+import { VantResolver } from "unplugin-vue-components/resolvers";
 
 // https://vitejs.dev/config/
-export default ({mode})=> defineConfig({
-  base: loadEnv(mode,process.cwd()).VITE_APP_BASE_URL,// 若服务器不是将该项目放在根目录的则 需要此设置 和服务器上同名
-  plugins: [
-    vue(),
-    Components({
-      resolvers: [VantResolver()],
-    }),
-  ],
-  resolve: {
-    alias: {
-      "@": path.resolve(__dirname, "./src"),
+export default ({ mode }) =>
+  defineConfig({
+    base: loadEnv(mode, process.cwd()).VITE_APP_BASE_URL, // 若服务器不是将该项目放在根目录的则 需要此设置 和服务器上同名
+    plugins: [
+      vue(),
+      Components({
+        resolvers: [VantResolver()],
+      }),
+    ],
+    // 本文相关配置
+    css: {
+      // css预处理器
+      preprocessorOptions: {
+        scss: {
+          // 定义全局的scss变量
+          // 给导入的路径最后加上 ;
+          additionalData: `@import '@/assets/styles/var.scss';`,
+        },
+      },
     },
-  },
-  build:{
-    outDir:loadEnv(mode, process.cwd()).VITE_APP_OUTDIR
-  },
-})
-
+    resolve: {
+      alias: {
+        "@": path.resolve(__dirname, "./src"),
+      }
+    },
+    build: {
+      outDir: loadEnv(mode, process.cwd()).VITE_APP_OUTDIR,
+    },
+  });