vue3+ts+element-plus 开发一个页面模块的详细过程
目录、文件名均使用kebab-case(短横线分隔式)命名规范
子组件目录:./progress-ctrl/comps
1、新建页面文件 progress-ctrl.vue
<script setup lang="ts" name="progress-ctrl">
</script>
<template></template>
<style scoped lang="scss"></style>
运行效果:
2、 页面布局
<script setup lang="ts" name="progress-ctrl">
</script>
<template>
<el-container class="layout-container">
<el-aside class="aside">
<!-- 左侧受理类别节点树 -->
受理类别节点树
</el-aside>
<el-container class="container">
<el-header class="header">
<!-- 查询栏 -->
查询栏
</el-header>
<el-main class="main">
<!-- main -->
主项数据区域
</el-main>
<el-footer class="footer">
<!-- 选项卡 -->
选项卡
<!-- 分页 -->
分页
</el-footer>
</el-container>
</el-container>
</template>
<style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.layout-container {
height: 100%;
border: 1px solid #ddd;
.aside {
width: 150px;
}
.container {
border-left: 1px solid #ddd;
.header {
height: auto;
min-height: 40px;
border-bottom: 1px solid #ddd;
}
.main {
min-height: 100px;
}
.footer {
height: auto;
min-height: 40px;
border-top: 1px solid #ddd;
}
}
}
</style>
运行效果:
3、新建子组件
3.1、子组件-左侧树
./progress-ctrl/comps/progress-ctrl-tree.vue
<script setup lang="ts" name="progress-ctrl-tree">
</script>
<template>
<div class="tree">
<el-scrollbar>
<el-tree
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:indent="0">
</el-tree>
</el-scrollbar>
</div>
</template>
<style scoped lang="scss"></style>
3.2、子组件-查询栏
./progress-ctrl/comps/progress-ctrl-search.vue
<script setup lang="ts" name="progress-ctrl-search">
</script>
<template>
<div class="search">
<el-form class="header-form" :inline="true" :label-width="90">
<el-form-item class="header-form-item-330" label="受理编号:">
<el-input clearable />
</el-form-item>
<el-form-item class="header-form-item-auto">
<el-button class="btn-same-width" type="primary" plain @click="">查询</el-button>
<el-button class="btn-same-width" type="primary" plain @click="">重置</el-button>
<el-button class="btn-same-width" type="primary" plain @click="">更多筛选</el-button>
</el-form-item>
</el-form>
<el-form class="header-form" :inline="true" :label-width="90" v-show="">
<el-form-item class="header-form-item-330" label="受理日期:">
<el-date-picker
type="daterange"
start-placeholder="开始日期"
range-separator="至"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD">
</el-date-picker>
</el-form-item>
<el-form-item class="header-form-item-330" label="受检单位:">
<el-input clearable />
</el-form-item>
</el-form>
</div>
</template>
<style scoped lang="scss"></style>
3.3、子组件-主项数据
./progress-ctrl/comps/progress-ctrl-main.vue
<script setup lang="ts" name="progress-ctrl-main">
</script>
<template>
<div class="main">
<el-table
ref="table"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="outerApplyId"
label="受理编号"
width="120"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="stateDescription"
label="状态"
width="150"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptDate"
label="受理日期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="reportDate"
label="报告限期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="verifyTypeName"
label="检验类别"
width="120"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptTypeName"
label="受理类别"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptGroupName"
label="受理组别"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="sjdwName"
label="受检单位"
width="300"
header-align="center"
sortable
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
3.4、子组件-选项卡
./progress-ctrl/comps/progress-ctrl-tabs.vue
<script setup lang="ts" name="progress-ctrl-tabs">
</script>
<template>
<div class="tabs">
<el-tabs type="border-card" @tab-click="">
<el-tab-pane label="受理信息" name="apply">
</el-tab-pane>
<el-tab-pane label="交接信息" name="jjd">
</el-tab-pane>
<el-tab-pane label="检测信息" name="test">
</el-tab-pane>
<el-tab-pane label="报告信息" name="report">
</el-tab-pane>
</el-tabs>
</div>
</template>
<style scoped lang="scss"></style>
3.5、子组件-分页
./progress-ctrl/comps/progress-ctrl-pagination.vue
<script setup lang="ts" name="progress-ctrl-pagination">
</script>
<template>
<div class="pagination">
<el-pagination
:page-sizes="[5]"
background
layout="total, prev, pager, next"
:small="true" />
</div>
</template>
<style scoped lang="scss"></style>
4、新建选项卡组件的子组件
4.1、孙组件-受理信息
./progress-ctrl/comps/progress-ctrl-tabs-apply.vue
<script setup lang="ts" name="progress-ctrl-tabs-apply">
</script>
<template>
<div class="tabs-apply">
<el-table
ref="applyTable"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="applyId"
label="子受理编号"
width="120"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptDate"
label="受理日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="acceptSmallTypeName"
label="受理小类"
width="150"
header-align="center"
show-overflow-tooltip />
<el-table-column prop="sampleKind" label="检材类别" width="150" header-align="center" show-overflow-tooltip />
<el-table-column
prop="keepCondition"
label="描述信息"
width="150"
header-align="center"
show-overflow-tooltip />
<el-table-column
prop="acceptPersonName"
label="受理人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
4.2、孙组件-交接信息
./progress-ctrl/comps/progress-ctrl-tabs-jjd.vue
<script setup lang="ts" name="progress-ctrl-tabs-jjd">
</script>
<template>
<div class="tabs-jjd">
<el-table
ref="jjdTable"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="blPersonName"
label="派样人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column prop="submitTime" label="派样时间" width="200" header-align="center" show-overflow-tooltip />
<el-table-column
prop="checkGroupName"
label="检验组别"
width="150"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="receivePersonName"
label="接样人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="receiveTime"
label="接样时间"
width="200"
header-align="center"
show-overflow-tooltip />
<el-table-column
prop="deadlineTime"
label="交接限期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
4.3、孙组件-检测信息
./progress-ctrl/comps/progress-ctrl-tabs-test.vue
<script setup lang="ts" name="progress-ctrl-tabs-test">
</script>
<template>
<div class="tabs-test">
<el-table
ref="sampleItemResultTable"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="sampleNo"
label="样品编号"
width="120"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column
prop="sampleName"
label="样品名称"
width="150"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column
prop="itemName"
label="检验项目"
width="150"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column
prop="result"
label="结果"
width="120"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="conclusion"
label="结论"
width="80"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="standardScript"
label="标准值"
width="120"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column prop="unit" label="单位" width="100" header-align="center" sortable show-overflow-tooltip />
<el-table-column
prop="ffbzId"
label="检测标准编号"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="ffbzName"
label="检测标准名称"
width="300"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="verifyMethod"
label="检验方法"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="person"
label="检验员"
width="100"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="allotTime"
label="分派时间"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="submitTime"
label="检测时间"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="deadlineDate"
label="检测限期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
4.4、孙组件-报告信息
./progress-ctrl/comps/progress-ctrl-tabs-report.vue
<script setup lang="ts" name="progress-ctrl-tabs-report">
</script>
<template>
<div class="tabs-report">
<el-table
ref="reportTable"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="deptName"
label="所属部门"
width="150"
header-align="center"
fixed="left"
sortable
show-overflow-tooltip />
<el-table-column
prop="type"
label="类型"
width="100"
header-align="center"
:align="`center`"
fixed="left"
sortable
show-overflow-tooltip
:formatter="(row: any) => { return row.type === 1 ? `检验报告` : `评价报告` }" />
<el-table-column
prop="reportTypeName"
label="报告类型"
width="200"
header-align="center"
fixed="left"
sortable
show-overflow-tooltip />
<el-table-column
prop="hbPerson"
label="编制人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hbDate"
label="编制日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hdPerson"
label="核对人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hdDate"
label="核对日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hfPerson"
label="签发人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hfDate"
label="签发日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="reportDate"
label="报告限期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
5、选项卡子组件导入使用子组件
<script setup lang="ts" name="progress-ctrl-tabs">
import ProgressCtrlTabsApply from './progress-ctrl-tabs-apply.vue';
import ProgressCtrlTabsJJD from './progress-ctrl-tabs-jjd.vue';
import ProgressCtrlTabsTest from './progress-ctrl-tabs-test.vue';
import ProgressCtrlTabsReport from './progress-ctrl-tabs-report.vue';
</script>
<template>
<div class="tabs">
<el-tabs type="border-card" @tab-click="">
<el-tab-pane label="受理信息" name="apply">
<ProgressCtrlTabsApply />
</el-tab-pane>
<el-tab-pane label="交接信息" name="jjd">
<ProgressCtrlTabsJJD />
</el-tab-pane>
<el-tab-pane label="检测信息" name="test">
<ProgressCtrlTabsTest />
</el-tab-pane>
<el-tab-pane label="报告信息" name="report">
<ProgressCtrlTabsReport />
</el-tab-pane>
</el-tabs>
</div>
</template>
<style scoped lang="scss"></style>
6、父组件导入使用子组件
<script setup lang="ts" name="progress-ctrl">
import ProgressCtrlTree from './progress-ctrl/comps/progress-ctrl-tree.vue';
import ProgressCtrlSearch from './progress-ctrl/comps/progress-ctrl-search.vue';
import ProgressCtrlMain from './progress-ctrl/comps/progress-ctrl-main.vue';
import ProgressCtrlTabs from './progress-ctrl/comps/progress-ctrl-tabs.vue';
import ProgressCtrlPagination from './progress-ctrl/comps/progress-ctrl-pagination.vue';
</script>
<template>
<el-container class="layout-container">
<el-aside class="aside">
<!-- 左侧受理类别节点树 -->
<ProgressCtrlTree />
</el-aside>
<el-container class="container">
<el-header class="header">
<!-- 查询栏 -->
<ProgressCtrlSearch />
</el-header>
<el-main class="main">
<!-- main -->
<ProgressCtrlMain />
</el-main>
<el-footer class="footer">
<!-- 选项卡 -->
<ProgressCtrlTabs />
<!-- 分页 -->
<ProgressCtrlPagination />
</el-footer>
</el-container>
</el-container>
</template>
<style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.layout-container {
height: 100%;
border: 1px solid #ddd;
.aside {
width: 150px;
}
.container {
border-left: 1px solid #ddd;
.header {
height: auto;
min-height: 40px;
border-bottom: 1px solid #ddd;
}
.main {
min-height: 100px;
}
.footer {
height: auto;
min-height: 40px;
border-top: 1px solid #ddd;
}
}
}
</style>
运行效果:
7、数据模型业务逻辑处理
7.1、数据定义
import type { Component } from "vue";
export interface ITree {
// 树节点的label,受理类别名称
label: string;
// 树节点的value,受理类别代码
value: string;
// 树节点的图标
icon?: Component;
// 图标颜色
iconColor?: string;
// 子节点
children?: ITree[];
}
// 分页对象
interface IPage {
// 当前页码
page: number
// 每页显示数量
size: number
}
// 查询对象
export interface IQueryObj {
// 分页器
pageHelper: IPage
// 受理编号
outerApplyId?: string
// 受检单位
sjdwName?: string
// 受理类别
acceptType?: string
// 受理开始日期
acceptDateBegin?: string
// 受理结束日期
acceptDateEnd?: string
}
export interface IApplyBasicInfo {
// 受理编号
outerApplyId: string
// 状态
stateDescription: string
// 受理日期
acceptDate: string
// 报告限期
reportDate: string
// 检验类别
verifyTypeName: string
// 受理类别
acceptTypeName: string
// 受理组别
acceptGroupName: string
// 受检单位
sjdwName: string
}
// 结构继承使用 extends
// import type { Apply, JJD, SampleItemResult, ReportSimpleVO } from "@/interface";
// export interface IApply extends Apply {
// }
// export interface IJJD extends JJD {
// }
// export interface ITest extends SampleItemResult {
// }
// export interface IReport extends ReportSimpleVO {
// }
7.2、数据处理
import { defineStore } from "pinia";
import { ref } from "vue";
import { acceptTypeOptionsByUserNameService } from "@/api/selectOptions";
import type { IApplyBasicInfo, IQueryObj, ITree } from "../types";
import {
applyBasicInfoSjkByQueryDTOService,
applyBasicInfoYztByQueryDTOService,
applyBasicInfoSjkAllService,
applyBasicInfoSjkTodayService,
applyBasicInfoSjk3DayService,
applyBasicInfoSjkRecentService,
applyBasicInfoSjkOverdueService
} from "@/api/applyBasicInfo";
// 使用 as 重命名导出的类型
import type { Apply as IApply, JJD as IJJD, SampleItemResult as ITest, ReportSimpleVO as IReport } from "@/interface";
import { applyDataByOuterApplyIdService } from "@/api/apply";
import { jjdDataByOuterApplyIdService } from "@/api/jjd";
// 使用 as 重命名导出的方法
import { sampleItemResultByOuterApplyIdService as testDataByOuterApplyIdService } from "@/api/sampleItemResult";
import { reportDataByOuterApplyIdService } from "@/api/report";
const useProgressCtrlStore = defineStore("progressCtrl", () => {
// 页面实例数据
// 受理类别列表数据
const acceptTypeListData = ref<{ label: string; value: string }[]>([]);
// 树数据
const treeData = ref<ITree[]>([]);
// 受理列表数据
const applyBasicInfoListData = ref<IApplyBasicInfo[]>([]);
// 查询对象
const queryObj = ref<IQueryObj>({
pageHelper: {
page: 1,
size: 5
},
acceptType: "",
outerApplyId: "",
sjdwName: "",
acceptDateBegin: "",
acceptDateEnd: ""
});
// 当前点击选择的树节点
const currentNode = ref<ITree>();
// 当前表格选择行
const currentTableRow = ref<IApplyBasicInfo>();
// 当前选中的选项卡名称
const activeTabName = ref("test");
// 子受理列表数据
const applyListData = ref<IApply[]>([]);
// 交接单列表数据
const jjdListData = ref<IJJD[]>([]);
// 检验列表数据
const testListData = ref<ITest[]>([]);
// 报告列表数据
const reportListData = ref<IReport[]>([]);
// 发送网络请求,获取受理类别列表数据
const postAcceptTypeListDataService = async () => {
let result = await acceptTypeOptionsByUserNameService();
acceptTypeListData.value = result.data;
};
// 发送网络请求,获取受理列表数据
const postApplyBasicInfoListDataService = async () => {
// 前置处理,清空数据
clearData();
let result;
// 通过树节点数据,获取受理列表数据
if (
!queryObj.value.outerApplyId &&
!queryObj.value.sjdwName &&
!queryObj.value.acceptDateBegin &&
!queryObj.value.acceptDateEnd
) {
queryObj.value.acceptType = currentNode.value?.value;
switch (currentNode.value?.value) {
case "#ALL#":
// 发送网络请求,获取受监控的受理列表数据(所有)
result = await applyBasicInfoSjkAllService(queryObj.value);
break;
case "#today#":
// 发送网络请求,获取受监控的受理列表数据(今天内到期)
result = await applyBasicInfoSjkTodayService(queryObj.value);
case "#3day#":
// 发送网络请求,获取受监控的受理单(三天内到期)
result = await applyBasicInfoSjk3DayService(queryObj.value);
break;
case "#recent#":
// 发送网络请求,获取受监控的受理单(最近30天)
result = await applyBasicInfoSjkRecentService(queryObj.value);
break;
case "#overdue#":
// 发送网络请求,获取受监控的受理单(已超期)
result = await applyBasicInfoSjkOverdueService(queryObj.value);
break;
default:
// 发送网络请求,获取受监控的受理单(树节点)
result = await applyBasicInfoSjkByQueryDTOService(queryObj.value);
break;
}
}
// 通过查询数据,获取受理列表数据
else {
queryObj.value.acceptType = currentNode.value?.value;
// 发送网络请求,获取受监控的受理列表数据(有状态描述)
result = await applyBasicInfoYztByQueryDTOService(queryObj.value);
}
applyBasicInfoListData.value = result.data.rows;
};
function clearData() {
applyBasicInfoListData.value = [];
applyListData.value = [];
jjdListData.value = [];
testListData.value = [];
reportListData.value = [];
}
// 发送网络请求,获取子受理列表数据
const postApplyListDataService = async () => {
if (!currentTableRow.value) return;
let result = await applyDataByOuterApplyIdService(currentTableRow.value?.outerApplyId);
applyListData.value = result.data;
};
// 发送网络请求,获取交接单列表数据
const postJJDListDataService = async () => {
if (!currentTableRow.value) return;
let result = await jjdDataByOuterApplyIdService(currentTableRow.value?.outerApplyId);
jjdListData.value = result.data;
};
// 发送网络请求,获取检验列表数据
const postTestListDataService = async () => {
if (!currentTableRow.value) return;
let result = await testDataByOuterApplyIdService(currentTableRow.value?.outerApplyId);
testListData.value = result.data;
};
// 发送网络请求,获取报告列表数据
const postReportListDataService = async () => {
if (!currentTableRow.value) return;
let result = await reportDataByOuterApplyIdService(currentTableRow.value?.outerApplyId);
reportListData.value = result.data;
};
// 暴露供给外部访问的属性和方法
return {
acceptTypeListData,
treeData,
applyBasicInfoListData,
queryObj,
currentTableRow,
currentNode,
activeTabName,
applyListData,
jjdListData,
testListData,
reportListData,
postAcceptTypeListDataService,
postApplyBasicInfoListDataService,
postApplyListDataService,
postJJDListDataService,
postTestListDataService,
postReportListDataService,
clearData
};
});
// 默认导出 store 实例
export default useProgressCtrlStore;
8、数据邦定(VM:视图+数据模型)
8.1、实例组件(父组件) progress-ctrl.vue
<script setup lang="ts" name="progress-ctrl">
import ProgressCtrlTree from './progress-ctrl/comps/progress-ctrl-tree.vue';
import ProgressCtrlSearch from './progress-ctrl/comps/progress-ctrl-search.vue';
import ProgressCtrlMain from './progress-ctrl/comps/progress-ctrl-main.vue';
import ProgressCtrlTabs from './progress-ctrl/comps/progress-ctrl-tabs.vue';
import ProgressCtrlPagination from './progress-ctrl/comps/progress-ctrl-pagination.vue';
</script>
<template>
<el-container class="layout-container">
<el-aside class="aside">
<!-- 左侧受理类别节点树 -->
<ProgressCtrlTree />
</el-aside>
<el-container class="container">
<el-header class="header">
<!-- 查询栏 -->
<ProgressCtrlSearch />
</el-header>
<el-main class="main">
<!-- main -->
<ProgressCtrlMain />
</el-main>
<el-footer class="footer">
<!-- 选项卡 -->
<ProgressCtrlTabs />
<!-- 分页 -->
<ProgressCtrlPagination />
</el-footer>
</el-container>
</el-container>
</template>
<style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.layout-container {
height: 100%;
border: 1px solid #ddd;
.aside {
width: 150px;
}
.container {
border-left: 1px solid #ddd;
.header {
height: auto;
min-height: 40px;
border-bottom: 1px solid #ddd;
}
.main {
min-height: 100px;
}
.footer {
height: auto;
min-height: 40px;
border-top: 1px solid #ddd;
}
}
}
</style>
8.2、子组件(左侧树) progress-ctrl-tree.vue
<script setup lang="ts" name="progress-ctrl-tree">
import { onMounted, ref, type Component, markRaw } from "vue";
import type { ITree } from "@/views/query/progress-ctrl/types";
import useProgressCtrlStore from "@/views/query/progress-ctrl/stores";
import { Clock } from "@element-plus/icons-vue";
const progressCtrlStore = useProgressCtrlStore();
onMounted(async () => {
// 获取受理类别列表数据
await progressCtrlStore.postAcceptTypeListDataService();
// 创建树形结构数据
createTreeData(progressCtrlStore.acceptTypeListData);
});
// 创建树形结构数据
function createTreeData(data: any[]) {
let childrenData = data.map((item) => {
let { label = "", value = "" } = { ...item };
return {
label: label as string,
value: value as string
};
});
progressCtrlStore.treeData.push({
label: "受理类别",
value: "#ALL#",
children: childrenData
});
progressCtrlStore.treeData.push({
label: "今天内到期",
// 组件本身不需要响应式处理,使用 markRaw 包裹组件,标记组件为非响应式
icon: markRaw(Clock),
iconColor: "blue",
value: "#today#"
});
progressCtrlStore.treeData.push({
label: "三天内到期",
// 组件本身不需要响应式处理,使用 markRaw 包裹组件,标记组件为非响应式
icon: markRaw(Clock),
iconColor: "orange",
value: "#3day#"
});
progressCtrlStore.treeData.push({
label: "最近30天",
// 组件本身不需要响应式处理,使用 markRaw 包裹组件,标记组件为非响应式
icon: markRaw(Clock),
iconColor: "green",
value: "#recent#"
});
progressCtrlStore.treeData.push({
label: "已超期",
// 组件本身不需要响应式处理,使用 markRaw 包裹组件,标记组件为非响应式
icon: markRaw(Clock),
iconColor: "red",
value: "#overdue#"
});
}
// 点击树节点
const onTreeNodeClick = async (node: ITree) => {
// 防止在同一树节点上点击多次,产生多次请求
if (progressCtrlStore.currentNode && progressCtrlStore.currentNode.value === node.value) {
return;
}
progressCtrlStore.currentNode = node;
// 获取受监控的受理单
await progressCtrlStore.postApplyBasicInfoListDataService();
};
</script>
<template>
<div class="tree">
<el-scrollbar>
<el-tree
:data="progressCtrlStore.treeData"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:indent="0"
@node-click="onTreeNodeClick">
</el-tree>
</el-scrollbar>
</div>
</template>
<style scoped lang="scss"></style>
8.3、子组件(查询栏) progress-ctrl-search.vue
<script setup lang="ts" name="progress-ctrl-search">
import useProgressCtrlStore from "@/views/query/progress-ctrl/stores";
import { ref } from "vue";
const progressCtrlStore = useProgressCtrlStore();
// 更多筛选
const moreFilterVisible = ref(false);
// 受理日期范围
const acceptDate = ref<string[]>([]);
// 查询
import { ElMessage } from "element-plus";
const onQueryClick = async () => {
progressCtrlStore.queryObj.acceptDateBegin = "";
progressCtrlStore.queryObj.acceptDateEnd = "";
// 从范围日期组件中,获取开始日期和结束日期
if (acceptDate.value.length) {
progressCtrlStore.queryObj.acceptDateBegin = acceptDate.value[0];
progressCtrlStore.queryObj.acceptDateEnd = acceptDate.value[1];
}
// 检查查询参数
if (
!progressCtrlStore.queryObj.acceptDateBegin &&
!progressCtrlStore.queryObj.acceptDateEnd &&
!progressCtrlStore.queryObj.outerApplyId &&
!progressCtrlStore.queryObj.sjdwName
) {
ElMessage.warning("请输入查询条件!");
return;
}
// 获取受监控的受理列表数据
await progressCtrlStore.postApplyBasicInfoListDataService();
};
// 重置
const onResetClick = () => {
// 清空日期选择器
acceptDate.value.length = 0;
// 清空查询条件
progressCtrlStore.queryObj.outerApplyId = "";
progressCtrlStore.queryObj.sjdwName = "";
progressCtrlStore.queryObj.acceptDateBegin = "";
progressCtrlStore.queryObj.acceptDateEnd = "";
// 清空数据
progressCtrlStore.clearData();
};
// 更多筛选
const onMoreFilterClick = () => {
moreFilterVisible.value = !moreFilterVisible.value;
};
</script>
<template>
<div class="search">
<el-form class="header-form" v-model="progressCtrlStore.queryObj" :inline="true" :label-width="90">
<el-form-item class="header-form-item-330" label="受理编号:">
<el-input v-model="progressCtrlStore.queryObj.outerApplyId" clearable />
</el-form-item>
<el-form-item class="header-form-item-auto">
<el-button class="btn-same-width" type="primary" plain @click="onQueryClick">查询</el-button>
<el-button class="btn-same-width" type="primary" plain @click="onResetClick">重置</el-button>
<el-button class="btn-same-width" type="primary" plain @click="onMoreFilterClick">更多筛选</el-button>
</el-form-item>
</el-form>
<el-form
class="header-form"
v-model="progressCtrlStore.queryObj"
:inline="true"
:label-width="90"
v-show="moreFilterVisible">
<el-form-item class="header-form-item-330" label="受理日期:">
<el-date-picker
v-model="acceptDate"
type="daterange"
start-placeholder="开始日期"
range-separator="至"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD">
</el-date-picker>
</el-form-item>
<el-form-item class="header-form-item-330" label="受检单位:">
<el-input v-model="progressCtrlStore.queryObj.sjdwName" clearable />
</el-form-item>
</el-form>
</div>
</template>
<style scoped lang="scss"></style>
8.4、子组件(主项数据) progress-ctrl-main.vue
<script setup lang="ts" name="progress-ctrl-main">
import useProgressCtrlStore from "../stores";
const progressCtrlStore = useProgressCtrlStore();
// 点击表格的行
const onTableRowClick = (row: any, column: any) => {
progressCtrlStore.currentTableRow = row;
};
</script>
<template>
<div class="main">
<el-table
ref="table"
:data="progressCtrlStore.applyBasicInfoListData"
:border="true"
highlight-current-row
style="width: 100%; height: 100%"
@row-click="onTableRowClick">
<el-table-column
prop="outerApplyId"
label="受理编号"
width="120"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="stateDescription"
label="状态"
width="150"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptDate"
label="受理日期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="reportDate"
label="报告限期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="verifyTypeName"
label="检验类别"
width="120"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptTypeName"
label="受理类别"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptGroupName"
label="受理组别"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="sjdwName"
label="受检单位"
width="300"
header-align="center"
sortable
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
8.5、子组件(选项卡) progress-ctrl-tabs.vue
<script setup lang="ts" name="progress-ctrl-tabs">
import ProgressCtrlTabsApply from './progress-ctrl-tabs-apply.vue';
import ProgressCtrlTabsJJD from './progress-ctrl-tabs-jjd.vue';
import ProgressCtrlTabsTest from './progress-ctrl-tabs-test.vue';
import ProgressCtrlTabsReport from './progress-ctrl-tabs-report.vue';
import useProgressCtrlStore from '../stores';
import type { TabsPaneContext } from "element-plus";
const progressCtrlStore = useProgressCtrlStore();
// 切换tab页
const onTabClick = async (tab: TabsPaneContext, event: Event) => {
progressCtrlStore.activeTabName = tab.paneName as string;
};
</script>
<template>
<div class="tabs">
<el-tabs :model-value="progressCtrlStore.activeTabName" type="border-card" @tab-click="onTabClick">
<el-tab-pane label="受理信息" name="apply">
<ProgressCtrlTabsApply />
</el-tab-pane>
<el-tab-pane label="交接信息" name="jjd">
<ProgressCtrlTabsJJD />
</el-tab-pane>
<el-tab-pane label="检测信息" name="test">
<ProgressCtrlTabsTest />
</el-tab-pane>
<el-tab-pane label="报告信息" name="report">
<ProgressCtrlTabsReport />
</el-tab-pane>
</el-tabs>
</div>
</template>
<style scoped lang="scss"></style>
8.6、孙组件(选项卡的子组件-受理信息)progress-ctrl-tabs-apply.vue
<script setup lang="ts" name="progress-ctrl-tabs-apply">
import useProgressCtrlStore from "../stores";
import { computed, watch } from "vue";
const progressCtrlStore = useProgressCtrlStore();
const tableData = computed(() => progressCtrlStore.applyListData);
watch(
() => progressCtrlStore.currentTableRow,
async () => {
if (progressCtrlStore.activeTabName === "apply") {
await progressCtrlStore.postApplyListDataService();
}
}
);
watch(
() => progressCtrlStore.activeTabName,
async () => {
if (progressCtrlStore.activeTabName === "apply") {
await progressCtrlStore.postApplyListDataService();
}
}
);
</script>
<template>
<div class="tabs-apply">
<el-table ref="applyTable" :data="tableData" :border="true" highlight-current-row style="width: 100%; height: 100%">
<el-table-column
prop="applyId"
label="子受理编号"
width="120"
fixed="left"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="acceptDate"
label="受理日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="acceptSmallTypeName"
label="受理小类"
width="150"
header-align="center"
show-overflow-tooltip />
<el-table-column prop="sampleKind" label="检材类别" width="150" header-align="center" show-overflow-tooltip />
<el-table-column prop="keepCondition" label="描述信息" width="150" header-align="center" show-overflow-tooltip />
<el-table-column
prop="acceptPersonName"
label="受理人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
8.7、孙组件(选项卡的子组件-交接信息)progress-ctrl-tabs-jjd.vue
<script setup lang="ts" name="progress-ctrl-tabs-jjd">
import useProgressCtrlStore from "../stores";
import { computed, watch } from "vue";
const progressCtrlStore = useProgressCtrlStore();
const tableData = computed(() => progressCtrlStore.jjdListData);
watch(
() => progressCtrlStore.currentTableRow,
async () => {
if (progressCtrlStore.activeTabName === "jjd") {
await progressCtrlStore.postJJDListDataService();
}
}
);
watch(
() => progressCtrlStore.activeTabName,
async () => {
if (progressCtrlStore.activeTabName === "jjd") {
await progressCtrlStore.postJJDListDataService();
}
}
);
</script>
<template>
<div class="tabs-jjd">
<el-table ref="jjdTable" :data="tableData" :border="true" highlight-current-row style="width: 100%; height: 100%">
<el-table-column
prop="blPersonName"
label="派样人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column prop="submitTime" label="派样时间" width="200" header-align="center" show-overflow-tooltip />
<el-table-column
prop="checkGroupName"
label="检验组别"
width="150"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="receivePersonName"
label="接样人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column prop="receiveTime" label="接样时间" width="200" header-align="center" show-overflow-tooltip />
<el-table-column
prop="deadlineTime"
label="交接限期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
8.8、孙组件(选项卡的子组件-检测信息)progress-ctrl-tabs-test.vue
<script setup lang="ts" name="progress-ctrl-tabs-test">
import useProgressCtrlStore from "../stores";
import { computed, watch } from "vue";
const progressCtrlStore = useProgressCtrlStore();
const tableData = computed(() => progressCtrlStore.testListData);
watch(
() => progressCtrlStore.currentTableRow,
async () => {
if (progressCtrlStore.activeTabName === "test") {
await progressCtrlStore.postTestListDataService();
}
}
);
watch(
() => progressCtrlStore.activeTabName,
async () => {
if (progressCtrlStore.activeTabName === "test") {
await progressCtrlStore.postTestListDataService();
}
}
);
</script>
<template>
<div class="tabs-test">
<el-table ref="testTable" :data="tableData" :border="true" highlight-current-row style="width: 100%; height: 100%">
<el-table-column
prop="sampleNo"
label="样品编号"
width="120"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column
prop="sampleName"
label="样品名称"
width="150"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column
prop="itemName"
label="检验项目"
width="150"
header-align="center"
fixed
sortable
show-overflow-tooltip />
<el-table-column prop="result" label="结果" width="120" header-align="center" sortable show-overflow-tooltip />
<el-table-column prop="conclusion" label="结论" width="80" header-align="center" sortable show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
<el-table-column
prop="standardScript"
label="标准值"
width="120"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column prop="unit" label="单位" width="100" header-align="center" sortable show-overflow-tooltip />
<el-table-column
prop="ffbzId"
label="检测标准编号"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="ffbzName"
label="检测标准名称"
width="300"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="verifyMethod"
label="检验方法"
width="150"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column prop="person" label="检验员" width="100" header-align="center" sortable show-overflow-tooltip />
<el-table-column
prop="allotTime"
label="分派时间"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="submitTime"
label="检测时间"
width="200"
header-align="center"
sortable
show-overflow-tooltip />
<el-table-column
prop="deadlineDate"
label="检测限期"
width="110"
header-align="center"
:align="`center`"
sortable
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
8.9、孙组件(选项卡的子组件-报告信息)progress-ctrl-tabs-report.vue
<script setup lang="ts" name="progress-ctrl-tabs-report">
import useProgressCtrlStore from "../stores";
import { watch } from "vue";
import { storeToRefs } from "pinia";
const progressCtrlStore = useProgressCtrlStore();
const { reportListData } = storeToRefs(progressCtrlStore);
const tableData = reportListData.value;
watch(
() => progressCtrlStore.currentTableRow,
async () => {
if (progressCtrlStore.activeTabName === "report") {
await progressCtrlStore.postReportListDataService();
}
}
);
watch(
() => progressCtrlStore.activeTabName,
async () => {
if (progressCtrlStore.activeTabName === "report") {
await progressCtrlStore.postReportListDataService();
}
}
);
</script>
<template>
<div class="tabs-report">
<el-table
ref="reportTable"
:data="tableData"
:border="true"
highlight-current-row
style="width: 100%; height: 100%">
<el-table-column
prop="deptName"
label="所属部门"
width="150"
header-align="center"
fixed="left"
sortable
show-overflow-tooltip />
<el-table-column
prop="type"
label="类型"
width="100"
header-align="center"
:align="`center`"
fixed="left"
sortable
show-overflow-tooltip
:formatter="(row: any) => { return row.type === 1 ? `检验报告` : `评价报告` }" />
<el-table-column
prop="reportTypeName"
label="报告类型"
width="200"
header-align="center"
fixed="left"
sortable
show-overflow-tooltip />
<el-table-column
prop="hbPerson"
label="编制人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hbDate"
label="编制日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hdPerson"
label="核对人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hdDate"
label="核对日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hfPerson"
label="签发人"
width="100"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="hfDate"
label="签发日期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="reportDate"
label="报告限期"
width="110"
header-align="center"
:align="`center`"
show-overflow-tooltip />
<el-table-column
prop="state"
label="状态"
width="100"
header-align="center"
:align="`center`"
fixed="right"
show-overflow-tooltip />
</el-table>
</div>
</template>
<style scoped lang="scss"></style>
8.10、子组件(分页) progress-ctrl-pagination.vue
<script setup lang="ts" name="progress-ctrl-pagination">
import useProgressCtrlStore from "../stores";
const progressCtrlStore = useProgressCtrlStore();
// 改变页码、显示数量,重新获取数据
const onPageOrSizeChange = async (currentPage: number, pageSize: number) => {
progressCtrlStore.queryObj.pageHelper.page = currentPage;
progressCtrlStore.queryObj.pageHelper.size = pageSize;
// 获取受理列表数据
await progressCtrlStore.postApplyBasicInfoSjkByQueryDTOListDataService();
};
</script>
<template>
<div class="pagination">
<el-pagination
:page-sizes="[5]"
v-model:page-size="progressCtrlStore.queryObj.pageHelper.size"
v-model:current-page="progressCtrlStore.queryObj.pageHelper.page"
background
layout="total, prev, pager, next"
:small="true"
@change="onPageOrSizeChange" />
</div>
</template>
<style scoped lang="scss"></style>
运行效果:
9、设置样式
9.1、子组件(左侧树) progress-ctrl-tree.vue
<template>
<div class="tree">
<el-scrollbar>
<el-tree
:data="progressCtrlStore.treeData"
:default-expand-all="true"
:highlight-current="true"
:expand-on-click-node="false"
:indent="0"
@node-click="onTreeNodeClick">
<!-- 自定义节点内容,点击的节点字体变色加粗 -->
<!-- 动态样式:通过<template #default>插槽自定义节点内容,使用:style绑定根据当前选择的节点值currentNode.value动态设置color和fontWeight -->
<template #default="{ node, data }">
<!-- 使用动态组件,添加图标,使用:style动态样式,设置图标大小和颜色 -->
<component
:is="data.icon"
:style="{ width: `18px`, height: `18px`, color: data.iconColor, marginLeft: `5px` }" />
<span
:style="{
color: progressCtrlStore.currentNode?.value === data.value ? data.iconColor ?? `#409EFF` : `#606266`,
fontWeight: progressCtrlStore.currentNode?.value === data.value ? `bold` : `normal`,
marginLeft: `5px`
}">
{{ node.label }}
</span>
</template>
</el-tree>
</el-scrollbar>
</div>
</template>
<style scoped lang="scss">
.tree {
width: 100%;
height: 100%;
overflow: auto;
}
// 树节点
::v-deep .el-tree-node__content {
height: 32px;
}
// 树节点展开图标
:deep .el-tree-node__content > .el-tree-node__expand-icon {
padding: 0;
}
</style>
运行效果:
9.2、子组件(查询栏) progress-ctrl-search.vue
<style scoped lang="scss">
* {
margin: 0;
padding: 0;
}
.search {
.header-form {
margin: 10px 0;
.header-form-item-330 {
width: 330px;
}
.header-form-item-auto {
width: auto;
}
.btn-same-width {
width: 100px;
margin: 0 15px 0 0;
}
}
}
</style>
运行效果:
9.3、子组件(主项数据) progress-ctrl-main.vue
<style scoped lang="scss">
.main {
width: 100%;
height: 100%;
}
</style>
运行效果:
9.4、子组件(选项卡) progress-ctrl-tabs.vue
<style scoped lang="scss">
.tabs {
.el-tabs {
border: none;
// border-bottom: 1px solid #ddd;
// 高度需要减去上下边框宽度
// height: calc(100% - 2px);
// 使用 box-sizing: border-box; 就不用考虑边框宽度
box-sizing: border-box;
height: 340px;
:deep .el-tabs__content {
margin: 0;
padding: 0;
}
.el-tab-pane {
height: 300px;
}
}
}
</style>
运行效果:
9.5、 子组件(分页) progress-ctrl-pagination.vue
<style scoped lang="scss">
.pagination {
padding: 2px 5px;
display: flex;
justify-content: center; /* 水平居中 */
}
</style>
运行效果:
10、加载数据的最终效果
11、组件结构图
12、视图数据关系图(VM关系图:视图+数据模型)