vue3中的一些便捷写法记录

发布时间:2024年01月17日

1、子组件触发父组件事件

<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>

2、公共Hooks函数封装(强烈建议好好看下,使用起来很方便)

2.1、table公共方法封装(table列表、分页、搜索、删除、状态修改)
// 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>
2.2、form表单公共方法封装(表单新增、修改、提交)
// 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>

文章来源:https://blog.csdn.net/FF_XM/article/details/135595980
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。