在早期的随笔就介绍过 ,把常规页面的内容拆分为几个不同的组件 ,如普通的页面 ,包括列表查询 、详细资料查看 、新增资料、编辑资料 、导入资料等页面场景 ,这些内容相对比较独立 ,而有一定的代码量 ,本篇随笔介绍基于Vue3+Typescript+Setup语法方式 ,来拆分页面模块内容为组件 ,实现分而治之的处理 。
1 、页面模块组件的划分
我们先来了解下常规页面的内容的整体界面布局 ,它包含常规的列表界面,新增 、编辑 、查看 、导入等界面 ,除了列表页面 ,其他内容以弹出层对话框的方式进行处理,如下界面示意图所示 。
这些页面也可以放在一个大页面里面进行处理 ,逻辑代码也可以整合一起进行管理 ,大致的页面布局如下所示。
我们看到,如果这样放置页面的模块内容 ,如果界面控件比较多的话 ,页面代码会急剧增加 ,而且由于代码太多 ,管理起来也非常不方便 ,最好的方式 ,还是拆分进行组件化的管理比较好 。
我们以一个测试用户的页面为例来介绍 ,测试用户列表界面如下所示 。
其中也包括了查看 、编辑 、新增 、导入等界面 ,我们后面逐一介绍 。
2 、页面组件的开发
我们前面介绍到 ,整个页面包含了列表界面,新增、编辑 、查看 、导入等界面 ,除了列表页面 ,其他内容以弹出层对话框的方式进行处理 。
我们分别创建index.vue代表主列表页面内容,view代表查看页面、edit代表新增或者编辑页面(两个页面类似 ,因此整合一起更精简) ,import代表导入页面,一起放在一个testuser页面目录中 ,作为一个模块页面 。
我们先以view.vue查看页面为例进行介绍 ,它是一个查看明细的界面 ,因此也是一个弹出对话框页面 ,我们把它的代码处理如下所示 。
其他的js代码采用tyepscript语法 ,我们把它放在
<script setup lang="ts">
//逻辑代码
</script>
为了把组件的方法公开 ,我们先定义一个接口类型 ,便于引用的时候 ,代码进行约束提示 。
<script setup lang="ts">
//组件的接口类型
export interface ExposeViewType {
show(id?: string | number): Function;
}
............
//显示窗口
const show = (id: string | number) => {
//处理代码
};
//暴露组件属性和方法
defineExpose({
show
});
</script>
这样我们在父页面中使用子模块组件的时候 ,就可以通过公开的方法进行调用了 。
//父页面index.vue
<!--查看详细组件界面-->
<view-data ref="viewRef" />
<!--新增 、编辑组件界面-->
<edit-data ref="editRef" @submit="saveEdit" />
<!--模板导入信息-->
<import-data ref="importRef" @finish="finishImport" />
</div>
</template>
<script setup lang="ts">
........
import ViewData, { ExposeViewType } from "./view.vue";
import EditData from "./edit.vue";
import ImportData from "./import.vue";
......
// 显示查看对话框处理
const viewRef = ref<ExposeViewType | null>(); //查看表单引用
//const viewRef = ref<InstanceType<typeof ViewData>>();
function showView(id) {
if (isEmpty(id)) {
warnMessage("请选择编辑的记录!");
return;
}
viewRef.value.show(id);
}
我们通过const viewRef = ref<ExposeViewType | null>(); 就可以获得组件类型的引用,然后调用组件的接口方法即可 。
viewRef.value.show(id);
在查看页面的组件定义模板中 ,我们大致代码如下所示。
声明了对应的引用 ,以及表单对象,以及提供相应的方法进行处理 ,这些内容对父页面封装了细节 。
<script setup lang="ts">
//组件的接口类型
export interface ExposeViewType {
show(id?: string | number): Function;
}
import { reactive, ref, onMounted, watch, computed, nextTick } from "vue";
import { FormInstance} from "element-plus";
defineOptions({ name: "ViewData" }); //定义组件名称
//声明Props的接口类型
interface Props {
visible?: boolean; // 是否显示
id?: string | number; // 接受外部v-model传入的id值
}
//使用默认值定义Props
const props = withDefaults(defineProps<Props>(), {
visible: false,
value: null
});
//声明组件事件
interface Emits {
(e: "update:id", id: string | number): void;
(e: "update:visible", visible: boolean): void;
(e: "close"): void;
//(e: "submit"): void;
}
//定义组件事件
const emit = defineEmits<Emits>();
我们定义了组件名称 、组件的Props属性、以及Emit事件 ,Emit事件如果想简单化一点,也可以直接使用名称即可 。
例如 ,有时候我们会直接声明名称进行定义Emit ,如下所示。
//定义触发事件
const emit = defineEmits(["error", "success", "remove", "change"]);
显示页面的方法 ,是公开给父页面进行调用的 ,因此接收一个id参数 ,并根据id值 ,利用axios访问远端API接口获取数据 ,进行赋值显示即可 。
//显示窗口
const show = (id: string | number) => {
if (!isNullOrUnDef(id)) {
testuser.Get(id).then(data => {
// console.log(data);
Object.assign(viewForm, data);
isVisible.value = true; //显示对话框
});
}
};
关于axios访问远端API接口的类实现 ,可以参考随笔《基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装 ,实现对后端API数据的访问和基类的统一封装处理》进行了解 。
这里的TestUser的APi类,继承自基类BaseApi ,因此拥有常规的处理方法。
最后 ,查看明细的窗口关闭后,需要设置一下窗口的相关标记 。
let isVisible = ref(false); //是否显示查看对话框
function closeDialog(formEl: FormInstance | undefined) {
// 关闭常规 添加 、编辑 、查看 、导入等窗口处理
isVisible.value = false;
if (!formEl) {
formEl.resetFields();
}
emit("close"); //关闭
}
由于窗口内部的显示标记和Prop属性的关系 ,我们需要处理一下 ,对他们进行Watch监控,并处理值的变化 。
//监控某些值的变化 ,进行处理
watch(
() => props.visible,
newValue => {
isVisible.value = newValue;
emit("update:visible", newValue);
}
);
watch(
() => isVisible,
newValue => {
// console.log(newValue);
emit("update:visible", newValue.value);
}
);
表单的form对象 ,我们根据后端数据结构进行生成即可 。
const viewRef = ref<FormInstance>(); //表单引用
// 表单属性定义
let viewForm = reactive({
id: "",
name: "",
sex: "",
birthDate: "",
nationality: "",
education: "",
marriage: "",
star: "",
height: "",
weight: "",
.................
createTime: "",
extensionData: "" // 扩展数据
});
有了这些处理 ,我们查看详细的页面弹出和关闭就正常了 。页面效果如下所示 。
新建 、编辑页面也是类似 ,只是在保存数据后触发相关的事件 ,让父页面进行更新显示即可 。
<!--查看详细组件界面-->
<view-data ref="viewRef" />
<!--新增 、编辑组件界面-->
<edit-data ref="editRef" @submit="saveEdit" />
<!--模板导入信息-->
<import-data ref="importRef" @finish="finishImport" />
如编辑 、新增页面的父组件页面 ,也是只需关注他的打开和完成处理即可 。
//新增 、编辑表单引用
const editRef = ref<ExposeViewType | null>();
//显示新增对话框
function showAdd() {
editRef.value.show();
}
// 显示编辑对话框
function showEdit(id) {
if (isEmpty(id)) {
warnMessage("请选择编辑的记录!");
return;
}
editRef.value.show(id);
}
//新增/更新后刷新
function saveEdit() {
getlist();
}
而在编辑信息的组件页面内部 ,就需要判断是更新还是插入记录的处理 ,完成后再抛出事件即可 。
// 保存数据处理
async function submitData() {
var formEl = editRef.value;
if (!formEl) return;
// console.log(editForm);
await formEl.validate(async valid => {
if (valid) {
//验证成功 ,执行下面方法
var result = false;
if (isAdd.value) {
result = await testuser.Create(editForm); //新增保存
} else {
result = await testuser.Update(editForm); //编辑保存
}
if (result) {
successMessage("操作成功!"); // 提示信息
emit("submit"); // 提示刷新数据
closeDialog(formEl); // 重置窗口状态
} else {
errorMessage("操作失败");
}
}
})
导入数据页面,大体也是类似 ,不过由于涉及到更多的是对导入处理的规则处理 ,需要封装一下相关的组件功能,因此后面再独立介绍细节实现 。
系列文章:
《基于SqlSugar的开发框架的循序渐进介绍(1)--框架基础类的设计和使用》
《基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理》
《基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发》
《基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理》
《基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转》
《基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口》
《基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传》
《基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录》
《基于SqlSugar的开发框架循序渐进介绍(9)-- 结合Winform控件实现字段的权限控制》
《基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装 ,实现对后端API数据的访问和基类的统一封装处理》
《基于SqlSugar的开发框架循序渐进介绍(11)-- 使用TypeScript和Vue3的Setup语法糖编写页面和组件的总结》
声明:本站所有文章 ,如无特殊说明或标注,均为本站原创发布。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。