Appearance
前端代码
涉及文件
温馨提示
主要涉及api接口,views视图,其它文件无须修改;路由是后端接口返回,需要先在后台创建菜单路由
代码均可使用代码生成工具一键生成,分别存放cogen 目录下
1. 添加路由
- 路由分为菜单和按钮两种
- 菜单主要设置,权限标识,路由路径,组件路径;
- 按钮主要设置权限标识
- 权限标识采用api接口名称,用“:” 分割,用于操作权限校验,使用
/@/utils/authFunction
的auth
方法 - 路由路径 是在视图在views 下的目录路径,例如
/system/user
- 组件路径 是在视图在views 下的文件路径,例如
/system/user/index
;一级菜单默认/layout/routerView/parent
2. 添加接口
- 在
src\api
目录下添加接口文件,主要方法增删改查
sh
import request from '/@/utils/request';
//获取分页列表
export function getList(params: object) {
return request({
url: '/admin/admin/index',
method: 'post',
data: params,
});
}
//获取全部菜单列表
export function getListAll(params: any) {
return request({
url: '/admin/admin/getlists',
method: 'post',
data: params,
});
}
//获取详情
export function getinfo(id: String) {
return request({
url: '/admin/admin/getinfo',
method: 'post',
data: { id },
});
}
//保存
export function save(params: object) {
return request({
url: '/admin/admin/save',
method: 'post',
data: params,
});
}
//删除
export function del(id: String) {
return request({
url: '/admin/admin/del',
method: 'post',
data: { id },
});
}
3. 创建视图列表
- 根据组件路径,在
src\views
下面创建文件 - 主要结构包括查询,表格,操作,分页,表单
sh
<template>
<div class="table-admin-container layout-padding">
<div class="table-admin-padding layout-padding-view layout-padding-auto">
<TableSearch :search="state.tableData.search" :isAdd="state.tableData.config.isAdd" @search="onSearch" @add="onTableAddRow"/>
<Table
ref="tableRef"
v-bind="state.tableData"
class="table-admin"
@updateRow="onTableUpdateRow"
@delRow="onTableDelRow"
@pageChange="onTablePageChange"
@sortHeader="onSortHeader"
/>
</div>
<EditDialog ref="editRef" @refresh="getTableData()" />
</div>
</template>
- 主要操作方法包括列表,新增,修改,分页
sh
<script setup lang="ts" name="articleColumn">
import { defineAsyncComponent, reactive, ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { getListAll,del } from '/@/api/articlecolumn/index';
import { handleTree } from '/@/utils';
// 引入组件
const Table = defineAsyncComponent(() => import('/@/components/table/index.vue'));
const TableSearch = defineAsyncComponent(() => import('/@/components/table/search.vue'));
const EditDialog = defineAsyncComponent(() => import('/@/views/article/column/form/edit.vue'));
// 定义变量内容
const tableRef = ref<RefType>();
const editRef = ref();
const state = reactive<TableDemoState>({
tableData: {
// 列表数据(必传)
data: [],
// 表头内容(必传,注意格式)
header: [
{ key: 'id', colWidth: '80', title: 'ID', type: 'text', isCheck: true },
{ key: 'name', colWidth: '300', title: '名称', type: 'text', isCheck: true },
{ key: 'imgs', colWidth: '80', title: '封面', type: 'image', isCheck: true },
{ key: 'sorts', colWidth: '', title: '排序', type: 'text', isCheck: true },
{ key: 'types', colWidth: '', title: '类型', type: 'radio', dict:{'1':'列表','2':'单页','3':'链接'},isCheck: true },
{ key: 'status', colWidth: '', title: '状态', type: 'radio',dict:{'1':'正常','0':'禁用'},isCheck: true },
],
// 配置项(必传)
config: {
total: 0, // 列表总数
loading: true, // loading 加载
isBorder: false, // 是否显示表格边框
isSerialNo: false, // 是否显示表格序号
isSelection: true, // 是否显示表格多选
isOperate: true, // 是否显示表格操作栏
isAdd: 'admin:articlecolumn:save', //新增标识
isUpdate: 'admin:articlecolumn:save', //修改标识
isDelete: 'admin:articlecolumn:del', //删除标识
},
// 搜索表单,动态生成(传空数组时,将不显示搜索,注意格式)
search: [
{ label: '名称', prop: 'name', placeholder: '请输入名称', required: false, type: 'input' },
{
label: '状态',
prop: 'status',
placeholder: '请选择',
required: false,
type: 'select',
options: [
{ label: '正常', value: 1 },
{ label: '禁用', value: 0 },
],
},
],
// 搜索参数(不用传,用于分页、搜索时传给后台的值,`getTableData` 中使用)
param: {
page: 1,
psize: 10,
},
// 打印标题
printName: '数据打印',
//列字典
dictData:{
},
},
});
// 初始化列表数据
const getTableData = () => {
state.tableData.config.loading = true;
state.tableData.data = [];
getListAll(state.tableData.param).then((response: any) => {
state.tableData.data = handleTree(response.data.data, 'id', 'pid', 'children');
state.tableData.config.total = response.data.total;
state.tableData.config.loading = false;
});
};
// 搜索点击时表单回调
const onSearch = (data: EmptyObjectType) => {
state.tableData.param = Object.assign({}, state.tableData.param, { ...data });
tableRef.value.pageReset();
};
// 删除当前项回调
const onTableDelRow = (row: EmptyObjectType) => {
return del(row.id).then(() => {
getTableData();
ElMessage.success(`删除${row.id}成功!`);
});
};
// 分页改变时回调
const onTablePageChange = (page: TableDemoPageType) => {
state.tableData.param.page = page.page;
state.tableData.param.psize = page.psize;
getTableData();
};
// 拖动显示列排序回调
const onSortHeader = (data: TableHeaderType[]) => {
state.tableData.header = data;
};
/** 新增按钮操作 */
const onTableAddRow = () => {
editRef.value.openDialog('add');
};
/** 修改按钮操作 */
const onTableUpdateRow = (row: any) => {
editRef.value.openDialog('edit',row);
};
// 页面加载时
onMounted(() => {
getTableData();
});
</script>
<style scoped lang="scss">
</style>
4. 创建视图表单
- 根据路由路径,在
src\views\路由路径\form
下面创建文件 - 主要结构包括表单字段和保存按钮
sh
<template>
<div class="system-menu-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="769px">
<el-form ref="dialogFormRef" :model="state.ruleForm" :rules="state.ruleRules" size="default" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="上级栏目" prop="pid">
<el-cascader
:options="state.menuData"
:props="{ checkStrictly: true, value: 'id', label: 'name' }"
placeholder="请选择上级栏目"
clearable
class="w100"
v-model="state.ruleForm.pid"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
</template>
</el-cascader>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="栏目类型" prop="types">
<el-radio-group v-model="state.ruleForm.types">
<el-radio :label="1">列表</el-radio>
<el-radio :label="2">单页</el-radio>
<el-radio :label="3">链接</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="栏目状态" prop="status">
<el-radio-group v-model="state.ruleForm.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
<el-form-item label="栏目名称" prop="name">
<el-input v-model="state.ruleForm.name" placeholder="请输入栏目名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
<el-form-item label="SEO描述" prop="seo_dec">
<el-input v-model="state.ruleForm.seo_dec" type="textarea" placeholder="请输入SEO描述" clearable></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default">取 消</el-button>
<el-button type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
- 主要操作方法包括详情,保存
sh
<script setup lang="ts" name="articleColumnForm">
import { defineAsyncComponent, reactive, onMounted, ref ,unref,nextTick} from 'vue';
import { getListAll,getinfo,save } from '/@/api/articlecolumn/index';
import { ElMessageBox, ElMessage } from 'element-plus';
import { handleTree } from '/@/utils';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 引入组件
const IconSelector = defineAsyncComponent(() => import('/@/components/iconSelector/index.vue'));
const Uploadimg = defineAsyncComponent(() => import('/@/components/Upload/singleImage.vue'));
// 定义变量内容
const dialogFormRef = ref();
const state = reactive({
ruleForm: {
id:0,
pid: 0, // 上级栏目
types: 1, // 栏目类型
},
menuData: [] , // 上级栏目数据
dialog: {
isShowDialog: false,
type: '',
title: '',
submitTxt: '',
},
// 表单校验
ruleRules: {
name: [{ required: true, message: '栏目名称不能为空', trigger: 'blur' }],
},
loading: false,
msg:'最佳尺寸',
});
const onUpload = (data) => {
state.ruleForm.imgs = data;
};
// 打开弹窗
const openDialog = (type: string, row?: any) => {
if (type === 'edit') {
// 实际请走接口
getinfo(row.id).then((response: any) => {
state.ruleForm.id = response.data.id;
state.ruleForm.seo_dec = response.data.seo_dec;
});
state.dialog.title = '修改栏目';
state.dialog.submitTxt = '修 改';
} else {
state.dialog.title = '新增栏目';
state.dialog.submitTxt = '新 增';
// 清空表单,此项需加表单验证才能使用
nextTick(() => {
dialogFormRef.value.resetFields();
initForm();
});
}
state.dialog.type = type;
state.dialog.isShowDialog = true;
// 查询栏目下拉树结构
getListAll().then((response: any) => {
response.data.data.unshift({ id: 0, name: '顶级栏目' });
state.menuData = handleTree(response.data.data, 'id', 'pid', 'children');
});
};
// 关闭弹窗
const closeDialog = () => {
initForm();
state.dialog.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 提交
const onSubmit = () => {
const formWrap = unref(dialogFormRef) as any;
if (!formWrap) return;
formWrap.validate((valid: boolean) => {
if (valid) {
state.loading = true;
if(state.ruleForm.pid != 0 && typeof(state.ruleForm.pid) == 'object'){
state.ruleForm.pid = state.ruleForm.pid.pop();
}
save(state.ruleForm)
.then(() => {
ElMessage.success('保存成功');
state.loading = false;
closeDialog(); // 关闭弹窗
emit('refresh');
})
.finally(() => {
state.loading = false;
});
}
})
};
// 页面加载时
onMounted(() => {
});
// 表单初始化,方法:`resetFields()` 无法使用
const initForm = () => {
state.ruleForm.id = 0;
state.ruleForm.seo_dec = '';
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
5. 刷新页面
- 刷新页面,查看结果