使用Vite创建一个新的Vue 3项目,并选择Element Plus作为UI框架。
npm create vite@latest vue-manage-system-- --template vue
cd vue-manage-system
本项目是为校园老师预约会议室小程序所搭建的后台管理系统,所用技术栈如下
unplugin-auto-import
和unplugin-vue-components
用于自动导入组件和组件库。安装项目所需的依赖项。
npm install
建立合理的目录结构,方便后续开发和管理。
vue-manage-system
│ index.html
│
├───public
│
├───src
│ ├───assets
│ ├───components
│ ├───router
│ ├───store
│ ├───types
│ ├───views
│ ├───api
│ ├───utils
│ │ └───request.js
│ │
│ ├───App.vue
│ └───main.ts
│
└───package.json
在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",
},
});
在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;
使用Pinia作为状态管理库,在store/permiss.js
中创建权限状态管理。
import { defineStore } from 'pinia';
export const usePermissStore = defineStore('permiss', {
// 状态和函数定义
});
在components
目录下创建自定义组件,如表格、对话框、搜索栏等,以提高代码复用性。
在views
目录下创建每个页面的视图文件,如Home.vue
、UserManagement.vue
等,并使用Element Plus组件来构建用户界面。
在api
目录下定义API请求函数,用于与后端通信,获取数据和提交表单。
import axios from "axios";
export const fetchUserData = async (page, size, name) => {
// 请求逻辑
};
在assets/css
目录下定义全局样式和主题,确保应用的一致性和美观。
在main.ts
中初始化Vue应用,配置插件和路由。
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
// 其他导入
const app = createApp(App);
// 插件和路由配置
app.mount('#app');
创建自定义指令permiss
,用于控制页面元素的显示,基于用户的权限。
app.directive('permiss', {
mounted(el, binding) {
// 指令逻辑
},
});
<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>