vue2制作高复用页面
记录一下页面搭建记录,利用vue2组件化开发的思想。这个页面适合于大部分信息管理系统~。模板固定,每次使用,直接修改表单表格参数,api接口等。
以上图页面为例,一个基础数据信息页面可以分为,分类(左侧),数据信息(右侧),搜索表单(右上),数据表格(右下),新增或编辑表单(对话框)。
全局css样式部分
.top_box {
// min-height: 10vh;
width: 100%;
border-radius: 5px;
background-color: #fff; /* 背景色为白色 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); /* 添加投影效果 */
padding: 10px 10px 10px;
margin-bottom: 10px;
}
.down_box {
min-height: 10vh;
width: 100%;
border-radius: 5px;
background-color: #fff; /* 背景色为白色 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); /* 添加投影效果 */
}
.left_box {
float: left;
width: 20%;
border-radius: 5px;
background-color: #fff; /* 背景色为白色 */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); /* 添加投影效果 */
padding-bottom: 5%;
}
.right_box {
float: left;
min-height: 90vh;
width: 79%;
margin-left: 1%;
border-radius: 5px;
background-color: #fff; /* 背景色为白色 */
}
index.vue(主页面)
<template>
<div class="app-container">
<div class="left_box">
<CategoryMenu @selectNode="selectNode" />
</div>
<div class="right_box">
<div class="top_box">
<search-form
:searchForm="searchForm"
:size="size"
@search="performSearch"
@reset="resetSearch"
/>
<div style="float: left; margin: 5px 0 0 20px">
<el-button
:disabled="currentCategoryId === null || currentCategoryId === ''"
type="primary"
plain
:size="size"
icon="el-icon-circle-plus-outline"
@click="toAdd"
>
新增
</el-button>
<el-button
type="success"
plain
:size="size"
icon="el-icon-document-copy"
disabled
>
迭代
</el-button>
</div>
<div style="clear: both"></div>
</div>
<div class="down_box">
<div style="padding: 20px 20px 10px 20px">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item
v-for="(item, index) in currentCategory"
:key="index"
>{{ item }}</el-breadcrumb-item
>
</el-breadcrumb>
</div>
<div style="padding: 10px; min-height: 400px">
<customTable :list="list" @toEdit="toEdit" @toDelete="toDelete" />
</div>
<div style="padding-bottom: 10px">
<el-pagination
:page-sizes="[10, 20, 40, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="counts"
:current-page.sync="page"
@size-change="(val) => handleSizeChange(val, this)"
@current-change="(val) => handleCurrentChange(val, this)"
align="center"
></el-pagination>
</div>
</div>
</div>
<editForm
:edit-vis="editVis"
:edit-status="editStatus"
:edit-form="editForm"
:rules="rules"
:userOption="userOption"
@submit="submitForm"
@cancel="cancel"
/>
</div>
</template>
<script>
import {
addStorageWarehouse,
pageStorageWarehouse,
updateStorageWarehouse,
deleteStorageWarehouse,
} from "@/api/storage/storage-warehouse";
import { list } from "@/api/system/user";
import {
confirmAction,
submitWithConfirmation,
} from "@/utils/confirmationHelper";
import paginationMethods from "@/utils/pagination.js";
import searchForm from "./components/searchForm.vue";
import customTable from "./components/customTable.vue";
import editForm from "./components/editForm.vue";
import CategoryMenu from "./components/CategoryMenu.vue";
export default {
name: "bom",
components: {
searchForm,
customTable,
editForm,
CategoryMenu,
},
data() {
return {
content: "BOM信息",
size: "small",
list: [],
searchForm: {},
currentCategoryId: "",
currentCategoryName: "",
currentCategory: [],
counts: 0,
page: 1,
pageSize: 10,
userOption: [],
editVis: false,
editStatus: false,
editForm: {},
rules: {
code: [{ required: true, message: "BOM编码不能为空", trigger: "blur" }],
},
};
},
watch: {},
created() {
this.init();
},
mounted() {
document.addEventListener("keyup", this.handleKeyUp);
},
beforeDestroy() {
document.removeEventListener("keyup", this.handleKeyUp);
},
methods: {
init() {
list().then((res) => {
this.userOption = res.data.list.map((item) => {
return {
value: item.id,
label: item.name,
};
});
});
this.fetchData();
},
handleKeyUp(event) {
if (event.key === "Enter") {
this.fetchData();
}
},
fetchData() {
var vm = this;
const params = {
page: vm.page,
pageSize: vm.pageSize,
categoryId: vm.currentCategoryId ? vm.currentCategoryId : undefined,
code: vm.searchForm.code ? vm.searchForm.code : undefined,
productCode: vm.searchForm.productCode
? vm.searchForm.productCode
: undefined,
};
// pageStorageWarehouse(params).then((res) => {
// vm.list = res.data.page.records;
// vm.counts = res.data.page.total;
// });
},
...paginationMethods, // 导入分页方法
performSearch(searchForm) {
this.searchForm = searchForm;
this.handleClickSearch(this);
},
resetSearch() {
this.pageSize = 10;
this.currentCategoryId = "";
this.currentCategoryName = "";
this.currentCategory = [];
this.resetTable(this);
},
toAdd() {
this.editForm = {};
this.editStatus = false;
this.editVis = true;
},
toEdit(row) {
this.editForm = { ...row };
this.editStatus = true;
this.editVis = true;
},
toDelete(row) {
const message = "是否删除" + this.content + "?";
const action = () => deleteStorageWarehouse(row.id);
confirmAction(this, message, action);
},
cancel() {
this.editVis = false;
this.editForm = {};
},
// 提交
submitForm(editForm) {
this.editForm.bomCategoryId = this.currentCategoryId;
const action = this.editStatus
? () => updateStorageWarehouse(editForm)
: () => addStorageWarehouse(editForm);
submitWithConfirmation(this, action);
},
selectNode(id, name, category) {
this.currentCategoryId = id;
this.currentCategoryName = name;
this.currentCategory = category.split(",");
this.fetchData();
},
},
};
</script>
<style lang="less" scoped>
</style>
分类菜单(左侧)
<template>
<div style="padding: 5px">
<div>
<h3 style="margin: 8px 0; color: rgb(111, 111, 111); text-align: center">
BOM分类
<span
style="float: right; margin-right: 10px; cursor: pointer"
@click="editVis = true"
>
<i class="el-icon-edit-outline" />
</span>
</h3>
</div>
<div style="margin-bottom: 10px">
<el-input placeholder="输入BOM分类" v-model="filterText" size="mini">
</el-input>
</div>
<el-tree
:data="data"
default-expand-all
:filter-node-method="filterNode"
:expand-on-click-node="false"
ref="tree"
>
<span slot-scope="{ data }">
<span style="font-size: 14px" @click="toFind(data)">
<i class="el-icon-folder" /> {{ data.code }} -
{{ data.name }}
</span>
</span>
</el-tree>
<el-dialog
title="BOM分类"
:visible.sync="editVis"
width="1280px"
top="56px"
append-to-body
>
<CategoryEdit />
</el-dialog>
</div>
</template>
<script>
import { getBomCategoryList } from "@/api/product/bom-category";
import CategoryEdit from "./CategoryEdit.vue";
export default {
components: {
CategoryEdit,
},
data() {
return {
filterText: "",
data: [],
editVis: false,
};
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
},
},
created() {
this.fetchData();
},
methods: {
fetchData() {
getBomCategoryList().then((res) => {
this.data = res.data.list;
});
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
toFind(data) {
// 返回目录id和目录分级
this.$parent.selectNode(data.id, data.name, this.findParentById(data.id));
},
findParentById(id) {
const findParentRecursive = (data, id, path = []) => {
for (let item of data) {
if (item.id === id) {
return path.concat(item);
} else if (item.children) {
const result = findParentRecursive(
item.children,
id,
path.concat(item)
);
if (result) {
return result;
}
}
}
return null;
};
const result = findParentRecursive(this.data, id);
if (result) {
return result.map((item) => item.name).join(",");
} else {
return "未找到父级元素";
}
},
},
};
</script>
搜索表单
<template>
<el-form label-width="100px" v-model="searchForm">
<el-form-item :size="size" label="BOM编码:" class="searchItem">
<el-input
v-model="searchForm.code"
placeholder="请输入"
class="searchInput"
clearable
/>
</el-form-item>
<el-form-item :size="size" label="产品编码:" class="searchItem">
<el-input
v-model="searchForm.productCode"
placeholder="请输入"
class="searchInput"
clearable
/>
</el-form-item>
<div class="searchItem">
<el-button
type="primary"
:size="size"
icon="el-icon-search"
@click="handleClickSearch()"
>
搜索
</el-button>
<el-button
icon="el-icon-refresh-right"
:size="size"
@click="() => resetTable()"
>
重置
</el-button>
</div>
<div style="clear: both"></div>
<!-- 清除浮动 -->
</el-form>
</template>
<script>
export default {
name: "SearchForm",
props: {
searchForm: {
type: Object,
required: true,
},
size: {
type: String,
default: "mini",
},
},
methods: {
handleClickSearch() {
this.$emit("search", this.searchForm);
},
resetTable() {
this.$emit("reset");
},
},
};
</script>
<style scoped>
.searchItem {
float: left;
margin-left: 20px;
}
.searchInput {
width: 160px;
}
</style>
数据表格
<template>
<div>
<el-table :data="list" fit highlight-current-row>
<el-table-column type="selection" width="55"> </el-table-column>
<el-table-column label="BOM编码" align="center" prop="code" width="230">
<template slot-scope="scope">
<span>
<el-link
:underline="false"
type="primary"
@click="toEdit(scope.row)"
>{{ scope.row.code }}</el-link
></span
>
</template>
</el-table-column>
<el-table-column
label="关联产品"
align="center"
prop="productCode"
width="230"
/>
<el-table-column label="版本号" align="center" prop="version" />
<el-table-column label="状态" align="center" prop="status" />
<!-- 操作栏 -->
<el-table-column
label="操作"
align="center"
width="230"
fixed="right"
prop="operation"
>
<template slot-scope="{ row, $index }">
<el-button size="mini" type="success" @click="toCopy(row)">
迭代
</el-button>
<el-button size="mini" type="success" v-if="row.status === 1">
提交
</el-button>
<el-button size="mini" type="warning" v-if="row.status === 2">
审核
</el-button>
<el-button size="mini" type="primary" @click="toEdit(row)">
查看
</el-button>
<el-button size="mini" type="danger" @click="toDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "custom-table",
props: {
list: {
type: Array,
required: true,
},
},
data() {
return {
size: "small",
};
},
watch: {
list(newVal, oldVal) {},
},
methods: {
toEdit(row) {
this.$emit("toEdit", row);
},
toCopy(row) {},
toDelete(row) {
this.$emit("toDelete", row);
},
},
};
</script>
<style>
</style>
新增或编辑表单(对话框)
<template>
<el-dialog
:title="editStatus ? '查看BOM信息' : '新增BOM信息'"
:visible="vis"
width="730px"
top="56px"
append-to-body
:before-close="handleClose"
>
<el-form
ref="editForm"
:model="editForm"
:rules="rules"
label-width="115px"
size="small"
>
<el-form-item
:size="size"
label="BOM编码:"
prop="code"
style="float: left"
>
<el-input
v-model="editForm.code"
class="editItem"
placeholder="请输入"
/>
</el-form-item>
<el-form-item
:size="size"
label="产品信息:"
prop="name"
style="float: left"
>
<el-input
v-model="editForm.name"
class="editItem"
placeholder="请输入"
/>
</el-form-item>
<div style="clear: both"></div>
<el-form-item label="备注:" prop="remark" style="float: left">
<el-input
v-model="editForm.remark"
type="textarea"
placeholder="请输入备注"
style="width: 515px"
/>
</el-form-item>
<div style="clear: both"></div>
</el-form>
<div slot="footer" class="dialog-footer">
<div style="margin-right: 60px">
<el-button
v-if="editStatus"
type="primary"
size="mini"
@click="submitForm('editForm')"
>保 存</el-button
>
<el-button
v-if="!editStatus"
type="primary"
size="mini"
@click="submitForm('editForm')"
>新 增</el-button
>
<el-button size="mini" @click="cancel">取 消</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
export default {
name: "edit",
props: {
editVis: {
type: Boolean,
required: true,
},
editStatus: {
type: Boolean,
required: true,
},
editForm: {
type: Object,
required: true,
},
rules: {
type: Object,
required: true,
},
userOption: {
type: Array,
required: true,
},
},
data() {
return {
size: "small",
vis: false,
};
},
watch: {
editVis(newVal, oldVal) {
this.vis = this.editVis;
},
},
methods: {
handleClose(done) {
this.$confirm("您确定要关闭吗?", "确认", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.cancel();
done();
})
.catch(() => {
// 用户点击取消时的处理
});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit("submit", this.editForm);
} else {
console.log("error submit!!");
return false;
}
});
},
cancel() {
this.$refs["editForm"].resetFields();
this.$emit("cancel");
},
},
};
</script>
<style lang="less" scoped>
.editItem {
width: 200px;
}
/deep/ .el-collapse-item__header {
padding-left: 50px;
}
</style>