<template>
<div class="father">
<h3>父组件,</h3>
<h4>儿子给的玩具:{{ toy }}</h4>
<Child :car="car" @getToy="getToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from "vue";
// 数据
const toy = ref()
// 方法
function getToy(value:string){
toy.value = value
}
</script>
<template>
<div class="child">
<h3>子组件</h3>
<h4>我的玩具:{{ toy }}</h4>
<button @click="handleToy">玩具给父亲</button>
</div>
</template>
<script setup lang="ts" name="Child">
import { ref } from "vue";
const toy = ref('奥特曼')
const emit = defineEmits(["getToy"]);
const handleToy = (type: number, orgId: number) => {
emit("getToy", toy);
};
</script>
// composables/useCommon.js文件内
import { ref, reactive, computed } from "vue";
import { message } from "@/composables/util.js";
import { ElMessageBox } from "element-plus";
// 公共table接口
interface TableOptions {
// 搜索参数
searchForm?: object;
// api文件内的接口
getList: Function;
// 获取数据后对数据进行处理的回调
// 注:(如果需要对列表的回调数据进行特殊处理就需要传,只是列表渲染就不需要传这个)
onGetListSuccess?: Function;
// 启用
enableStatus?: Function;
// 禁用
disableStatus?: Function;
// 删除
delete?: Function;
}
// 复选框接口
interface idOptions {
id: number;
}
// 公共逻辑拆分(分页+列表+删除+搜索+修改状态)
// opt参数 必传:getList(获取列表数据的接口)
// 选传:searchForm(搜索参数)、updateStatus(修改状态的接口)、
// delete(删除状态的接口)、onGetListSuccess(获取数据后对数据进行处理的回调)
export function useInitTable(opt: TableOptions) {
const tableData = ref([]);
const loading = ref(false);
// 分页参数
const currentPage = ref(1);
const limit = ref(10);
const total = ref(0);
// 搜索
let searchForm = null;
let resetSearchForm = null;
// 搜索参数可能会有多个,需要使用组件传递对应搜索参数,公共组件动态获取
if (opt.searchForm) {
searchForm = reactive({ ...opt.searchForm });
resetSearchForm = () => {
// opt.searchForm的格式searchForm: {keyword: ''},使用组件传的值必定为空,循环给searchForm初始化值
for (const key in opt.searchForm) {
searchForm[key] = opt.searchForm[key];
}
getData();
};
}
// 分页
const handleSizeChange = (pageSize: number) => {
searchForm.pageSize = pageSize;
getData();
};
const handleCurrentChange = (currentPage: number) => {
console.log(currentPage, "currentPage");
searchForm.pageNum = currentPage;
getData();
};
const getData = async (p = null) => {
// p为当前页码数
if (typeof p == "number") {
currentPage.value = p;
}
loading.value = true;
// const res = await opt.getList(currentPage.value, searchForm);
const queryParams = {};
for (const key in searchForm) {
if (searchForm[key]) {
queryParams[key] = searchForm[key];
}
}
const res = await opt.getList(queryParams);
if (res.code === 200) {
// 部分组件需要返回特殊的参数,如:每个item中都要返回一个checked属性,那么将执行使用组件传来的逻辑,返回对应参数
if (opt.onGetListSuccess && typeof opt.onGetListSuccess == "function") {
opt.onGetListSuccess(res);
} else {
console.log(res, "res");
tableData.value = res.data.list;
total.value = res.data.total;
}
}
loading.value = false;
};
getData();
// 启用
const handleEnable = async (id: number) => {
try {
await opt.enableStatus(id);
message("启用成功", { type: "success" });
getData();
} catch (err) {
console.log(err, "err");
}
};
// 禁用
const handleDisable = async (id: number) => {
try {
await opt.disableStatus(id);
message("禁用成功", { type: "success" });
getData();
} catch (err) {
console.log(err, "err");
}
};
// 删除
const handleDelete = async (ids: object) => {
loading.value = true;
try {
// 弹出确认框
ElMessageBox.confirm("是否删除此字典类型?", "提示", {
distinguishCancelAndClose: true,
confirmButtonText: "确定",
cancelButtonText: "取消"
})
.then(async () => {
// 用户点击确定按钮的处理逻辑
await opt.delete({ ids }).then(res => {
if (res.code === 200) {
message("删除成功", { type: "success" });
getData();
return;
} else {
message(res.message, { type: "error" });
return;
}
});
})
.catch(() => {
// 用户点击取消按钮的处理逻辑
message("已取消操作", { type: "info" });
});
} catch (err) {
loading.value = false;
}
};
// 复选框多选选中id
const multiSelectionIds = ref([]);
const handleSelectionChange = (e: Array<object>) => {
const ids = e.map((o: idOptions) => {
return o.id;
});
multiSelectionIds.value = ids;
};
// 批量删除
// const multipleTableRef = ref(null);
const handleMultiDelete = () => {
if (!multiSelectionIds.value.length)
return message("请选择至少一个选项", { type: "info" });
try {
const params = {
ids: multiSelectionIds.value.join()
};
ElMessageBox.confirm("是否删除选中的字典类型?", "提示", {
distinguishCancelAndClose: true,
confirmButtonText: "确定",
cancelButtonText: "取消"
})
.then(async () => {
// 用户点击确定按钮的处理逻辑
await opt.delete(params).then(res => {
if (res.code === 200) {
message("删除成功", { type: "success" });
getData();
return;
} else {
message(res.message, { type: "error" });
return;
}
});
})
.catch(() => {
// 用户点击取消按钮的处理逻辑
message("已取消操作", { type: "info" });
});
} catch (err) {
console.log(err, "err");
}
};
// 批量修改状态
const handleMultiStatusChange = async (status: string) => {
if (!multiSelectionIds.value.length)
return message("请选择至少一个选项", { type: "info" });
try {
// await opt.updateStatus(multiSelectionIds.value, status);
console.log(status, "status");
message("修改状态成功", { type: "success" });
getData();
} catch (err) {
console.log(err, "err");
}
};
return {
searchForm,
resetSearchForm,
tableData,
limit,
loading,
total,
currentPage,
getData,
handleDisable,
handleEnable,
handleDelete,
handleSelectionChange,
handleMultiDelete,
handleMultiStatusChange,
handleSizeChange,
handleCurrentChange,
multiSelectionIds
};
}
使用table公共方法
<script setup lang="ts" name="EvaluateDetail">
// 公共table方法
import { useInitTable } from "@/composables/useCommon.js";
// table列表接口
import { getCategoryList } from "@/api/evaluate";
// 公共组件
import JmSearch from "@/components/JmSearch/index.vue";
import JmSearchItem from "@/components/JmSearchItem/index.vue";
import JmTable from "@/components/JmTable/index.vue";
import JmPagination from "@/components/JmPagination/index.vue";
// 搜索相关参数
const params = {
patientName: "",
userAccount: "",
supportWorkerName: "",
evaluateTime: [],
evaluateTimeStart: "",
evaluateTimeEnd: "",
pageNum: 1,
pageSize: 10,
sortOrder: "desc"
};
const searchLabelList = [
{
key: "patientName",
label: "就诊人姓名"
},
{
key: "userAccount",
label: "评价账号"
},
{
key: "supportWorkerName",
label: "护理人员"
}
];
// 选择时间
interface DateType {
[index: number]: string;
}
const handlePicker = (e: Array<DateType>) => {
if (e && e.length > 0) {
searchForm.evaluateTimeStart = e[0];
searchForm.evaluateTimeEnd = e[1];
} else {
searchForm.evaluateTimeStart = "";
searchForm.evaluateTimeEnd = "";
}
};
// 公共方法table使用
const {
tableData,
getData,
handleSizeChange,
handleCurrentChange,
resetSearchForm,
total,
searchForm
} = useInitTable({
searchForm: params,
// getCategoryList为table接口
getList: getCategoryList
});
const tableColumns = [
{ prop: "supportWorkerId", label: "户工编号", width: "130" },
{ prop: "userAccount", label: "评价账号", width: "180" },
{ prop: "patientName", label: "就诊人姓名", width: "180" },
{ prop: "supportWorkerName", label: "护理人员", width: "180" },
{ prop: "orgName", label: "所属机构", width: "260" },
{ prop: "servicerNo", label: "服务单编号", width: "200", slot: "order" },
{
prop: "comprehensiveScore",
label: "评价分值",
width: "130",
sortable: true
},
{ prop: "detail", label: "查看", width: "200", slot: "actions" },
{ prop: "evaluateTime", label: "评价时间", width: "200" }
];
</script>
<template>
<div>
<!-- 搜索 -->
<jm-search @search="getData" @reset="resetSearchForm" :model="searchForm">
<jm-search-item
v-for="(item, index) in searchLabelList"
:key="index"
:spanItem="5"
:label="item.label"
>
<el-input
v-model="searchForm[item.key]"
:placeholder="item.label"
clearable
/>
</jm-search-item>
<jm-search-item :spanItem="5" label="下单时间">
<el-date-picker
v-model="searchForm['evaluateTime']"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
@change="handlePicker"
/>
</jm-search-item>
</jm-search>
<jm-table
:initial-data="tableData"
:columns="tableColumns"
:showCheckbox="false"
:height="'418'"
/>
<!-- 分页组件 -->
<jm-pagination
@handleSizeChange="handleSizeChange"
@handleCurrentChange="handleCurrentChange"
:total="total"
/>
</div>
</template>
<style lang="scss" scoped></style>
// composables/useCommon.js文件内
import { ref, reactive, computed } from "vue";
import { message } from "@/composables/util.js";
import { ElMessageBox } from "element-plus";
// 公共表单接口
interface FormOptions {
// 表单参数
form: object;
rules?: object;
title?: string;
editId?: number;
currentPage?: number;
// 获取数据后对数据进行处理的回调
// 编辑获取表单详情后的钩子
// 注:页面中需要表单数据渲染的才需要传这个钩子,比如页面中有tag标签需要表单内容回显,但是不能直接用表单数据
beforeGetForm?: Function;
// 注:(如果需要对列表的回调数据进行特殊处理就需要传,只是列表渲染就不需要传这个)
beforeSubmit?: Function;
// 表单保存后的回调方法
afterSubmit?: Function;
// api文件内的接口
// 详情
detail?: Function;
// 修改
update?: Function;
// 新增
create?: Function;
getData?: Function;
}
// 复选框接口
interface idOptions {
id: number;
}
// 表单新增修改接口
interface FormDataOptions {
code: number;
data?: object;
}
// 表单(新增+修改+提交)逻辑拆分
// opt参数 必传:form(表单初始值)、
// 可选:title(表单标题)、currentPage(当前页:必须在useInitTable之后)、
// update(修改表单接口)、create(新增表单接口)
export function useInitForm(opt: FormOptions) {
const formRef = ref(null);
// 表单参数
const defaultForm = opt.form;
const form = reactive({});
// 表单规则
const rules = opt.rules || {};
// 修改id
const editId = ref(0);
// 弹框title
const drawerTitle = computed(() => {
console.log("editId.value", editId.value);
return editId.value ? "编辑" + opt.title : "新增" + opt.title;
});
// 重置表单
const resetForm = (row = {}) => {
if (formRef.value) formRef.value.clearValidate();
for (const key in defaultForm) {
form[key] = row[key];
}
};
// // 新增
// const handleCreate = () => {
// resetForm(defaultForm);
// };
// // 编辑
// const handleEdit = () => {
// resetForm(defaultForm);
// };
if (opt.editId) {
editId.value = opt.editId;
// 是否有获取详情钩子
if (opt.detail && typeof opt.detail == "function") {
opt.detail(editId.value).then((res: FormDataOptions) => {
if (res.code === 200) {
// 是否需要对form数据进行特殊处理 详情见beforeGetForm接口介绍
if (opt.beforeGetForm && typeof opt.beforeGetForm == "function") {
opt.beforeGetForm(res);
} else {
resetForm(res.data);
}
}
});
}
} else {
resetForm(defaultForm);
}
// 提交表单
const handleSubmit = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return false;
console.log(form, "form");
try {
// 将form赋值给body,传给回调,处理时间戳
let body = {};
if (opt.beforeSubmit && typeof opt.beforeSubmit == "function") {
body = opt.beforeSubmit({ ...form });
} else {
body = form;
}
const Fun = editId.value ? opt.update(body) : opt.create(body);
// 修改刷新当前页,新增刷新第一页
Fun.then((res: FormDataOptions) => {
console.log(res, "res提交表单");
if (res.code === 200) {
message(drawerTitle.value + "成功", { type: "success" });
} else {
message(drawerTitle.value + "失败", { type: "warning" });
}
opt.afterSubmit(res);
});
} catch (err) {
console.log(err);
}
});
};
return {
formRef,
form,
rules,
editId,
drawerTitle,
handleSubmit,
resetForm
};
}
使用form公共方法
<script setup lang="ts" name="AccountDetail">
import { ref, reactive } from "vue";
import { useInitForm } from "@/composables/useCommon.js";
import { getRoleLists } from "@/api/system";
import { RoleListsResult } from "@/api/type/system";
import {
userAccountManageSave,
userAccountResetPwd,
userAccountDetail
} from "@/api/business";
const props = defineProps({
pageType: {
type: Number,
default: 0
},
workerId: {
type: Number,
default: 0
}
});
const formInit = {
loginName: "",
password: 12345678,
roleId: "",
roleName: "",
userName: "",
userId: 0
};
const selectKey = ref("roleId");
if (props.workerId) {
selectKey.value = "roleName";
}
interface formOptions {
code: number;
}
// 公共form方法使用
const { formRef, rules, handleSubmit, form } = useInitForm({
editId: props.workerId,
title: "账号",
form: formInit,
// userAccountManageSave、userAccountResetPwd、userAccountDetail都是相应的接口
create: userAccountManageSave,
update: userAccountResetPwd,
detail: userAccountDetail,
rules: {
loginName: [{ required: true, message: "请输入账号名称", trigger: "blur" }],
password: [{ required: true, message: "请输入账号名称", trigger: "blur" }],
userName: [{ required: true, message: "请输入账号名称", trigger: "blur" }],
roleId: [{ required: true, message: "请选择账号角色", trigger: "blur" }]
},
afterSubmit: (res: formOptions) => {
if (res.code === 200) {
changePage(1);
}
}
});
const roleList = reactive([]);
const getRoleListFn = () => {
getRoleLists().then((res: RoleListsResult) => {
if (res.code === 200) {
const resInit = res.data.map(item => {
return {
roleId: item.roleId,
roleName: item.roleName
};
});
// 对象不能直接赋值,vue3检测不到
roleList.push(...resInit);
}
});
};
getRoleListFn();
const emit = defineEmits(["open"]);
const changePage = (type: number, id?: number) => {
// 实现打开页面的逻辑
const val = {
type,
id
};
emit("open", val);
};
// onBeforeMount(() => {
// getRoleListFn();
// });
</script>
<template>
<div>
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="90px"
:inline="false"
>
<el-form-item label="创建账号" prop="loginName">
<el-input
v-model="form['loginName']"
max="20"
placeholder="请输入创建账号"
:disabled="pageType === 3 ? true : false"
/>
</el-form-item>
<el-form-item label="设置密码" prop="password">
<el-input
v-model="form['password']"
max="60"
placeholder="请输入设置密码"
/>
</el-form-item>
<el-form-item label="账号姓名" prop="userName">
<el-input
v-model="form['userName']"
max="60"
placeholder="请输入账号姓名"
:disabled="pageType === 3 ? true : false"
/>
</el-form-item>
<el-form-item label="账号角色" prop="roleId" class="el-input">
<el-select
v-model="form[selectKey]"
placeholder="城市运营"
:disabled="pageType === 3 ? true : false"
>
<el-option
v-for="(item, index) in roleList"
:key="index"
:label="item.roleName"
:value="item.roleId"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">{{
props.workerId ? "编辑" : "创建"
}}</el-button>
<el-button @click="changePage(1)">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-form-item__content) {
flex: unset;
width: 300px;
}
</style>