一元网络论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 54|回复: 0

简约易用的 Vue3 后台管理系统。

[复制链接]

3万

主题

3万

帖子

9万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
95194
发表于 2024-10-26 10:44:03 | 显示全部楼层 |阅读模式
```
基于帝莎编程改写的

前端源码
## 文件夹结构
```
├── .gitignore
├── .vscode/
├── README.md
├── index.html
├── node_modules/
├── package-lock.json
├── package.json
├── public/
├── src/
│   ├── App.vue
│   ├── api/
│   │   ├── index.js
│   │   ├── manager.js
│   │   └── user.js
│   ├── assets/
│   │   └── logo.png
│   ├── axios.js
│   ├── components/
│   │   ├── CountTo.vue
│   │   ├── FormDrawer.vue
│   │   ├── IndexCard.vue
│   │   ├── IndexChart.vue
│   │   └── IndexNavs.vue
│   ├── composables/
│   │   ├── auth.js
│   │   ├── useManager.js
│   │   ├── useTabList.js
│   │   └── util.js
│   ├── layouts/
│   │   ├── admin.vue
│   │   └── components/
│   │       ├── FHeader.vue
│   │       ├── FMenu.vue
│   │       └── FTagList.vue
│   ├── main.js
│   ├── pages/
│   │   ├── 404.vue
│   │   ├── category/
│   │   │   └── list.vue
│   │   ├── comment/
│   │   │   └── list.vue
│   │   ├── coupon/
│   │   │   └── list.vue
│   │   ├── goods/
│   │   │   └── list.vue
│   │   ├── image/
│   │   │   └── list.vue
│   │   ├── index.vue
│   │   ├── login.vue
│   │   ├── notice/
│   │   │   └── list.vue
│   │   ├── order/
│   │   │   └── list.vue
│   │   ├── setting/
│   │   │   └── base.vue
│   │   └── user/
│   │       └── list.vue
│   ├── permission.js
│   ├── router/
│   │   └── index.js
│   └── store/
│       └── index.js
├── vite.config.js
└── 课时50.店铺和交易提示组件开发和交互汇总源码.md
```
====================== 课时50.店铺和交易提示组件开发和交互\.gitignore ======================
## 课时50.店铺和交易提示组件开发和交互\.gitignore
```  # 内容未读取(排除)
```
====================== 课时50.店铺和交易提示组件开发和交互\index.html ======================
## 课时50.店铺和交易提示组件开发和交互\index.html
```html

  
   
   
   
    Vite App
  
  
   
   
  


```
====================== 课时50.店铺和交易提示组件开发和交互\package-lock.json ======================
## 课时50.店铺和交易提示组件开发和交互\package-lock.json
```  # 内容未读取(排除)
```
====================== 课时50.店铺和交易提示组件开发和交互\package.json ======================
## 课时50.店铺和交易提示组件开发和交互\package.json
```json
{
  "name": "shop-admin",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^1.1.4",
    "@vueuse/core": "^8.4.2",
    "@vueuse/integrations": "^8.4.1",
    "axios": "^0.27.2",
    "echarts": "^5.3.2",
    "element-plus": "^2.1.11",
    "gsap": "^3.10.4",
    "nprogress": "^0.2.0",
    "pinia": "^2.2.4",
    "universal-cookie": "^4.0.4",
    "vue": "^3.2.25",
    "vue-router": "^4.0.15"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^2.3.1",
    "mockjs": "^1.1.0",
    "vite": "^2.9.7",
    "vite-plugin-windicss": "^1.8.4",
    "windicss": "^3.5.1"
  }
}
```
====================== 课时50.店铺和交易提示组件开发和交互\README.md ======================
## 课时50.店铺和交易提示组件开发和交互\README.md
```md
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `

  

body{
  @apply bg-gray-100;
}
#nprogress .bar{
  background-color: #f4f4f4!important;
  height: 3px!important;
}

```
====================== 课时50.店铺和交易提示组件开发和交互\src\axios.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\axios.js
```js
import axios from "axios"
import { toast } from '~/composables/util'
import { getToken } from '~/composables/auth'
import { useUserStore } from '~/store' // 导入 Pinia store
const service = axios.create({
    baseURL: 'http://localhost:3000/api', // 指向后端服务器
    timeout: 8000,
  });
// 添加请求拦截器
service.interceptors.request.use(function (config) {
    // 往header头自动添加token
    const token = getToken()
    if (token) {
        config.headers["token"] = token
    }
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response.data.data;
}, function (error) {
    const msg = error.response.data.msg || "请求失败"
    if (msg == "非法token,请先登录!") {
        const userStore = useUserStore() // 使用 Pinia store
        userStore.logoutUser().finally(() => location.reload())
    }
    toast(msg, "error")
    return Promise.reject(error);
})
export default service
```
====================== 课时50.店铺和交易提示组件开发和交互\src\main.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\main.js
```js
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import { router } from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import { createPinia } from 'pinia'
// import './mock' // 同步引入
import 'virtual:windi.css'
import 'nprogress/nprogress.css'

const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.use(ElementPlus)
// 全局注册 Element Plus 图标组件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
// 确保在初始化 Pinia 和其他插件之后再导入权限控制
import "./permission"
app.mount('#app')
```
====================== 课时50.店铺和交易提示组件开发和交互\src\permission.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\permission.js
```js
import { router, addRoutes } from "~/router"
import { getToken } from "~/composables/auth"
import {
    toast,
    showFullLoading,
    hideFullLoading
} from "~/composables/util"
import { useUserStore } from '~/store'
let hasGetInfo = false
router.beforeEach(async (to, from, next) => {
    showFullLoading()
    const token = getToken()
    if (!token && to.path !== "/login") {
        toast("请先登录", "error")
        return next({ path: "/login" })
    }
    if (token && to.path === "/login") {
        toast("请勿重复登录", "error")
        return next({ path: from.path ? from.path : "/" })
    }
    const userStore = useUserStore()
    let hasNewRoutes = false
    if (token && !hasGetInfo) {
        let { menus } = await userStore.fetchUserInfo()
        hasGetInfo = true
        hasNewRoutes = addRoutes(menus)
    }
    let title = (to.meta.title ? to.meta.title : "")
    document.title = title
    hasNewRoutes ? next(to.fullPath) : next()
})
router.afterEach((to, from) => hideFullLoading())
```
====================== 课时50.店铺和交易提示组件开发和交互\src\api\index.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\api\index.js
```js
import axios from '~/axios'
export function getStatistics1(){
    return axios.get("/admin/statistics1")
}
export function getStatistics2(){
    return axios.get("/admin/statistics2")
}
export function getStatistics3(type){
    return axios.get("/admin/statistics3?type="+type)
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\api\manager.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\api\manager.js
```js
import axios from '~/axios'
export function login(username,password){
    return axios.post("/admin/login",{
        username,
        password
    })
}
export function getinfo(){
    return axios.post("/admin/getinfo")
}
export function logout(){
    return axios.post("/admin/logout")
}
export function updatepassword(data){
    return axios.post("/admin/updatepassword",data)
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\api\user.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\api\user.js
```js
// src/api/user.js
import axios from '~/axios';
export function uploadAvatar(data) {
  return axios.post('/admin/upload', data, {
    headers: {
      'Content-Type': 'multipart/form-data',
    },
  });
}
export function updateUserInfo(data) {
  return axios.post('/admin/updateuserinfo', data);
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\assets\logo.png ======================
## 课时50.店铺和交易提示组件开发和交互\src\assets\logo.png
```  # 内容未读取(排除)
```
====================== 课时50.店铺和交易提示组件开发和交互\src\components\CountTo.vue ======================
## 课时50.店铺和交易提示组件开发和交互\src\components\CountTo.vue
```vue
    {{ d.num.toFixed(0) }}

```
====================== 课时50.店铺和交易提示组件开发和交互\src\components\FormDrawer.vue ======================
## 课时50.店铺和交易提示组件开发和交互\src\components\FormDrawer.vue
```vue
   
        
            
               
            
            
                {{ confirmText }}
                取消
            
        
   

    .formDrawer{
        width: 100%;
        height: 100%;
        position: relative;
        @apply flex flex-col;
    }
    .formDrawer .body{
        flex: 1;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 50px;
        overflow-y: auto;
    }
    .formDrawer .actions{
        height: 50px;
        @apply mt-auto flex items-center;
    }
```
====================== 课时50.店铺和交易提示组件开发和交互\src\components\IndexCard.vue ======================
## 课时50.店铺和交易提示组件开发和交互\src\components\IndexCard.vue
```vue
   
        
            
                {{ title }}
               
                    {{ tip }}
               
            
        
        
            
               
                    
                        {{ item.value }}
                        {{ item.label }}
                    
               
            
        
        
   
   

```
====================== 课时50.店铺和交易提示组件开发和交互\src\components\IndexChart.vue ======================
## 课时50.店铺和交易提示组件开发和交互\src\components\IndexChart.vue
```vue
   
        
            
                订单统计
               
                    {{ item.text }}
               
            
        
        
   

```
====================== 课时50.店铺和交易提示组件开发和交互\src\components\IndexNavs.vue ======================
## 课时50.店铺和交易提示组件开发和交互\src\components\IndexNavs.vue
```vue
   
        
            
               
                    
                        
                    
                    {{ item.title }}
               
            
        
   

```
====================== 课时50.店铺和交易提示组件开发和交互\src\composables\auth.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\composables\auth.js
```js
import { useCookies } from '@vueuse/integrations/useCookies'
const TokenKey = "admin-token"
const cookie = useCookies()
// 获取token
export function getToken(){
    return cookie.get(TokenKey)
}
// 设置token
export function setToken(token){
    return cookie.set(TokenKey,token)
}
// 清除token
export function removeToken(){
    return cookie.remove(TokenKey)
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\composables\useManager.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\composables\useManager.js
```js
import { ref, reactive } from 'vue'
import { logout, updatepassword } from "~/api/manager"
import { showModal, toast } from "~/composables/util"
import { useRouter } from "vue-router"
import { useUserStore } from '~/store' // 导入 Pinia store
export function useRepassword() {
    const router = useRouter()
    const userStore = useUserStore() // 使用 Pinia store
    // 修改密码
    const formDrawerRef = ref(null)
    const form = reactive({
        oldpassword: "",
        password: "",
        repassword: ""
    })
    const rules = {
        oldpassword: [
            {
                required: true,
                message: '旧密码不能为空',
                trigger: 'blur'
            },
        ],
        password: [
            {
                required: true,
                message: '新密码不能为空',
                trigger: 'blur'
            },
        ],
        repassword: [
            {
                required: true,
                message: '确认密码不能为空',
                trigger: 'blur'
            },
        ]
    }
    const formRef = ref(null)
    const onSubmit = () => {
        formRef.value.validate((valid) => {
            if (!valid) {
                return false
            }
            formDrawerRef.value.showLoading()
            updatepassword(form)
                .then(res => {
                    toast("修改密码成功,请重新登录")
                    userStore.logoutUser() // 使用 Pinia store 的 action
                    // 跳转回登录页
                    router.push("/login")
                })
                .finally(() => {
                    formDrawerRef.value.hideLoading()
                })
        })
    }
    const openRePasswordForm = () => formDrawerRef.value.open()
    return {
        formDrawerRef,
        form,
        rules,
        formRef,
        onSubmit,
        openRePasswordForm
    }
}
export function useLogout() {
    const router = useRouter()
    const userStore = useUserStore() // 使用 Pinia store
    function handleLogout() {
        showModal("是否要退出登录?").then(res => {
            logout().finally(() => {
                userStore.logoutUser() // 使用 Pinia store 的 action
                // 跳转回登录页
                router.push("/login")
                // 提示退出登录成功
                toast("退出登录成功")
            })
        })
    }
    return {
        handleLogout
    }
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\composables\useTabList.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\composables\useTabList.js
```js
import { ref } from 'vue'
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { useCookies } from '@vueuse/integrations/useCookies'
import { router } from '~/router';
export function useTabList() {
    const route = useRoute()
    const cookie = useCookies()
    const activeTab = ref(route.path)
    const tabList = ref([
        {
            title: '后台首页',
            path: "/"
        },
    ])
    // 添加标签导航
    function addTab(tab) {
        let noTab = tabList.value.findIndex(t => t.path == tab.path) == -1
        if (noTab) {
            tabList.value.push(tab)
        }
        cookie.set("tabList", tabList.value)
    }
    // 初始化标签导航列表
    function initTabList() {
        let tbs = cookie.get("tabList")
        if (tbs) {
            tabList.value = tbs
        }
    }
    initTabList()
    onBeforeRouteUpdate((to, from) => {
        activeTab.value = to.path
        addTab({
            title: to.meta.title,
            path: to.path
        })
    })
    const changeTab = (t) => {
        console.log(route.path);
        activeTab.value = t
        router.push(t)
    }
    const removeTab = (t) => {
        let tabs = tabList.value
        let a = activeTab.value
        if (a == t) {
            tabs.forEach((tab, index) => {
                if (tab.path == t) {
                    const nextTab = tabs[index + 1] || tabs[index - 1]
                    if (nextTab) {
                        a = nextTab.path
                    }
                }
            })
        }
        activeTab.value = a
        tabList.value = tabList.value.filter(tab => tab.path != t)
        cookie.set("tabList", tabList.value)
    }
    const handleClose = (c) => {
        if (c == "clearAll") {
            // 切换回首页
            activeTab.value = "/"
            // 过滤只剩下首页
            tabList.value = [{
                title: '后台首页',
                path: "/"
            }]
        } else if (c == "clearOther") {
            // 过滤只剩下首页和当前激活
            tabList.value = tabList.value.filter(tab => tab.path == "/" || tab.path == activeTab.value)
        }
        cookie.set("tabList", tabList.value)
    }
    return {
        activeTab,
        tabList,
        changeTab,
        removeTab,
        handleClose
    }
}
```
====================== 课时50.店铺和交易提示组件开发和交互\src\composables\util.js ======================
## 课时50.店铺和交易提示组件开发和交互\src\composables\util.js
```js
import { ElNotification,ElMessageBox } from 'element-plus'
import nprogress from 'nprogress'
// 消息提示
export function toast(message,type = "success",dangerouslyUseHTMLString = false){
    ElNotification({
        message,
        type,
        dangerouslyUseHTMLString,
        duration:3000
    })
}
// 显示全屏loading
export function showFullLoading(){
  nprogress.start()
}
// 隐藏全屏loading
export function hideFullLoading(){
  nprogress.done()
}
export function showModal(content = "提示内容",type = "warning",title = ""){
    return ElMessageBox.confirm(
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一元网络论坛

GMT+8, 2024-11-24 19:10 , Processed in 0.104196 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表