vue3实战——后台管理系统

Vue3实战——会议室预约后台管理系统开发步骤笔记

1. 创建项目

使用Vite创建一个新的Vue 3项目,并选择Element Plus作为UI框架。

npm create vite@latest vue-manage-system-- --template vue
cd vue-manage-system

本项目是为校园老师预约会议室小程序所搭建的后台管理系统,所用技术栈如下

  • Element Plus:用于UI组件。
  • Vue Router:用于页面路由管理。
  • Pinia:作为状态管理库。
  • Axios:用于HTTP请求。
  • Vite插件:如unplugin-auto-importunplugin-vue-components用于自动导入组件和组件库。

2. 安装依赖

安装项目所需的依赖项。

npm install

3. 项目结构

建立合理的目录结构,方便后续开发和管理。

vue-manage-system
│   index.html
│
├───public
│
├───src
│   ├───assets
│   ├───components
│   ├───router
│   ├───store
│   ├───types
│   ├───views
│   ├───api
│   ├───utils
│   │   └───request.js
│   │
│   ├───App.vue
│   └───main.ts
│
└───package.json

4. 配置Vite

vite.config.ts中配置项目的基本属性,包括别名、代理、插件等。

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import VueSetupExtend from "vite-plugin-vue-setup-extend";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";

export default defineConfig({
  base: "/sadmin/",
  plugins: [
    vue(),
    VueSetupExtend(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
  server: {
    proxy: {
      "/api": {
        target: "https://ehuiyue.buteck.com/api",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },
  optimizeDeps: {
    include: ["schart.js"],
  },
  resolve: {
    alias: {
      "@": "/src",
      "~": "/src/assets",
    },
  },
  define: {
    __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "true",
  },
});

5. 路由配置

router/index.ts中定义应用的路由,包括登录、首页、用户管理、会议室管理等页面。

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
// 省略导入组件和store

const routes: RouteRecordRaw[] = [
  // 路由配置
];

const router = createRouter({
  history: createWebHashHistory('/sadmin'),
  routes
});

// 路由守卫
router.beforeEach((to, from, next) => {
  // 路由守卫逻辑
});

export default router;

6. 状态管理

使用Pinia作为状态管理库,在store/permiss.js中创建权限状态管理。

import { defineStore } from 'pinia';

export const usePermissStore = defineStore('permiss', {
  // 状态和函数定义
});

7. UI组件

components目录下创建自定义组件,如表格、对话框、搜索栏等,以提高代码复用性。

views目录下创建每个页面的视图文件,如Home.vueUserManagement.vue等,并使用Element Plus组件来构建用户界面。

8. API请求

api目录下定义API请求函数,用于与后端通信,获取数据和提交表单。

import axios from "axios";

export const fetchUserData = async (page, size, name) => {
  // 请求逻辑
};

9. 全局样式

assets/css目录下定义全局样式和主题,确保应用的一致性和美观。

10. 主入口

main.ts中初始化Vue应用,配置插件和路由。

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
// 其他导入

const app = createApp(App);
// 插件和路由配置
app.mount('#app');

11. 权限控制

创建自定义指令permiss,用于控制页面元素的显示,基于用户的权限。

app.directive('permiss', {
  mounted(el, binding) {
    // 指令逻辑
  },
});

12. 具体页面功能(例,会议室管理页)

<template>
  <div>
    <TableSearch :query="query" :options="searchOpt" :search="handleSearch" />
    <div class="container">
      <TableCustom
        :key="componentKey"
        :columns="columns"
        :tableData="tableData"
        :total="page.total"
        :currentPage="page.index"
        :viewFunc="handleView"
        :delFunc="handleDelete"
        :changePage="changePage"
        :editFunc="handleEdit"
      >
        <template #status="{ rows }">
          <el-tag type="success" v-if="rows.status">正常</el-tag>
          <el-tag type="danger" v-else>不可预约</el-tag>
        </template>
        <template #toolbarBtn>
          <el-button
            type="warning"
            :icon="CirclePlusFilled"
            @click="visible = true"
            >新增</el-button
          >
          <el-button type="danger" @click="visible2 = true">
            <el-icon style="margin-right: 5px"><DeleteFilled /></el-icon>
            删除会议室
          </el-button>
        </template>
      </TableCustom>
    </div>
    <el-dialog
      :title="isEdit ? '编辑' : '新增会议室'"
      v-model="visible"
      width="700px"
      destroy-on-close
      :close-on-click-modal="false"
      @close="closeDialog"
    >
      <TableEdit
        :form-data="rowData"
        :options="options"
        :edit="isEdit"
        :update="updateData"
      />
    </el-dialog>
    <el-dialog
      title="查看详情"
      v-model="visible1"
      width="700px"
      destroy-on-close
    >
      <TableDetail :data="viewData">
        <template #status="{ rows }">
          <el-tag type="success" v-if="rows.status">Active</el-tag>
          <el-tag type="danger" v-else>Disabled</el-tag>
        </template>
      </TableDetail>
    </el-dialog>
    <el-dialog
      title="删除会议室"
      v-model="visible2"
      width="700px"
      destroy-on-close
      :close-on-click-modal="false"
      @close="closeDialog"
    >
      <TableEdit
        :form-data="rowData"
        :options="optionss"
        :edits="visible2"
        :update="updateData"
      />
    </el-dialog>
  </div>
</template>

<script setup lang="ts" name="system-user">
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import { CirclePlusFilled } from "@element-plus/icons-vue";
import { User } from "@/types/user";
import { fetchUserData, DeleteData } from "@/api";
import TableCustom from "@/components/table-custom.vue";
import TableDetail from "@/components/table-detail.vue";
import TableSearch from "@/components/table-search.vue";
import { FormOption, FormOptionList } from "@/types/form-option";
import axios from "axios";
import { useRouter } from "vue-router";
const router = useRouter();
const goTologon = () => {
  // 使用 router.push 方法进行页面跳转
  router.push("/login");
};
console.log(TableSearch.props, "search");
const startTime = ref("");
const endTime = ref("");
// 查询相关
const query = reactive({
  name: "",
});
const searchOpt = ref<FormOptionList[]>([
  { type: "input", label: "会议室查询:", prop: "name" },
]);
const handleSearch = (queryData) => {
  console.log(queryData, "搜索");

  changePage(1, queryData.name, "");
};
// 表格相关
let columns = ref([
  { type: "index", label: "序号", width: 55, align: "center" },
  { prop: "roomName", label: "会议室名称", sortable: 'custom' },
  { prop: "capacity", label: "容纳人数" },
  { prop: "status", label: "状态" },
  { prop: "time", label: "时间" },
  { prop: "operator", label: "操作", width: 250 },
]);
const page = reactive({
  index: 1,
  size: 10,
  total: 0,
});
const componentKey = ref(0); // 强制刷新组件
const tableData = ref<User[]>([]);
const getData = async (e, n,p) => {
  const ress = await fetchUserData(e, n,p);
  if (ress == "Request failed with status code 403") {
    goTologon();
  }
  //console.log(ress, "shdfbkjdbgdfjk");
  tableData.value = ress.list;
  page.total = ress.total;

  componentKey.value++;
  console.log(ress, tableData.value, "tableData");
};
getData(1, "","");

const changePage = (val: number, name: string, p) => {
  page.index = val;
  getData(page.index, name,p);
};

// 新增/编辑弹窗相关
let options = ref<FormOption>({
  labelWidth: "100px",
  span: 12,
  list: [
    { type: "input", label: "会议室名称", prop: "roomName", required: true },
    { type: "input", label: "容纳人数", prop: "capacity", required: true },
  ],
});
let optionss = ref<FormOption>({
  labelWidth: "100px",
  span: 12,
  list: [
    { type: "input", label: "会议室名称", prop: "roomName", required: true },
  ],
});
const visible = ref(false);
const visible2 = ref(false);
const isEdit = ref(false);
const isEdits = ref(false);
const rowData = ref({});
const handleEdit = (row: User) => {
  rowData.value = { ...row };
  isEdit.value = true;
  visible.value = true;
  getData(1, "","");
};
const updateData = () => {
  closeDialog();
  //getData(2);
  setTimeout(() => {
    getData(1, "","");
  }, 500);
  //getData(1, "");
  console.log("更新数据");
};

const closeDialog = () => {
  visible.value = false;
  visible2.value = false;
  isEdit.value = false;
};

// 查看详情弹窗相关
const visible1 = ref(false);
const viewData = ref({
  row: {},
  list: [],
});
const handleView = (row: User) => {
  viewData.value.row = { ...row };
  viewData.value.list = [
    {
      prop: "roomName",
      label: "会议室名称",
    },
    {
      prop: "capacity",
      label: "容纳人数",
    },
    {
      prop: "status",
      label: "Status",
    },
    {
      prop: "time",
      label: "时间",
    },
  ];
  visible1.value = true;
};

// 删除相关
const handleDelete = async (row: User) => {
  console.log(row, "删除");
  const res = await DeleteData(row.id);
  if (res.data.message == "success") {
    ElMessage.success("删除成功");
  } else {
    ElMessage.error("删除失败");
  }
  getData(1, "","");
  page.index = 1;
};
</script>

<style scoped></style>

你可能感兴趣的:(笔记,vue.js,前端)