JP3-4-MyClub后台前端(四)

发布于:2025-08-15 ⋅ 阅读:(10) ⋅ 点赞:(0)

Java道经 - 项目 - MyClub - 后台前端(四)


传送门:JP3-4-MyClub后台前端(一)
传送门:JP3-4-MyClub后台前端(二)
传送门:JP3-4-MyClub后台前端(三)
传送门:JP3-4-MyClub后台前端(四)
传送门:JP3-4-MyClub后台前端(五)

S04. UMS用户管理

E01. UMS部门模块

武技:在 router/index.js 文件中开发全部相关页面路由配置

import Dept from '../views/ums/dept/Dept.vue';
import DeptInsert from '../views/ums/dept/DeptInsert.vue';
import DeptUpdate from '../views/ums/dept/DeptUpdate.vue';

const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {path: '/', name: 'Login', component: Login},
        {
            path: '/Main', name: 'Main', component: Main,
            redirect: '/Dashboard',
            children: [
                {path: '/Dept', name: 'Dept', component: Dept},
                {path: '/DeptInsert', name: 'DeptInsert', component: DeptInsert},
                {path: '/DeptUpdate', name: 'DeptUpdate', component: DeptUpdate},
            ]
        }
    ]
});

1. 部门列表

心法:部门列表页面

在这里插入图片描述

武技:开发部门列表页面 views/ums/dept/Dept.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";

// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'HomeFilled', label: '部门列表'},
];
// 数据头
const headItems = [
  {type: 'ipt', span: 5, placeholder: '搜索部门名', callback: pageByTitle},
];
// 表格列
const columns = [
  {label: '部门名称', prop: 'title'},
  {label: '所在房间', prop: 'room.title'},
  {label: '部门描述', prop: 'info', type: 'card', width: 520},
];

/* ==================== 分页查询 ==================== */

// 表格数据 + 分页数据 + 部门名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let deptTitle = ref();

async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
  // 分页参数
  let config = {
    api: pageApi,
    args: {module: 'dept'},
    params: {pageNum, pageSize},
    records, pageInfo
  };
  // 若房间名不为空,则附加为分页条件
  if (deptTitle.value) config['params']['title'] = deptTitle.value;
  // 发送分页请求
  await myPage(config);
}

/* ==================== 搜索部门名 ==================== */

function pageByTitle(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || deptTitle.value) {
    deptTitle.value = val;
    page();
  }
}

/* ==================== 删除成功回调 ==================== */

function deleteSuccess() {
  ElMessage.success('删除成功');
  page();
}

/* ==================== 报表打印 ==================== */

function downloadExcel() {
  excelApi('/dept/excel', '部门报表');
}

/* ==================== 加载函数 ==================== */

onMounted(() => page());
</script>

<template>
  <my-nav :items="navItems"/>
  <my-head :items="headItems"/>
  <my-table module="dept"
            insert-page="/DeptInsert"
            update-page="/DeptUpdate"
            :records="records"
            :columns="columns"
            :delete-api="deleteApi"
            :delete-batch-api="deleteBatchApi"
            :delete-callback="deleteSuccess"
            :excel-api="downloadExcel"
            :pageInfo="pageInfo"/>
</template>

<style scoped lang="scss"></style>

2. 添加部门

心法:添加部门页面

在这里插入图片描述

武技:开发添加部门页面

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {insertApi, listApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 所在房间下拉菜单选项
let roomOptions = ref([]);
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'HomeFilled', label: '部门列表', url: '/Dept'},
  {icon: 'Plus', label: '添加新部门'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '部门名称', prop: 'title', required: true, span: 12},
  {span: 12, hidden: true},
  {label: '所在房间', prop: 'fkRoomId', required: true, type: 'select', options: roomOptions.value, span: 12},
  {label: '部门描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({});
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 添加成功后 ==================== */

function insertSuccess() {
  ElMessage.success('添加记录成功!');
  setTimeout(() => router.push('/Dept'), 1000);
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  // 查询全部房间并添加到下拉菜单选项中
  Object.values(getResponseData(await listApi({module: 'room'}))).forEach(room => {
    roomOptions.value.push({label: room['title'], value: room['id']});
  });
});

</script>

<template>
  <my-nav :items="navItems"/>
  <el-card v-if="roomOptions.length > 0" class="dept-insert-card" header="添加新部门">
    <my-form type="insert"
             :items="items"
             :params="params"
             :rules="rules"
             :api="insertApi"
             :args="{module: 'dept'}"
             :callback="insertSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.dept-insert-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

3. 修改部门

心法:修改部门页面

在这里插入图片描述

武技:开发修改部门页面 views/ums/dept/DeptUpdate.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {listApi, updateApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import {getResponseData} from "../../../request/index.js";
import router from "../../../router";

// 部门记录
let dept = JSON.parse(sessionStorage.getItem('row'));
// 所在房间下拉菜单选项
let roomOptions = ref([]);
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'HomeFilled', label: '部门列表', url: '/Dept'},
  {icon: 'Edit', label: '修改部门'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '部门名称', prop: 'title', required: true, span: 12},
  {span: 12, hidden: true},
  {label: '所在房间', prop: 'fkRoomId', required: true, type: 'select', options: roomOptions.value, span: 12},
  {label: '部门描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(dept);
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 修改成功后 ==================== */

function updateSuccess() {
  ElMessage.success('修改记录成功!');
  setTimeout(() => router.push('/Dept'), 1000);
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  // 查询全部房间并添加到下拉菜单选项中
  Object.values(getResponseData(await listApi({module: 'room'}))).forEach(room => {
    roomOptions.value.push({label: room['title'], value: room['id']});
  });
});

</script>

<template>
  <my-nav :items="navItems"/>
  <el-card v-if="roomOptions.length > 0" class="dept-update-card" header="修改部门信息">
    <my-form type="update"
             :items="items"
             :params="params"
             :rules="rules"
             :api="updateApi"
             :args="{module: 'dept'}"
             :callback="updateSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.dept-update-card{
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

E02. UMS员工模块

武技:在 router/index.js 文件中开发全部相关页面路由配置

import Emp from '../views/ums/emp/Emp.vue';
import EmpInsert from '../views/ums/emp/EmpInsert.vue';
import EmpUpdate from '../views/ums/emp/EmpUpdate.vue';
import EmpUpdateRoles from "../views/ums/emp/EmpUpdateRoles.vue";

const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {path: '/', name: 'Login', component: Login},
        {
            path: '/Main', name: 'Main', component: Main,
            redirect: '/Dashboard',
            children: [
                {path: '/Emp', name: 'Emp', component: Emp},
                {path: '/EmpInsert', name: 'EmpInsert', component: EmpInsert},
                {path: '/EmpUpdate', name: 'EmpUpdate', component: EmpUpdate},
                {path: '/EmpUpdateRoles ', name: 'EmpUpdateRoles ', component: EmpUpdateRoles },
            ]
        }
    ]
});

1. 员工列表

心法:员工列表页面

在这里插入图片描述

武技:开发员工列表页面 views/ums/emp/Emp.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {getResponseData, myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, listApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import {dateFormat, genderFormat} from "../../../util/index.js";
import {PROJECT_INFO} from "../../../const/index.js";
import router from "../../../router/index.js";

// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'User', label: '员工列表'}
];
// 数据头
const headItems = [
  {type: 'ipt', span: 5, placeholder: '搜索登录账号', callback: pageByUsername},
  {type: 'ipt', span: 5, placeholder: '搜索手机号码', callback: pageByPhone},
  {type: 'ipt', span: 5, placeholder: '搜索身份证号', callback: pageByIdcard},
  {type: 'ipt', span: 5, placeholder: '搜索真实姓名', callback: pageByRealname},
  {type: 'opt', span: 4, placeholder: '搜索部门', callback: pageByDeptId, options: deptOptions.value},
];
// 表格列
const columns = [
  {label: '员工头像', prop: 'avatar', type: 'img', minio: minio},
  {label: '真实姓名', prop: 'realname', width: 90},
  {label: '所在部门', prop: 'dept.title', width: 130},
  {label: '员工性别', prop: 'gender', type: 'tag', tagTypeFn: e => e === 0 ? 'danger' : e === 1 ? 'primary' : 'default', format: genderFormat, width: 90},
  {label: '员工描述', prop: 'info', type: 'card'},
  {label: '登录账号', prop: 'username', width: 130},
  {label: '手机号码', prop: 'phone', width: 90},
  {label: '身份证号', prop: 'idcard', width: 140},
  {label: '微信号码', prop: 'wechat'},
  {label: '电子邮箱', prop: 'email'},
  {label: '员工年龄', prop: 'age', width: 90},
  {label: '所在省份', prop: 'province', width: 90},
  {label: '现居住地', prop: 'address', type: 'card'},
  {label: '入职日期', prop: 'hiredate', type: 'date', format: dateFormat, width: 110},
];
// 按钮列
const buttons = [
  {label: '重设角色', icon: 'Edit', callback: toEmpUpdateRoles},
];

/* ==================== 重设角色 ==================== */

function toEmpUpdateRoles(row) {
  router.push({
    path: '/EmpUpdateRoles', query: {
      empId: row['id'],
      realname: row['realname']
    }
  });
}

/* ==================== 员工头像 ==================== */

function minio(src) {
  return PROJECT_INFO.minioHost + '/avatar/' + src;
}

/* ==================== 分页查询 ==================== */

// 表格数据 + 分页数据 + 登录账号 + 手机号码 + 身份证号 + 真实姓名 + 所在部门ID
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let username = ref();
let phone = ref();
let idcard = ref();
let realname = ref();
let fkDeptId = ref();

async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
  // 分页参数
  let config = {
    api: pageApi,
    args: {module: 'emp'},
    params: {pageNum, pageSize},
    records, pageInfo
  };
  // 若登录账号不为空,则附加为分页条件
  if (username.value) config['params']['username'] = username.value;
  // 若手机号码不为空,则附加为分页条件
  if (phone.value) config['params']['phone'] = phone.value;
  // 若身份证号不为空,则附加为分页条件
  if (idcard.value) config['params']['idcard'] = idcard.value;
  // 若真实姓名不为空,则附加为分页条件
  if (realname.value) config['params']['realname'] = realname.value;
  // 若所在部门ID不为空,则附加为分页条件
  if (fkDeptId.value) config['params']['fkDeptId'] = fkDeptId.value;
  // 发送分页请求
  await myPage(config);
}

/* ==================== 搜索登录账号 ==================== */

function pageByUsername(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || username.value) {
    username.value = val;
    page();
  }
}

/* ==================== 搜索手机号码 ==================== */

function pageByPhone(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || phone.value) {
    phone.value = val;
    page();
  }
}

/* ==================== 搜索身份证号 ==================== */

function pageByIdcard(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || idcard.value) {
    idcard.value = val;
    page();
  }
}

/* ==================== 搜索真实姓名 ==================== */

function pageByRealname(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || realname.value) {
    realname.value = val;
    page();
  }
}

/* ==================== 搜索所在部门 ==================== */

function pageByDeptId(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || fkDeptId.value) {
    fkDeptId.value = val;
    page();
  }
}

/* ==================== 删除成功回调 ==================== */

function deleteSuccess() {
  ElMessage.success('删除成功!');
  page();
}

/* ==================== 报表打印 ==================== */

function downloadExcel() {
  excelApi('/emp/excel', '员工报表');
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  await page();
  // 查询全部部门并添加到下拉菜单选项中
  Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
    deptOptions.value.push({label: dept['title'], value: dept['id']});
  });
});
</script>

<template>
  <my-nav :items="navItems"/>
  <my-head :items="headItems" v-if="deptOptions.length > 0"/>
  <my-table module="emp"
            insert-page="/EmpInsert"
            update-page="/EmpUpdate"
            :records="records"
            :columns="columns"
            :buttons="buttons"
            :delete-api="deleteApi"
            :delete-batch-api="deleteBatchApi"
            :delete-callback="deleteSuccess"
            :excel-api="downloadExcel"
            :pageInfo="pageInfo"/>
</template>

<style scoped lang="scss"></style>

2. 添加员工

心法:添加员工页面

在这里插入图片描述

武技:开发添加员工页面 views/ums/emp/EmpInsert.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {onMounted, reactive, ref} from "vue";
import {insertApi, listApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {DEFAULT_PASSWORD, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'User', label: '员工列表', url: '/Emp'},
  {icon: 'Plus', label: '添加新员工'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '登录账号', prop: 'username', required: true, span: 12},
  {label: '真实姓名', prop: 'realname', required: true, span: 12},
  {label: '手机号码', prop: 'phone', required: true, span: 12},
  {label: '身份证号', prop: 'idcard', required: true, span: 12},
  {label: '微信号码', prop: 'wechat', required: true, span: 12},
  {label: '电子邮箱', prop: 'email', required: true, span: 12},
  {label: '所在部门', prop: 'fkDeptId', required: true, type: 'select', options: deptOptions.value, span: 12},
  {label: '入职日期', prop: 'hiredate', required: true, type: 'datetime', span: 12},
  {label: '现居住地', prop: 'address', required: true, type: 'textarea', rows: 2},
  {label: '员工描述', prop: 'info', type: 'textarea', rows: 7},
]);
let params = reactive({password: DEFAULT_PASSWORD});
let rules = {
  realname: RULE.REALNAME,
  phone: RULE.PHONE,
  email: RULE.EMAIL,
  address: RULE.ADDRESS,
  idcard: RULE.IDCARD,
  info: RULE.INFO,
};

/* ==================== 添加成功后 ==================== */

function insertSuccess() {
  ElMessage.success('添加成功!');
  setTimeout(() => router.push('/Emp'), 1000);
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  // 查询全部部门并添加到下拉菜单选项中
  Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
    deptOptions.value.push({label: dept['title'], value: dept['id']});
  });
});
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="emp-insert-card" header="添加新员工">
    <my-form v-if="deptOptions.length > 0"
             type="insert"
             :items="items"
             :params="params"
             :rules="rules"
             :api="insertApi"
             :args="{module: 'emp'}"
             :callback="insertSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.emp-insert-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

3. 修改员工

心法:修改员工页面

在这里插入图片描述

武技:开发修改员工页面 views/ums/emp/EmpUpdate.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import MyUpload from "../../../components/MyUpload.vue";
import {onMounted, reactive, ref} from "vue";
import {listApi, updateApi} from "../../../api/axios.js";
import {getResponseData} from "../../../request/index.js";
import {GENDER_OPTIONS, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import {UPLOAD_AVATAR_URL} from "../../../api/emp.js";
import router from "../../../router";

// 员工记录
let emp = JSON.parse(sessionStorage.getItem('row'));
// 所在部门下拉菜单选项
let deptOptions = ref([]);
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'User', label: '员工列表', url: '/Emp'},
  {icon: 'Edit', label: '修改员工'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '登录账号', prop: 'username', disabled: true, span: 12},
  {label: '真实姓名', prop: 'realname', required: true, span: 12},
  {label: '手机号码', prop: 'phone', required: true, span: 12},
  {label: '身份证号', prop: 'idcard', required: true, span: 12},
  {label: '微信号码', prop: 'wechat', required: true, span: 12},
  {label: '电子邮箱', prop: 'email', required: true, span: 12},
  {label: '员工性别', prop: 'gender', required: true, type: 'select', options: GENDER_OPTIONS, span: 12},
  {label: '员工年龄', prop: 'age', required: true, type: 'number', span: 12},
  {label: '所在部门', prop: 'fkDeptId', required: true, type: 'select', options: deptOptions.value, span: 12},
  {label: '入职日期', prop: 'hiredate', required: true, type: 'datetime', span: 12},
  {label: '现居住地', prop: 'address', required: true, type: 'textarea', rows: 2},
  {label: '员工描述', prop: 'info', type: 'textarea', rows: 7},
]);
let params = reactive(emp);
let rules = {
  realname: RULE.REALNAME,
  phone: RULE.PHONE,
  email: RULE.EMAIL,
  address: RULE.ADDRESS,
  idcard: RULE.IDCARD,
  info: RULE.INFO,
};

/* ==================== 修改成功后 ==================== */

function updateSuccess() {
  ElMessage.success('修改记录成功!');
  setTimeout(() => router.push('/Emp'), 1000);
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  // 查询全部部门并添加到下拉菜单选项中
  Object.values(getResponseData(await listApi({module: 'dept'}))).forEach(dept => {
    deptOptions.value.push({label: dept['title'], value: dept['id']});
  });
});
</script>

<template>
  <my-nav :items="navItems"/>
  <el-row :gutter="30" class="emp-update-row">
    <el-col :span="16">
      <el-card class="emp-update-card" header="修改基本信息">
        <my-form v-if="deptOptions.length > 0"
                 type="update"
                 :items="items"
                 :params="params"
                 :rules="rules"
                 :api="updateApi"
                 :args="{module: 'emp'}"
                 :callback="updateSuccess"/>
      </el-card>
    </el-col>
    <el-col :span="8">
      <el-card header="上传员工头像">
        <my-upload :url="UPLOAD_AVATAR_URL + '/' + emp['id']"
                   name="avatarFile"
                   :callback="updateSuccess"
                   :autoUpload="true"/>
      </el-card>
    </el-col>
  </el-row>
</template>

<style scoped lang="scss">
.emp-update-row {
  margin: 65px auto 0; // 外边距
}
</style>

4. 重设角色

心法:重设员工角色页面

在这里插入图片描述

武技:开发重设员工角色页面 views/ums/emp/EmpUpdateRoles.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import {getResponseData} from "../../../request";
import {onMounted, ref} from "vue";
import {listApi} from "../../../api/axios.js";
import {listByEmpIdApi, updateByEmpIdApi} from "../../../api/role.js";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 获取路由中的员工主键和真实姓名
let empId = router.currentRoute.value.query['empId'];
let realname = router.currentRoute.value.query['realname'];
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'User', label: '员工列表', url: '/Emp'},
  {icon: 'Edit', label: '为员工重设角色'},
];
// 全部角色 + 我的角色主键数组
let allRoles = ref([]);
let roleIds = ref([]);

/* ==================== 确认修改角色 ==================== */

async function updateRoles() {
  let data = getResponseData(await updateByEmpIdApi(empId, roleIds.value));
  if (data) {
    ElMessage.success('角色重设成功,下次登录生效!');
  }
}

/* ==================== 加载函数 ==================== */


onMounted(async () => {
  // 查询全部角色
  Object.values(getResponseData(await listApi({module: 'role'}))).forEach(role => {
    allRoles.value.push({label: role['title'], key: role['id']});
  });

  // 查询该员工的角色
  Object.values(getResponseData(await listByEmpIdApi(empId))).forEach(role => {
    roleIds.value.push(role['id']);
  });
});

</script>

<template>
  <div class="emp-roles-body">
    <my-nav :items="navItems"/>
    <el-transfer class="emp-roles-transfer"
                 filterable filter-placeholder="输入关键字"
                 v-if="allRoles.length > 0"
                 v-model="roleIds"
                 :data="allRoles"
                 :titles="['全部可选角色', '【' + realname+ '】已选角色']"
                 :props="{key: 'key', label: 'label'}"
                 :button-texts="['移除', '添加']">
      <template #left-footer>
        <el-text class="mx-1" type="info">tips: 请重新选择该员工的角色!</el-text>
      </template>
      <template #right-footer>
        <el-button type="primary" @click="updateRoles" size="small">确认修改</el-button>
      </template>
    </el-transfer>
  </div>
</template>

<style scoped lang="scss">
.emp-roles-body {
  text-align: center; // 内容居中

  .emp-roles-transfer {
    margin-top: 65px; // 上外边距
  }
}

:deep(.el-transfer-panel) {
  width: 300px; // 宽度
}

:deep(.el-transfer-panel__body) {
  height: 400px; // 高度
}

:deep(.el-transfer-panel__footer) {
  text-align: center; // 内容居中
}
</style>

E03. UMS角色模块

武技:在 router/index.js 文件中开发全部相关页面路由配置

import Role from '../views/ums/role/Role.vue';
import RoleInsert from '../views/ums/role/RoleInsert.vue';
import RoleUpdate from '../views/ums/role/RoleUpdate.vue';
import RoleUpdateMenus from '../views/ums/role/RoleUpdateMenus.vue';

const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {path: '/', name: 'Login', component: Login},
        {
            path: '/Main', name: 'Main', component: Main,
            redirect: '/Dashboard',
            children: [
                {path: '/Role', name: 'Role', component: Role},
                {path: '/RoleInsert', name: 'RoleInsert', component: RoleInsert},
                {path: '/RoleUpdate', name: 'RoleUpdate', component: RoleUpdate},
                {path: '/RoleUpdateMenus', name: 'RoleUpdateMenus', component: RoleUpdateMenus},
            ]
        }
    ]
});

1. 角色列表

心法:角色列表页面

在这里插入图片描述

武技:开发角色列表页面 views/ums/role/Role.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../router/index.js";

// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'UserFilled', label: '角色列表'}
];
// 数据头
const headItems = [
  {type: 'ipt', span: 5, placeholder: '搜索角色标题', callback: pageByTitle},
];
// 表格列
const columns = [
  {label: '角色标题', prop: 'title'},
  {label: '角色描述', prop: 'info', type: 'card', width: 520},
];
// 按钮列
const buttons = [
  {label: '重设菜单', icon: 'Edit', callback: toRoleUpdateMenus},
];

/* ==================== 重设菜单 ==================== */

function toRoleUpdateMenus(row) {
  router.push({
    path: '/RoleUpdateMenus', query: {
      roleId: row['id'],
      roleTitle: row['title']
    }
  });
}

/* ==================== 分页查询 ==================== */

// 表格数据 + 分页数据 + 角色标题
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let roleTitle = ref();

async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
  // 分页参数
  let config = {
    api: pageApi,
    args: {module: 'role'},
    params: {pageNum, pageSize},
    records, pageInfo
  };
  // 若角色标题不为空,则附加为分页条件
  if (roleTitle.value) config['params']['title'] = roleTitle.value;
  // 发送分页请求
  await myPage(config);
}

/* ==================== 搜索角色标题 ==================== */

function pageByTitle(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || roleTitle.value) {
    roleTitle.value = val;
    page();
  }
}

/* ==================== 删除成功回调 ==================== */

function deleteSuccess() {
  ElMessage.success('删除成功!');
  page();
}

/* ==================== 报表打印 ==================== */

function downloadExcel() {
  excelApi('/role/excel', '角色报表');
}

/* ==================== 加载函数 ==================== */

onMounted(() => page());
</script>

<template>
  <my-nav :items="navItems"/>
  <my-head :items="headItems"/>
  <my-table module="role"
            insert-page="/RoleInsert"
            update-page="/RoleUpdate"
            :records="records"
            :columns="columns"
            :buttons="buttons"
            :delete-api="deleteApi"
            :delete-batch-api="deleteBatchApi"
            :delete-callback="deleteSuccess"
            :excel-api="downloadExcel"
            :pageInfo="pageInfo"/>
</template>

<style scoped lang="scss"></style>

2. 添加角色

心法:添加角色页面

在这里插入图片描述

武技:开发添加角色页面 views/ums/role/RoleInsert.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../api/axios.js";
import {DEFAULT_PASSWORD, RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'UserFilled', label: '角色列表', url: '/Role'},
  {icon: 'Plus', label: '添加新角色'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '角色标题', prop: 'title', required: true, span: 12},
  {hidden: true, span: 12},
  {label: '角色描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({password: DEFAULT_PASSWORD});
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 添加成功后 ==================== */

function insertSuccess() {
  ElMessage.success('添加成功!');
  setTimeout(() => router.push('/Role'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="role-insert-card" header="添加新角色">
    <my-form type="insert"
             :items="items"
             :params="params"
             :rules="rules"
             :api="insertApi"
             :args="{module: 'role'}"
             :callback="insertSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.role-insert-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

3. 修改角色

心法:修改角色页面

在这里插入图片描述

武技:开发修改角色页面 views/ums/role/RoleUpdate.vue

<script setup>  
import MyForm from "../../../components/MyForm.vue";  
import MyNav from "../../../components/MyNav.vue";  
import {reactive, ref} from "vue";  
import {updateApi} from "../../../api/axios.js";  
import {RULE} from "../../../const";  
import {ElMessage} from "element-plus";  
import router from "../../../router";

// 角色记录
let role = JSON.parse(sessionStorage.getItem('row'));
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'UserFilled', label: '角色列表', url: '/Role'},
  {icon: 'Edit', label: '修改角色'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '角色标题', prop: 'title', required: true, span: 12},
  {hidden: true, span: 12},
  {label: '角色描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(role);
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 修改成功后 ==================== */

function updateSuccess() {
  ElMessage.success('修改记录成功!');
  setTimeout(() => router.push('/Role'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="role-update-card" header="修改角色信息">
    <my-form type="update"
             :items="items"
             :params="params"
             :rules="rules"
             :api="updateApi"
             :args="{module: 'role'}"
             :callback="updateSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.role-update-card{
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

4. 重设菜单

心法:重设菜单页面

在这里插入图片描述

武技:开发重设菜单页面 views/ums/role/RoleUpdateMenus.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import {getResponseData} from "../../../request";
import {onMounted, ref} from "vue";
import {listApi} from "../../../api/axios.js";
import {listByRoleIdApi, updateByRoleIdApi} from "../../../api/menu.js";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 获取路由中的角色主键和角色标题
let roleId = router.currentRoute.value.query['roleId'];
let roleTitle = router.currentRoute.value.query['roleTitle'];
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'UserFilled', label: '角色列表', url: '/Role'},
  {icon: 'Edit', label: '为角色重设菜单'},
];
// 全部菜单 + 我的菜单主键数组
let allMenus = ref([]);
let menuIds = ref([]);
// 菜单的父子关系
let idToPidMap = {};

/* ==================== 确认修改菜单 ==================== */

/**
 * 处理选中的菜单ID列表
 *
 * 1. 遍历处理选中的菜单ID列表。
 * 2. 将选中的菜单ID追加到临时数组 result 中。
 * 3. 根据菜单的父子关系 idToPidMap 对象获取该菜单的父菜单ID。
 * 4. 若临时数组 result 中已存在相同的父菜单ID,则不会重复追加。
 * 5. 若临时数组 result 中不存在相同的父菜单ID,则追加到临时数组 result 中。
 *
 * @param menuIds 选中的菜单列表
 * @return result 处理后的菜单ID列表,包括全部子菜单的ID和对应父菜单的ID,不存在重复项。
 */
function buildMenuIds(menuIds) {
  let result = [];
  for (let i in menuIds) {
    result.push(menuIds[i]);
    let parentMenuId = idToPidMap[menuIds[i]];
    if (result.indexOf(parentMenuId) === -1) {
      result.push(parentMenuId);
    }
  }
  return result;
}

async function updateMenus() {
  let data = getResponseData(await updateByRoleIdApi(roleId, buildMenuIds(menuIds.value)));
  if (data) {
    ElMessage.success('菜单重设成功,下次登录生效!');
  }
}

/* ==================== 加载函数 ==================== */

onMounted(async () => {
  // 查询全部菜单
  Object.values(getResponseData(await listApi({module: 'menu'}))).forEach(menu => {
    // 记录菜单的父子关系: {id01: pid01, id02: pid02 .. }
    idToPidMap[menu['id']] = menu['pid'];
    // 组装 ElTransfer 数据
    if (menu['pid'] !== 0) {
      allMenus.value.push({label: menu['parentTitle'] + ' / ' + menu['title'], key: menu['id']});
    }
  });

  // 查询该角色的菜单
  Object.values(getResponseData(await listByRoleIdApi(roleId))).forEach(menu => {
    menuIds.value.push(menu['id']);
  });
});

</script>

<template>
  <div class="role-menus-body">
    <my-nav :items="navItems"/>
    <el-transfer class="role-menus-transfer"
                 filterable filter-placeholder="输入关键字"
                 v-if="allMenus.length > 0"
                 v-model="menuIds"
                 :data="allMenus"
                 :titles="['全部可选菜单', '【' + roleTitle + '】已选菜单']"
                 :props="{key: 'key', label: 'label'}"
                 :button-texts="['移除', '添加']">
      <template #left-footer>
        <el-text class="mx-1" type="info">tips: 请重新选择该角色的菜单!</el-text>
      </template>
      <template #right-footer>
        <el-button type="primary" @click="updateMenus" size="small">确认修改</el-button>
      </template>
    </el-transfer>
  </div>
</template>

<style scoped lang="scss">
.role-menus-body {
  text-align: center; // 内容居中

  .role-menus-transfer {
    margin-top: 65px; // 上外边距
  }
}

:deep(.el-transfer-panel) {
  width: 300px; // 宽度
}

:deep(.el-transfer-panel__body) {
  height: 400px; // 高度
}

:deep(.el-transfer-panel__footer) {
  text-align: center; // 内容居中
}
</style>

E04. UMS菜单模块

武技:在 router/index.js 文件中开发全部相关页面路由配置

import Menu from '../views/ums/menu/Menu.vue';
import MenuInsert from '../views/ums/menu/MenuInsert.vue';
import MenuUpdate from '../views/ums/menu/MenuUpdate.vue';
import SubMenu from '../views/ums/menu/subMenu/SubMenu.vue';
import SubMenuInsert from '../views/ums/menu/subMenu/SubMenuInsert.vue';
import SubMenuUpdate from '../views/ums/menu/subMenu/SubMenuUpdate.vue';

const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {path: '/', name: 'Login', component: Login},
        {
            path: '/Main', name: 'Main', component: Main,
            redirect: '/Dashboard',
            children: [
                {path: '/Menu', name: 'Menu', component: Menu},
                {path: '/MenuInsert', name: 'MenuInsert', component: MenuInsert},
                {path: '/MenuUpdate', name: 'MenuUpdate', component: MenuUpdate},
                {path: '/SubMenu', name: 'SubMenu', component: SubMenu},
                {path: '/SubMenuInsert', name: 'SubMenuInsert', component: SubMenuInsert},
                {path: '/SubMenuUpdate', name: 'SubMenuUpdate', component: SubMenuUpdate},
            ]
        }
    ]
});

1. 父菜单列表

心法:父菜单列表页面

在这里插入图片描述

武技:开发父菜单列表页面 views/ums/menu/Menu.vue

<script setup>
import MyNav from "../../../components/MyNav.vue";
import MyHead from "../../../components/MyHead.vue";
import MyTable from "../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../router/index.js";

// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'Menu', label: '菜单列表(父菜单)'}
];
// 数据头
const headItems = [
  {type: 'ipt', span: 5, placeholder: '搜索父菜单名', callback: pageByTitle},
];
// 表格列
const columns = [
	{label: '菜单图标', prop: 'icon', type: 'icon', width: 90},  
	{label: '菜单名称', prop: 'title', width: 120},  
	{label: '菜单描述', prop: 'info', type: 'card', width: 520},
];
// 按钮列
const buttons = [
  {label: '下级菜单', icon: 'Menu', callback: toSubMenu},
];

/* ==================== 跳转下级菜单页面 ==================== */

function toSubMenu(row) {
  sessionStorage.setItem('pid', row['id']);
  sessionStorage.setItem('parentTitle', row['title']);
  router.push('/SubMenu');
}

/* ==================== 分页查询 ==================== */

// 表格数据 + 分页数据 + 菜单名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let menuTitle = ref();

async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
  // 分页参数(额外添加 pid=0 保证仅查询父菜单)
  let config = {
    api: pageApi,
    args: {module: 'menu'},
    params: {pageNum, pageSize, pid: 0},
    records, pageInfo
  };
  // 若菜单名不为空,则附加为分页条件
  if (menuTitle.value) config['params']['title'] = menuTitle.value;
  // 发送分页请求
  await myPage(config);
}

/* ==================== 搜索菜单名 ==================== */

function pageByTitle(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || menuTitle.value) {
    menuTitle.value = val;
    page();
  }
}

/* ==================== 删除成功回调 ==================== */

function deleteSuccess() {
  ElMessage.success('删除成功!');
  page();
}

/* ==================== 报表打印 ==================== */

function downloadExcel() {
  excelApi('/menu/excel', '菜单报表');
}

/* ==================== 加载函数 ==================== */

onMounted(() => page());

</script>

<template>
  <my-nav :items="navItems"/>
  <my-head :items="headItems"/>
  <my-table module="menu"
            insert-page="/MenuInsert"
            update-page="/MenuUpdate"
            :records="records"
            :columns="columns"
            :buttons="buttons"
            :delete-api="deleteApi"
            :delete-batch-api="deleteBatchApi"
            :delete-callback="deleteSuccess"
            :excel-api="downloadExcel"
            :pageInfo="pageInfo"/>
</template>

<style scoped lang="scss"></style>

2. 添加父菜单

心法:添加父菜单页面

在这里插入图片描述

武技:开发添加父菜单页面 views/ums/menu/MenuInsert.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理', url: '/Dept'},
  {icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
  {icon: 'Plus', label: '添加新菜单(父菜单)'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '菜单名称', prop: 'title', required: true, span: 12},
  {span: 12, hidden: true},
  {label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
  {label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({pid: 0, url: '/'});
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 添加成功后 ==================== */

function insertSuccess() {
  ElMessage.success('添加记录成功!');
  setTimeout(() => router.push('/Menu'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="menu-insert-card" header="添加新菜单(父菜单)">
    <my-form type="insert"
             :items="items"
             :params="params"
             :rules="rules"
             :api="insertApi"
             :args="{module: 'menu'}"
             :callback="insertSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.menu-insert-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

3. 修改父菜单

心法:修改父菜单页面

在这里插入图片描述

武技:开发修改父菜单页面 views/ums/menu/MenuUpdate.vue

<script setup>
import MyForm from "../../../components/MyForm.vue";
import MyNav from "../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {updateApi} from "../../../api/axios.js";
import {RULE} from "../../../const";
import {ElMessage} from "element-plus";
import router from "../../../router";

// 菜单记录
let menu = JSON.parse(sessionStorage.getItem('row'));
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理', url: '/Dept'},
  {icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
  {icon: 'Edit', label: '修改菜单(父菜单)'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '菜单名称', prop: 'title', required: true, span: 12},
  {span: 12, hidden: true},
  {label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
  {label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(menu);
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 修改成功后 ==================== */

function updateSuccess() {
  ElMessage.success('修改记录成功!');
  setTimeout(() => router.push('/Menu'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="menu-update-card" header="修改菜单(父菜单)">
    <my-form type="update"
             :items="items"
             :params="params"
             :rules="rules"
             :api="updateApi"
             :args="{module: 'menu'}"
             :callback="updateSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.menu-update-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

4. 子菜单列表

心法:子菜单列表页面

在这里插入图片描述

武技:开发子菜单列表页面 views/ums/menu/subMenu/SubMenu.vue

<script setup>
import MyNav from "../../../../components/MyNav.vue";
import MyHead from "../../../../components/MyHead.vue";
import MyTable from "../../../../components/MyTable.vue";
import {onMounted, ref, reactive} from "vue";
import {myPage} from "../../../../request";
import {deleteApi, deleteBatchApi, excelApi, pageApi} from "../../../../api/axios";
import {ElMessage} from "element-plus";
import router from "../../../../router/index.js";

// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
  {icon: 'Menu', label: '菜单列表(子菜单)'}
];
// 数据头
const headItems = [
  {type: 'ipt', span: 5, placeholder: '搜索子菜单名', callback: pageByTitle},
];
// 表格列
const columns = [
  {label: '菜单图标', prop: 'icon', type: 'icon', width: 90},
  {label: '菜单名称', prop: 'title', width: 120},
  {label: '路由地址', prop: 'url', width: 120},
  {label: '父菜单名称', prop: 'parent.title', width: 120},
  {label: '菜单描述', prop: 'info', type: 'card', width: 520},
];

/* ==================== 分页查询 ==================== */

// 表格数据 + 分页数据 + 菜单名称
let records = ref();
let pageInfo = reactive({pageNum: 1, pageSize: 5, total: 0, callback: page});
let menuTitle = ref();

async function page(pageNum = pageInfo['pageNum'], pageSize = pageInfo['pageSize']) {
  // 分页参数
  let config = {
    api: pageApi,
    args: {module: 'menu'},
    params: {pageNum, pageSize, pid},
    records, pageInfo
  };
  // 若菜单名不为空,则附加为分页条件
  if (menuTitle.value) config['params']['title'] = menuTitle.value;
  // 发送分页请求
  await myPage(config);
}

/* ==================== 搜索菜单名 ==================== */

function pageByTitle(val) {
  // 仅当输入框有值,或者当前处于按条件分页状态时,发送分页请求
  if (val || menuTitle.value) {
    menuTitle.value = val;
    page();
  }
}

/* ==================== 删除成功回调 ==================== */

function deleteSuccess() {
  ElMessage.success('删除成功!');
  page();
}

/* ==================== 报表打印 ==================== */

function downloadExcel() {
  excelApi('/menu/excel', '菜单报表');
}

/* ==================== 加载函数 ==================== */

onMounted(() => page());
</script>

<template>
  <my-nav :items="navItems"/>
  <my-head :items="headItems"/>
  <my-table module="menu"
            insert-page="/SubMenuInsert"
            update-page="/SubMenuUpdate"
            :records="records"
            :columns="columns"
            :delete-api="deleteApi"
            :delete-batch-api="deleteBatchApi"
            :delete-callback="deleteSuccess"
            :excel-api="downloadExcel"
            :pageInfo="pageInfo"/>
</template>

<style scoped lang="scss"></style>

5. 添加子菜单

心法:添加子菜单页面

在这里插入图片描述

武技:开发添加子菜单页面 views/ums/menu//subMenu/SubMenuInsert.vue

<script setup>
import MyForm from "../../../../components/MyForm.vue";
import MyNav from "../../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {insertApi} from "../../../../api/axios.js";
import {RULE} from "../../../../const";
import {ElMessage} from "element-plus";
import router from "../../../../router";

// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
  {icon: 'Menu', label: '菜单列表(子菜单)', url: '/SubMenu'},
  {icon: 'Plus', label: '添加新菜单(子菜单)'},
]
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '上级菜单', prop: 'parentTitle', disabled: true},
  {label: '菜单名称', prop: 'title', required: true, span: 12},
  {label: '路由地址', prop: 'url', required: true, span: 12},
  {label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
  {label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive({pid, parentTitle});
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 添加成功后 ==================== */

function insertSuccess() {
  ElMessage.success('添加成功!');
  setTimeout(() => router.push('/SubMenu'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="menu-insert-card" header="添加新菜单(子菜单)">
    <my-form type="insert"
             :items="items"
             :params="params"
             :rules="rules"
             :api="insertApi"
             :args="{module: 'menu'}"
             :callback="insertSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.menu-insert-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

6. 修改子菜单

心法:修改子菜单页面

在这里插入图片描述

武技:开发修改子菜单页面 views/ums/menu/subMenu/SubMenuUpdate.vue

<script setup>
import MyForm from "../../../../components/MyForm.vue";
import MyNav from "../../../../components/MyNav.vue";
import {reactive, ref} from "vue";
import {updateApi} from "../../../../api/axios.js";
import {RULE} from "../../../../const";
import {ElMessage} from "element-plus";
import router from "../../../../router";

// 菜单记录
let menu = JSON.parse(sessionStorage.getItem('row'));
// 父菜单ID和父菜单名称
const pid = sessionStorage.getItem('pid');
const parentTitle = sessionStorage.getItem('parentTitle');
menu['pid'] = pid;
menu['parentTitle'] = parentTitle;
// 路径导航
const navItems = [
  {icon: 'Avatar', label: '用户管理'},
  {icon: 'Menu', label: '菜单列表(父菜单)', url: '/Menu'},
  {icon: 'Menu', label: '菜单列表(子菜单)', url: '/SubMenu'},
  {icon: 'Edit', label: '修改菜单(子菜单)'},
];
// 表单项 + 表单值 + 表单规则
let items = ref([
  {label: '上级菜单', prop: 'parentTitle', disabled: true},
  {label: '菜单名称', prop: 'title', required: true, span: 12},
  {label: '路由地址', prop: 'url', required: true, span: 12},
  {label: '菜单图标', prop: 'icon', required: true, type: 'icon'},
  {label: '菜单描述', prop: 'info', type: 'textarea'},
]);
let params = reactive(menu);
let rules = {title: RULE.TITLE, info: RULE.INFO};

/* ==================== 修改成功后 ==================== */

function updateSuccess() {
  ElMessage.success('修改记录成功!');
  setTimeout(() => router.push('/SubMenu'), 1000);
}
</script>

<template>
  <my-nav :items="navItems"/>
  <el-card class="menu-update-card" header="修改菜单(子菜单)">
    <my-form type="update"
             :items="items"
             :params="params"
             :rules="rules"
             :api="updateApi"
             :args="{module: 'menu'}"
             :callback="updateSuccess"/>
  </el-card>
</template>

<style scoped lang="scss">
.menu-update-card {
  width: 60%; // 宽度
  margin: 65px auto 0; // 外边距
}
</style>

Java道经 - 项目 - MyClub - 后台前端(四)


传送门:JP3-4-MyClub后台前端(一)
传送门:JP3-4-MyClub后台前端(二)
传送门:JP3-4-MyClub后台前端(三)
传送门:JP3-4-MyClub后台前端(四)
传送门:JP3-4-MyClub后台前端(五)


网站公告

今日签到

点亮在社区的每一天
去签到