admin 发表于 2024-10-26 10:44:03

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

```
基于帝莎编程改写的

前端源码
## 文件夹结构
```
├── .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 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 || tabs
                  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(
页: [1]
查看完整版本: 简约易用的 Vue3 后台管理系统。