Lyman
Lyman
Published on 2026-02-11 / 7 Visits
0
0

业务集成二开

业务集成二开

目录结构

业务功能目录结构如下:

# src/views/checkRule

├─index.ts                      // 打包入口文件
├─index.vue                     // 根组件
├─detail.vue                    // 详情组件(如果有)
├─package.json                  // 项目依赖包版本信息
├─pageConfig.js                 // 页面组件配置
├─_build                        // 构建配置目录
|   ├─tsconfig.json
|   ├─tsconfig.node.json
|   ├─typings.d.ts
|   └vite.config.ts
├─api                           // 接口目录
|  └index.js

业务功能目录在 /src/views目录下,不建议在 /src/views目录下创建目录嵌入业务功能。

_build为构建配置目录,下载[_build.zip],解压到业务功能目录中。

说明

说明框架提供的模块作用,以及如何使用。
提供代码示例,展示如何在开发业务功能时,使用框架提供的方法为二开人员提供二开入口。

hooks钩子

以npm包的方式提供业务功能包,本质上业务功能也是一个vue组件,因此使用props参数的方式接收外部属性,达到参数标准化的目的。

<script setup lang="ts">
import { modelProps } from "@imom/vue-2nd-kit";

const props = defineProps({
  ...modelProps,
  // ...其他属性
});

// ...其他代码
</script>
名称 类型 默认值 说明
customSetup ({ state, config }: CustomParamsType): CustomParamsType => ({ state, config }) ({state, config}) => ({state, config}) setup时期数据初始化
customBeforeMount async (_: CustomLifecycleParamsType): void => void 0 (_) => void 0 vue的beforeMount生命周期
customMounted async (_: CustomLifecycleParamsType): Promise\ => void 0 (_) => void 0 vue的mounted生命周期
customBeforeUnmount async (_: CustomLifecycleParamsType): Promise\ => void 0 (_) => void 0 vue的beforeUnmount生命周期
customUnmounted async (_: CustomLifecycleParamsType): Promise\ => void 0 (_) => void 0 vue的unmounted生命周期
beforeSearch async (_: ObjType): Promise\ => _ (_) => _ 调用搜索前
tableCellClick async (_e: RowDataType): HookReturnDataType => void 0 (_) => void 0 表格单元格点击事件
tableClickAction async (_rowData: RowDataType, _type: string): HookReturnDataType => void 0 (_) => void 0 表格操作列点击事件
beforeFetchList async (_params: ObjType): HookReturnDataType => _params (_) => _ 调用查询列表数据前
afterFetchList async (_responseBody: ResponseBodyType): HookReturnDataType => _responseBody (_) => _ 调用查询列表后
catchFechListError async (_error: any): HookReturnDataType => void 0 (_) => void 0 调用查询列表数据出现错误时
setButtonGroupAttrs ({ config, state: _state }: CustomParamsType): Reactive\ => config ({ config, _state }) => config 设置按钮组属性
buttonGroupClick async (_btnItem: ObjType, _: CustomParamsType): HookReturnDataType => void 0 (_, __) => void 0 按钮组点击事件
beforeFormSubmit async (_: CustomParamsType) => void 0 (_) => void 0 表单提交事件前
customApi (_type: string): HookReturnDataType| undefined => void 0 (_) => void 0 自定义api接口

customSetup

  • customSetup - setup时期数据初始化,用于预留给二开交付人员在页面 setup时自定义变量、配置。
    • 入参
      • _
        • state 全局变量状态,vue3的 reactive对象。
        • config 组件配置项,vue3的 reactive对象。
    • 返回
      • _
        • _.state 修改后的全局变量状态
        • _.config 修改后的组件配置项
<script setup>
import { modelProps, modelState, modelConfig } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
</script>

customBeforeMount

  • customBeforeMount-vue的beforeMount生命周期,用于预留给二开交付人员在 beforeMount生命周期
    • 入参
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 组件配置项,vue3的 reactive对象。
    • 返回(支持Promise)
<script setup>
import { onBeforeMount, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...

onBeforeMount(async () => {
  await props.customBeforeMount({ state: $state, config: $config, methods: $methods });
});
</script>

customMounted

  • customMounted-vue的mounted生命周期
    • 入参
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 组件配置项,vue3的 reactive对象。
    • 返回(支持Promise)
<script setup>
import { onMounted, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...

onMounted(async () => {
  await props.customMounted({ state: $state, config: $config, methods: $methods });
});
</script>

customBeforeUnmount

  • customBeforeUnmount-vue的beforeUnmount生命周期
    • 入参
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 组件配置项,vue3的 reactive对象。
    • 返回(支持Promise)
<script setup>
import { onBeforeUnmount, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...

onBeforeUnmount(async () => {
  await props.customBeforeUnmount({ state: $state, config: $config, methods: $methods });
});
</script>

customUnmounted

  • customUnmounted-vue的unmounted生命周期
    • 入参
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 组件配置项,vue3的 reactive对象。
    • 返回(支持Promise)
<script setup>
import { onUnmounted, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...

onUnmounted(async () => {
  await props.customUnmounted({ state: $state, config: $config, methods: $methods });
});
</script>

beforeSearch

$methods.searchForm.handleSearch已内置封装 beforeSearch,不使用默认 $methods.searchForm.handleSearch搜索逻辑需自行调用。

  • beforeSearch-调用搜索前。
    • 入参
      • params 查询参数。
    • 返回(支持Promise)
      • params 修改后的查询参数。

已集成到 $methods.searchForm.handleSearch

<template>
  <div>
    <SieSearchForm
      v-model="$state.searchFormData"
      :form-item-list="$config.searchForm.formItemList"
      :loading="$state.loading"
      :attrs="$config.searchForm.attrs"
      @search="handleSearch"></SieSearchForm>
    <!-- ...其余代码 -->
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieSearchForm } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// --------------------------------
/**
 * 点击查询表单的查询按钮事件
 */
const handleSearch = () => {
  // 使用默认逻辑加载数据
  $methods.searchForm.handleSearch($state.searchFormData);
};

/**
 * 点击查询表单的查询按钮事件
 */
const handleSearch = () => {
  // 不使用默认逻辑加载数据,预留钩子
  const isReturn = props.beforeSearch($state.searchFormData);
  if (isReturn === false) return;
  
  // ...搜索逻辑
};
// --------------------------------

// ...逻辑代码
</script>

tableCellClick

$methods.table.cellClick已内置封装 tableCellClick钩子,$methods.table.cellClick讲当前选中行 e.row赋给 $state.currentRow

  • tableCellClick-表格单元格点击事件。
    • 入参
      • e 点击单元格对象。
    • 返回(支持Promise)
      • false 可选,返回false不执行原点击事件逻辑。
<template>
  <div>
    <SieTable
      ref="tableRef"
      v-model="$state.selectedData"
      :table-data="$state.tableData"
      :is-loading="$state.loading"
      :table-data-total="$state.tableDataTotal"
      :list-params="$state.tableParams"
      v-bind="$config.table"
      @cell-click="cellClick"
      @fetch-list="$methods.table.fetchList">
    </SieTable>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieTable } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// --------------------------------
/**
 * 单元格点击事件
 * @param {Object} row 当前点击行
 */
const cellClick = ({ row }) => {
  // 使用默认cellClick方法
  $methods.table.cellClick({ row });

  // ...逻辑处理
};

/**
 * 单元格点击事件
 * @param {Object} row 当前点击行
 */
const cellClick = ({ row }) => {
  // 不使用默认cellClick方法
  $state.currentRow = row;

  // ...逻辑处理
};
// --------------------------------

// ...其他逻辑处理
</script>

tableClickAction

支持外部修改表格操作列点击事件逻辑,必要时可阻止原逻辑继续执行。

  • tableClickAction-表格操作列点击事件。
    • 入参
      • rowData 行数据。
      • type 操作按钮类型。
    • 返回(支持Promise)
      • false 可选,返回false不执行原点击事件逻辑。
<template>
  <div>
    <SieTable
      ref="tableRef"
      :table-data="$state.tableData"
      :is-loading="$state.loading"
      :table-data-total="$state.tableDataTotal"
      :list-params="$state.tableParams"
      v-bind="$config.table"
      @click-action="clickAction"
      @fetch-list="$methods.table.fetchList">
    </SieTable>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieTable } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// --------------------------------
/**
 * 操作栏按钮点击事件
 * @param {Object} rowData 当前点击行数据源
 * @param {string} type 操作类型
 */
const clickAction = async (rowData, type) => {
  // 预留操作列点击事件钩子
  const isContinue = await props.tableClickAction(rowData, type);
  if (isContinue === false) return;

  switch (type) {
    case "edit":
      $state.formData = { ...rowData };
      $state.isFormVisible = true;
      break;

    case "delete":
      // ...删除逻辑
      break;

    default:
      break;
  }
};
// --------------------------------
</script>

beforeFetchList

$methods.table.fetchList已内置封装 beforeFetchList。执行顺序如下:

[处理 parentParams] -> beforeCallback -> beforeFetchList -> $methods.table.fetchList

  • beforeFetchList-调用查询列表数据前。
    • 入参
      • extraParams 查询参数。
    • 返回(支持Promise)
      • extraParams 修改后的查询参数。
<template>
  <div>
    <SieTable
      ref="tableRef"
      :table-data="$state.tableData"
      :is-loading="$state.loading"
      :table-data-total="$state.tableDataTotal"
      :list-params="$state.tableParams"
      v-bind="$config.table"
      @fetch-list="loadTable">
    </SieTable>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieTable, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// --------------------------------
/**
 * 使用默认数据查询
 * @param {Object} extraParams 拓展查询参数
 */
const loadTable = async (extraParams = {}) => {
  try {
    // 使用默认表格查询方法,返回接口传回body内容(try正常执行完毕)和错误的Error值(catch执行)
    const { result, error } = await $methods.table.fetchList(extraParams, {
      // 【可选】父组件传过来的查询参数
      parentParams: [
        // {
        //   field: "", // 查询字段
        //   value: "", // 查询值
        //   required: true // 是否必填
        // }
      ],
      // 【可选】请求前的回调,一般是标品有特殊处理用,返回方法处理后、二开处理前参数
      beforeCallback: (_) => _,
      // 【可选】请求后的回调,一般标品有特殊处理用,返回方法处理后、二开处理前的响应数据
      afterCallback: (res) => {
        if (res.code === 0) {
          // ...业务成功逻辑
        } else {
          // ...业务错误逻辑
        }
        return res;
      },
      // 【可选】请求错误的回调,一般标品有特殊处理用,返回是否继续执行catch错误逻辑,返回false不执行默认错误逻辑(提示错误)
      catchErrorCallback: () => {
        // ...接口或逻辑错误处理
      }
    });
  } catch (err) {
    catchError(err);
    // 隐藏加载动画
    $state.loading = false;
  }
};

/**
 * 不使用默认数据查询方法
 */
const loadTable = async (extraParams = {}) => {
  if ($state.loading) return;

  try {
    let params = {
      ...$state.tableParams,
      ...$state.searchFormData,
      ...extraParams
    }
    // 二开扩展处理请求前参数
    params = await e.props.beforeFetchList(params);
    // 处理获取api
    const $api = e.props.customApi("table.page") || e.api.page;
    // 请求
    let res = await $api(params);
    // 二开扩展处理请求后数据
    res = await e.props.afterFetchList(res);
    if (res?.code === 0) {
      $state.tableData = res?.data?.records || [];
      $state.tableDataTotal = res?.data?.total || 0;
      $state.tableParams.current = res?.data?.current || 1;
      $state.tableParams.size = res?.data?.size || 10;

      // 清空选中数据
      $state.currentRow = null;
      $state.selectedData = [];
    }
  } catch (err) {
    let isErr;
    // 二开扩展处理错误
    isErr = await e.props.catchFechListError(err);
    if (isErr !== false) {
      catchError(err);
    }
  }
}
// --------------------------------

// ...业务逻辑
</script>

afterFetchList

$methods.table.fetchList已内置封装 afterFetchList。执行顺序如下:

$methods.table.fetchList -> afterCallback -> afterFetchList

不使用默认 $methods.table.fetchList方法,自行绑定 afterFetchList钩子参考beforeFetchList部分。

  • afterFetchList-调用查询列表后。
    • 入参
      • res 接口返回结果。
    • 返回(支持Promise)
      • res 修改后的接口返回结果。
<template>
  <div>
    <SieTable
      ref="tableRef"
      :table-data="$state.tableData"
      :is-loading="$state.loading"
      :table-data-total="$state.tableDataTotal"
      :list-params="$state.tableParams"
      v-bind="$config.table"
      @fetch-list="loadTable">
    </SieTable>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieTable, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 数据查询
 * @param {Object} extraParams 拓展查询参数
 */
const loadTable = async (extraParams = {}) => {
  try {
    await $methods.table.fetchList(extraParams, {
      // 【可选】父组件传过来的查询参数
      parentParams: [
        // {
        //   field: "", // 查询字段
        //   value: "", // 查询值
        //   required: true // 是否必填
        // }
      ],
      // 【可选】请求前的回调,一般是标品有特殊处理用,返回方法处理后、二开处理前参数
      beforeCallback: (_) => _,
      // 【可选】请求后的回调,一般标品有特殊处理用,返回方法处理后、二开处理前的响应数据
      afterCallback: (res) => {
        // ...业务逻辑
        return res;
      },
      // 【可选】请求错误的回调,一般标品有特殊处理用,返回是否继续执行catch错误逻辑,返回false不执行默认错误逻辑(提示错误)
      catchErrorCallback: () => {}
    });
  } catch (err) {
    catchError(err);
    // 隐藏加载动画
    $state.loading = false;
  }
};

// ...业务逻辑
</script>

catchFechListError

$methods.table.fetchList已内置封装``catchFechListError。执行顺序如下:

catchErrorCallback -> catchFechListError

不使用默认 $methods.table.fetchList方法,自行绑定 afterFetchList钩子参考beforeFetchList部分。

  • catchFechListError-调用查询列表数据出现错误时。
    • 入参
      • error 接口返回结果。
    • 返回(支持Promise)
      • false 可选,返回false不执行错误逻辑提示及相关错误逻辑。
<template>
  <div>
    <SieTable
      ref="tableRef"
      :table-data="$state.tableData"
      :is-loading="$state.loading"
      :table-data-total="$state.tableDataTotal"
      :list-params="$state.tableParams"
      v-bind="$config.table"
      @fetch-list="loadTable">
    </SieTable>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieTable, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 数据查询
 * @param {Object} extraParams 拓展查询参数
 */
const loadTable = async (extraParams = {}) => {
  try {
    await $methods.table.fetchList(extraParams, {
      // 【可选】父组件传过来的查询参数
      parentParams: [
        // {
        //   field: "", // 查询字段
        //   value: "", // 查询值
        //   required: true // 是否必填
        // }
      ],
      // 【可选】请求前的回调,一般是标品有特殊处理用,返回方法处理后、二开处理前参数
      beforeCallback: (_) => _,
      // 【可选】请求后的回调,一般标品有特殊处理用,返回方法处理后、二开处理前的响应数据
      afterCallback: (res) => {
        // ...业务逻辑
        return res;
      },
      // 【可选】请求错误的回调,一般标品有特殊处理用,返回是否继续执行catch错误逻辑,返回false不执行默认错误逻辑(提示错误)
      catchErrorCallback: () => {}
    });
  } catch (err) {
    catchError(err);
    // 隐藏加载动画
    $state.loading = false;
  }
};

// ...业务逻辑
</script>

setButtonGroupAttrs

按钮组配置会根据表格数据勾选项而动态改变按钮的 disabled属性,因此按钮组配置一般为 computed计算属性。因此 setButtonGroupAttrs需在钩子中运行。

  • setButtonGroupAttrs-设置按钮组属性。
    • 入参
      • e
        • e.state 全局变量状态,vue3的 reactive对象。
        • e.config 组件的配置对象。
    • 返回
      • config 组件的配置对象。
<template>
  <div>
    <SieButtonGroup v-bind="buttonGroupAttrs"></SieButtonGroup>
  </div>
</template>

<script setup>
import { computed, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieButtonGroup, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

const buttonGroupAttrs = computed(() => {
  // 按钮组配置钩子,外部可处理并返回config配置
  return props.setButtonGroupAttrs({
    config: {
      limitNum: 8,
      buttonList: [
        {
          text: "新增",
          type: "primary",
          key: "add"
        },
        {
          text: "启用",
          type: "default",
          key: "useBatch",
          disabled: $state.selectedData.length === 0
        },
        {
          text: "禁用",
          type: "default",
          key: "disableBatch",
          disabled: $state.selectedData.length === 0
        },
        {
          text: "批量删除",
          type: "default",
          key: "deleteBatch",
          disabled: $state.selectedData.length === 0
        }
      ]
    },
    state: $state
  });
});

// ...其他代码
</script>

buttonGroupClick

自定义按钮事件时可使用,可选择阻止原逻辑执行

  • buttonGroupClick-按钮组点击事件。
    • 入参
      • btnItem 按钮对象。
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 组件的配置对象。
    • 返回(支持Promise)
      • false 可选,返回false不执行。
<template>
  <div>
    <SieButtonGroup v-bind="buttonGroupAttrs" @click="buttonGroupClick"></SieButtonGroup>
  </div>
</template>

<script setup>
import { computed, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieButtonGroup, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

const buttonGroupAttrs = computed(() => {
  return props.setButtonGroupAttrs({
    config: {
      limitNum: 8,
      buttonList: [
        {
          text: "新增",
          type: "primary",
          key: "add"
        },
        {
          text: "启用",
          type: "default",
          key: "useBatch",
          disabled: $state.selectedData.length === 0
        },
        {
          text: "禁用",
          type: "default",
          key: "disableBatch",
          disabled: $state.selectedData.length === 0
        },
        {
          text: "批量删除",
          type: "default",
          key: "deleteBatch",
          disabled: $state.selectedData.length === 0
        }
      ]
    },
    state: $state
  });
});

/**
 * 按钮组点击事件
 */
const buttonGroupClick = async (btnItem) => {
  // 外部阻止按钮组点击事件钩子
  const isContinue = await props.buttonGroupClick(btnItem, { state: $state, config: $config });
  if (isContinue === false) return;

  // ...默认按钮逻辑
}

// ...其他代码
</script>

beforeFormSubmit

  • beforeFormSubmit-表单提交事件前。
    • 入参
      • _
        • _.state 全局变量状态,vue3的 reactive对象。
        • _.config 全局配置项,vue3的 reactive对象。
    • 返回(支持Promise)
      • false 可选,返回false不执行提交逻辑。
<template>
  <div>
    <SieDrawer v-model="$state.isFormVisible" :title="formTitle" @confirm="handleConfirm" @cancel="handleCancel">
      <SieForm ref="formRef" v-model="$state.formData" v-bind="$config.form"></SieForm>
    </SieDrawer>
  </div>
</template>

<script setup>
import { computed, ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { SieDrawer, SieForm, catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

const formTitle = computed(() => {
  return $state.formData?.id ? "编辑" : "新增";
});

/**
 * 表单,点击【确认】按钮
 */
const handleConfirm = async () => {
  // 表单提交前的钩子,可阻止表单默认提交事件
  const isContinue = await props.beforeFormSubmit({ state: $state, config: $config });
  if (isContinue === false) return;

  submit();
};

async function submit() { 
  // ...业务代码
}

async function handleCancel() { 
  // ...业务代码
}

// ...其他代码
</script>

customApi

替换默认的api接口。

  • customApi-自定义api接口。
    • 入参
      • type api类型,用于业务开发人员告知二开交付人员当前使用的api类型。(暂定)参数值:表格数据查询接口 pagetable.page,表格新增接口 form.create,表格编辑接口 form.update。其余api类型可由业务开发人员自行定义。
    • 返回
      • Promise请求,并非固定有返回值。对于业务开发人员来说,可能会无返回值,需要用默认api接口做兜底处理。
<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { $notify, catchError } from "@dme/snack-ui";
import { api } from "./api/index";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 二、(50)表单,提交
 */
const submit = async () => {
  // 显示加载动画
  $state.loading = true;

  let isSuccess = false;
  const data = $state.formData;
  let valid;
  try {
    valid = await formRef.value.validate();
  } catch (err) {
    valid = err;
  }

  if (valid) {
    try {
      // 接口配置
      const cusApiConfig = [
        // 使用props.customApi作为外部修改接口的钩子
        { api: props.customApi("form.create") || api.create, msg: "添加成功" },
        { api: props.customApi("form.update") || api.update, msg: "更新成功" }
      ][data.id ? 1 : 0];

      const res = await cusApiConfig.api(data);
      if (res?.code === 0) {
        $notify.success(cusApiConfig.msg);
        $state.isFormVisible = false; // 操作成功后,关闭表单
        isSuccess = true;
      }
    } catch (err) {
      catchError(err);
    }

  }
  // 隐藏加载动画
  $state.loading = false;

  if (isSuccess) {
    // ...逻辑处理
  }
};

// ...其他代码
</script>

variable变量

modelState是页面状态的默认模板,是一个函数,返回一个对象。返回的对象提供了:预定义数据结构、状态初始化、状态扩展点,实现标准化和可维护性。

<template>
  <div>
    <div>父级ID:{{ $state.parentId }}</div>
  </div>
</template>

<script setup lang="ts">
import { createPageContext, modelProps, modelState } from "@imom/vue-2nd-kit";

const props = defineProps({
  ...modelProps
});

// customSetup生命周期处理state、config
const defaultSetupValue = props.customSetup({
  state: {
    ...modelState(),
    searchFormData: { // 覆盖默认searchFormData
      siteId: "123456" // 站点ID
    },
    parentId: "abc123" // 扩展自定义创建父级ID
  },
  config: {
    // ...
  }
});

const { $state } = createPageContext({
  state: {
    ...defaultSetupValue.state // customSetup处理后的初始化状态值,覆盖默认state
  }
  // ...其余配置
});

console.log("$state.parentId >>> ", $state.parentId); // 读取父级ID

// ...其他代码

defineExpose({
  // ...其他导出
  $state // 导出页面状态变量
});
</script>

modelState创建的默认 $state属性:

  • loading 状态加载中,默认 false
  • searchFormData 搜索表单数据,默认 {}
  • tableParams 表格参数。
    • current 当前页码,默认 1
    • size 每页条数,默认 10
  • selectedData 表格选中数据,默认 []
  • currentRow 表格当前行数据,默认 null
  • tableData 表格数据,默认 []
  • tableDataTotal 表格数据总数,默认 0
  • formData 表单数据,默认 {}
  • isFormVisible 表单是否可见,默认 false

API方法

由业务页面抛出,给外部调用。

<script setup>
import { ref } from "vue";
import { modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
import pageConfig from "./pageConfig";
import { catchError } from "@dme/snack-ui";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});
const tableRef = ref(null);
const formRef = ref(null);

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    formRef
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...其他逻辑

const customFunc = () => {
  console.log('customFunc');
}

// --------------------------------
// 导出方法、变量等给外部二开使用
defineExpose({
  // ...其他导出
  $methods, // 导出统一的API方法
  customFunc // 自定义导出
});
// --------------------------------
</script>

$methods.searchForm

搜索表单内置方法。

名称 类型 说明
handleSearch (params: object) => Promise 查询方法,会讲搜索页码 $state.tableParams.current重置为 1
setFormItemList () => { insert(datas: FormItemListType, idx: number | undefined): this; update(datas: FormItemListType): this; delete(datas: string:[]): this; replace(data: Record): this;} insert:插入表单项,idx为插入位置(默认为最后);update:更新表单项,delete:删除表单项;replace:替换表单项

$methods.buttonGroup

按钮组内置方法。

名称 类型 说明

$methods.table

表格内置方法。

名称 类型 说明
fetchList (params: object) => Promise 表格数据获取方法
selectChange (data: TinyGridColumnCellResultType) => void 表格勾选事件
cellClick (e: TinyGridColumnCellResultType) => void 表格单元格点击事件
clickAction (rowData: ObjType, type: string) => void 表格操作列点击事件
clearSelectTrace () => void 抹除主表选择痕迹
setColumnConfig () => { insert: (datas: ColumnConfigType, idx: number | undefined) => this; update: (datas: ColumnConfigType) => this; delete: (datas: string[]) => this; replace: (data: Record) => this; } insert:插入列,idx为插入位置(默认为最后),update:更新列,delete:删除列,replace:替换列

$methods.form

表单内置方法。

名称 类型 说明
setFormItemList () => { insert(datas: FormItemListType, idx: number | undefined): this; update(datas: FormItemListType): this; delete(datas: string:[]): this; replace(data: Record): this;} insert: 插入表单项,idx为插入位置(默认为最后),update:更新表单项,delete:删除表单项,replace:替换表单项

$methods.tabs

标签页内置方法。

名称 类型 说明
click (tabItem: TabItemType) => void tabs点击事件
setTabItems () => { insert: (datas: TabItemsType, idx: number | undefined) => this; update: (datas: TabItemsType) => this; delete: (datas: string[]) => this; replace: (data: Record) => this; } insert:插入tabItems,idx为插入位置(默认为最后),update:更新tabItems,delete:删除tabItems,replace:替换tabItems

$methods.leftTree

(预留,未实现)左侧树形菜单内置方法。

slot插槽

预留插槽给外部调用。插槽规则如下:

  1. 单个单词、小驼峰的为整个独立组件的插槽,如 searchFormtable
  2. 带下划线的为独立组件内部的插槽,如 table_xxxform_xxx
<template>
  <div class="page-wrapper auto-scroll">
    <TinyCollapse v-model="collapseActiveName">
      <!-- 查询条件 -->
      <TinyCollapseItem :title="pageConfig.pageInfo.searchTitle" name="1">
        <!-- searchForm 查询表单插槽 -->
        <slot name="searchForm">
          <SieSearchForm
            v-model="$state.searchFormData"
            :form-item-list="$config.searchForm.formItemList"
            :loading="$state.loading"
            :attrs="$config.searchForm.attrs"
            @search="handleSearch">
            <template v-for="(sf, _idx) in $config.searchForm.formItemList" :key="sf.prop" v-slot:[sf.prop]="searchFormScope">
              <!-- searchForm_ 查询表单项插槽 -->
              <slot :name="`searchForm_${sf.prop}`" v-bind="{ ...searchFormScope }" :biz-state="$state" :biz-config="$config"></slot>
            </template>
          </SieSearchForm>
        </slot>
      </TinyCollapseItem>

      <!-- 列表 -->
      <TinyCollapseItem :title="pageConfig.pageInfo.title" name="2">
        <div class="btn-panel">
          <!-- buttonGroup 按钮组插槽 -->
          <slot name="buttonGroup">
            <SieButtonGroup v-bind="buttonGroupAttrs" @click="buttonGroupClick"></SieButtonGroup>
          </slot>
        </div>
        <!-- table 表格插槽 -->
        <slot name="table">
          <SieTable
            ref="tableRef"
            :table-data="$state.tableData"
            :is-loading="$state.loading"
            :table-data-total="$state.tableDataTotal"
            :list-params="$state.tableParams"
            v-bind="$config.table"
            @click-action="clickAction"
            @select-change="$methods.table.selectChange"
            @select-all="$methods.table.selectChange"
            @fetch-list="loadTable">
            <template v-for="(t, _idx) in $config.table.columnConfig" :key="t.field" v-slot:[t.field]="tableScope">
              <!-- table_ 表格列插槽 -->
              <slot :name="`table_${t.field}`" v-bind="{ ...tableScope }"></slot>
            </template>
          </SieTable>
        </slot>
      </TinyCollapseItem>
    </TinyCollapse>

    <!-- 新增/编辑弹窗 -->
     <!-- form 表单插槽 -->
    <slot name="form">
      <SieDrawer v-model="$state.isFormVisible" :title="formTitle" @confirm="handleConfirm" @cancel="handleCancel">
        <template #content>
          <SieForm ref="formRef" v-model="$state.formData" v-bind="$config.form">
            <template v-for="(f, _idx) in $config.form.formItemList" :key="f.prop" v-slot:[f.prop]="formScope">
              <!-- form_ 表单项插槽 -->
              <slot :name="`form_${f.prop}`" v-bind="{ ...formScope }" :biz-state="$state" :biz-config="$config"></slot>
            </template>
          </SieForm>
        </template>
      </SieDrawer>
    </slot>
  </div>
</template>

searchForm

查询表单组件插槽,可替换整个查询表单。

slot插槽-searchForm.png

searchForm_

查询表单组件的表单项插槽,可以根据 prop名称替换指定表单项。

slot插槽-searchForm_.png

buttonGroup

表格组件插槽,可替换整个表格。

slot插槽-buttonGroup.png

buttonGroup_

表格组件按钮插槽,可以根据 key名称替换指定按钮。

table

表格组件插槽,可替换整个表格。

slot插槽-table.png

table_

表格组件的列插槽,可以根据 field名称替换指定列。

slot插槽-table_.png

form

表单组件插槽,可替换整个表单。

slot插槽-form.png

form_

表单组件的表单项插槽,可以根据 prop名称替换指定表单项。

slot插槽-form_.png

tabs

标签页组件插槽,可替换整个标签页。

slot插槽-tabs.png

tabs_items

标签页组件的标签项尾部追加插槽,可在标签项最后追加页签内容。

slot插槽-tabs_items.png

tab_

标签页组件的标签项插槽,可以根据 name名称替换指定标签项。

slot插槽-tab_.png

leftTree

左侧树组件插槽,可以替换整个左侧树。

代码使用

标准页面结构主要部分为四大代码块:searchFrombuttonGrouptableform,如主从表会增加 tabs,左侧树结构会增加 leftTree

二开交付人员-集成使用.png

searchForm

查询表单。

<template>
  <div>
    <!-- ...其他代码 -->

      <slot name="searchForm">
        <SieSearchForm
          v-model="$state.searchFormData"
          :form-item-list="$config.searchForm.formItemList"
          :loading="$state.loading"
          :attrs="$config.searchForm.attrs"
          @search="handleSearch">
          <template
            v-for="(sf, _idx) in $config.searchForm.formItemList"
            :key="sf.prop"
            v-slot:[sf.prop]="searchFormScope">
            <slot
              :name="`searchForm_${sf.prop}`"
              v-bind="{ ...searchFormScope }"
              :biz-state="$state"
              :biz-config="$config"></slot>
          </template>
        </SieSearchForm>
      </slot>

    <!-- ...其他代码 -->
  </div>
</template>

<script setup>
// 一、Vue 和 Composition API
import { ref } from "vue";
// 二、第三方库(包含自己封装的其他库)
import { createPageContext, modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
// 三、API
import { api } from "./api/index";
// 四、基础配置
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    // ...其他代码
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 一、(10)点击【查询】按钮
 */
const handleSearch = (params) => {
  // 查询数据
  $methods.searchForm.handleSearch({
    ...$state.searchFormData,
    ...params
  });
};

// ...其他代码
</script>

buttonGroup

按钮组。

<template>
  <div>
    <!-- ...其他代码 -->

      <div class="btn-panel">
        <slot name="buttonGroup">
          <SieButtonGroup v-bind="buttonGroupAttrs" @click="buttonGroupClick">
            <template
              v-for="(btnItem, _btnIndex) in buttonGroupAttrs.buttonList"
              :key="btnItem.key"
              v-slot:[btnItem.key]="buttonGroupScope">
              <slot
                :name="`buttonGroup_${btnItem.key}`"
                v-bind="{ ...buttonGroupScope }"
                :biz-state="$state"
                :biz-config="$config"></slot>
            </template>
          </SieButtonGroup>
        </slot>
      </div>

    <!-- ...其他代码 -->
  </div>
</template>

<script setup>
// 一、Vue 和 Composition API
import { ref } from "vue";
// 二、第三方库(包含自己封装的其他库)
import { createPageContext, modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
// 三、API
import { api } from "./api/index";
// 四、基础配置
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState(),
    formDataInit: () => {}
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    // ...其他代码
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

const buttonGroupAttrs = computed(() => {
  // ...其他代码
  return props.setButtonGroupAttrs({
    config: {
      limitNum: 8,
      buttonList: [
        {
          text: "新增",
          type: "primary",
          key: "add"
        },
        {
          text: "导入",
          type: "default",
          key: "import"
        }
      ]
    },
    state: $state
  });
});

/**
 * 按钮组点击事件
 * @param btnItem 按钮对象
 */
const buttonGroupClick = async (btnItem) => {
  const isContinue = await props.buttonGroupClick(btnItem, { state: $state, config: $config });
  if (isContinue === false) return;

  // 获取当前选中的所有 ID
  const selectIds = $state.selectedData.map((item) => item.id);

  switch (btnItem.key) {
    case "add":
      $state.formData = { ...$state.formDataInit() };
      $state.isFormVisible = true;
      break;

    case "import":
      // ...其他代码
      break;

    default:
      break;
  }
};

// ...其他代码
</script>

table

表格。

<template>
  <div>
    <!-- ...其他代码 -->

      <slot name="table">
        <SieTable
          ref="tableRef"
          v-model="$state.selectedData"
          :table-data="$state.tableData"
          :is-loading="$state.loading"
          :table-data-total="$state.tableDataTotal"
          :list-params="$state.tableParams"
          v-bind="$config.table"
          @click-action="clickAction"
          @select-change="$methods.table.selectChange"
          @select-all="$methods.table.selectChange"
          @fetch-list="loadTable">
          <template v-for="(t, _idx) in $config.table.columnConfig" :key="t.field" v-slot:[t.field]="tableScope">
            <slot :name="`table_${t.field}`" v-bind="{ ...tableScope }"></slot>
          </template>
        </SieTable>
      </slot>

    <!-- ...其他代码 -->
  </div>
</template>

<script setup>
// 一、Vue 和 Composition API
import { ref } from "vue";
// 二、第三方库(包含自己封装的其他库)
import { catchError } from "@dme/snack-ui";
import { createPageContext, modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
// 三、API
import { api } from "./api/index";
// 四、基础配置
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});

//#region 页面所有 DOM 实例引用
const tableRef = ref(null);

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    tableRef,
    // ...其他代码
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 二、(10)数据查询
 * @param {Object} extraParams 拓展查询参数
 */
const loadTable = async (extraParams = {}) => {
  try {
    await $methods.table.fetchList(extraParams, {
      // 父组件传过来的查询参数
      parentParams: [
        // {
        //   field: "", // 查询字段
        //   value: "", // 查询值
        //   required: true // 是否必填
        // }
      ],
      // 请求前的回调,一般是标品有特殊处理用,返回方法处理后、二开处理前参数
      beforeCallback: (_) => _,
      // 请求后的回调,一般标品有特殊处理用,返回方法处理后、二开处理前的响应数据
      afterCallback: (res) => res,
      // 请求错误的回调,一般标品有特殊处理用,返回是否继续执行catch错误逻辑,返回false不执行默认错误逻辑(提示错误)
      catchErrorCallback: () => {}
    });
  } catch (err) {
    catchError(err);
    // 隐藏加载动画
    $state.loading = false;
  }
};

/**
 * 二、(60)操作栏按钮点击事件
 * @param {Object} rowData 当前点击行数据源
 * @param {string} type 操作类型
 */
const clickAction = async (rowData, type) => {
  const isContinue = await props.tableClickAction(rowData, type);
  if (isContinue === false) return;

  switch (type) {
    case "edit":
      $state.formData = { ...rowData };
      $state.isFormVisible = true;
      break;

    case "delete":
      // ...删除逻辑
      break;

    default:
      break;
  }
};

// ...其他代码
</script>

form

表单。

<template>
  <div>
    <!-- ...其他代码 -->

    <!-- 新增/编辑弹窗 -->
    <slot name="form">
      <SieDrawer
        v-model="$state.isFormVisible"
        :title="formTitle"
        @confirm="handleConfirm"
        @cancel="handleCancel"
        width="1200">
        <template #content>
          <SieForm ref="formRef" v-model="$state.formData" v-bind="$config.form" label-width="110px">
            <template v-for="(f, _idx) in $config.form.formItemList" :key="f.prop" v-slot:[f.prop]="formScope">
              <slot :name="`form_${f.prop}`" v-bind="{ ...formScope }" :biz-state="$state" :biz-config="$config"></slot>
            </template>
          </SieForm>
        </template>
      </SieDrawer>
    </slot>

    <!-- ...其他代码 -->
  </div>
</template>

<script setup>
// 一、Vue 和 Composition API
import { ref } from "vue";
// 二、第三方库(包含自己封装的其他库)
import { createPageContext, modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
// 三、API
import { api } from "./api/index";
// 四、基础配置
import pageConfig from "./pageConfig";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig
  }
});

const formRef = ref(null); // 表单,实例

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    formRef,
    // ...其他代码
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

/**
 * 二、(30)表单,点击【确认】按钮
 */
const handleConfirm = async () => {
  const isContinue = await props.beforeFormSubmit({ state: $state, config: $config });
  if (isContinue === false) return;

  submit();
};

/**
 * 二、(40)表单,点击【取消】按钮
 */
const handleCancel = () => {};

/**
 * 二、(50)表单,提交
 */
const submit = async () => {
  // ...表单提交逻辑
}

// ...其他代码
</script>

tabs

多页签(一般用于子表)。

<template>
  <div>
    <!-- ...其他代码 -->
   
    <!-- 【从表】TAB -->
    <div class="tab-box">
      <slot name="tabs">
        <TinyTabs v-bind="$config.tabs.attrs" @click="$methods.tabs.click">
          <TinyTabItem v-for="(tab, tabIdx) in $config.tabs.tabItems" :key="tabIdx" v-bind="tab.attrs">
            <slot :name="`tab_${tab.attrs.name}`">
              <component
                ref="compRef"
                :is="tab.childNode.component"
                :parent-id="$state.currentRow?.id"
                v-bind="tab.childNode.attrs"></component>
            </slot>
          </TinyTabItem>
          <slot name="tabs_items"></slot>
        </TinyTabs>
      </slot>
    </div>

    <!-- ...其他代码 -->
  </div>
</template>

<script setup lang="ts"> 
// 一、Vue 和 Composition API
import { ref, markRaw } from "vue";
// 二、第三方库(包含自己封装的其他库)
import { TinyTabs, TinyTabItem } from "@opentiny/vue";
import { createPageContext, modelProps, modelState, modelConfig, modelEmits } from "@imom/vue-2nd-kit";
// 三、API
import { api } from "./api/index";
// 四、基础配置
import pageConfig from "./pageConfig";
// 五、业务组件
import ReportingManagementSlave from "./slaveComponents/ReportingManagementSlave/index.vue";
import ApsOperationLogSlave from "./slaveComponents/ApsOperationLogSlave/index.vue";

const props = defineProps({
  ...modelProps
});
const emits = defineEmits([...modelEmits]);

const compRef = ref(null);

const defaultSetupValue = props.customSetup({
  state: {
    ...modelState()
  },
  config: {
    ...modelConfig,
    ...pageConfig,
    tabs: {
      attrs: {
        activeName: "reportingManagement",
        tabStyle: "card"
      },
      tabItems: [
        {
          attrs: {
            title: "报工管理信息表",
            name: "reportingManagement"
          },
          childNode: {
            attrs: {},
            component: markRaw(ReportingManagementSlave) // 标记组件对象,使其保持原样,不被转换为响应式对象。
          }
        },
        {
          attrs: {
            title: "aps运行日志",
            name: "apsOperationLog"
          },
          childNode: {
            attrs: {},
            component: markRaw(ApsOperationLogSlave)
          }
        }
      ]
    }
  }
});

const { $state, $config, $methods, $ref } = createPageContext({
  props,
  emits,
  ref: {
    compRef
    // ...其他代码
  },
  state: {
    ...defaultSetupValue.state
  },
  config: {
    ...defaultSetupValue.config
  },
  api
});

// ...其他代码
</script>

leftTree

待完善。

<template>
  <div>
    <!-- ...其他代码 -->
    <!-- ...其他代码 -->
  </div>
</template>

<script setup>
// ...其他代码
</script>

其他业务模块

由于标准模块不能覆盖所有业务场景,若有非标准模块,需要业务人员自行扩展。

生成业务功能包

使用npm包的方式提供前端业务功能包,通常一个菜单对应一个业务功能包。

包名规范

包名需要带有项目、产品线、模块、业务功能等要素,做到语义化、规范化。

  • 包名规则
    • 小写 包名必须全部使用小写字母,不允许使用大写字母
    • 无空格和特殊字符 包名不能包含空格 、~)('!* 等特殊字符
    • 不能以 ._ 开头 包名不能以 ._ 开头

::: tip 模板
@imom-${产品线}/${模块}-${业务功能}
:::

  • 示例
    • imom产品中,qms产品线,process-control模块,检验业务类型规则(checkRule)功能
      • @imom-qms/processcontrol-checkrule
    • imom产品中,mes产品线,smt模块,工单操作台(workOrderConsole)功能
      • @imom-mes/smt-workorderconsole
// package.json

{
  "name": "@imom-qms/processcontrol-checkrule",
  // ...其他属性
}

版本规范

npm包版本管理遵循语义化版本控制(Semantic Versioning,简称 SemVer)规范,这一规范为软件版本赋予了明确的意义,使得版本升级和依赖管理变得更加简单和清晰。

  • 版本号格式
    • 版本号格式为 MAJOR.MINOR.PATCH,如 1.0.0
    • 遵循语义化版本规范,重大更新使用 major,新功能使用 minor,bug 修复使用 patch
    • 项目中也出现 x.y.z格式的版本号,其中 x等同于 MAJORy等同于 MINORz等同于 PATCH。从中延伸,打包脚本中 build:x即更新 MAJOR版本号;同理,build:y更新 MINOR版本号,build:z更新 PATCH版本号。

更多说明请查看版本号说明

// package.json

{
  // ...其他属性
  "version": "1.0.0"
  // ...其他属性
}

打包发版

进行这一步前,请先确保已阅读6. npm包开发

  1. 下载[_build.zip],并解压到业务功能目录下。
  2. 创建导出组件的入口文件 index.ts

修改打包入口文件 index.ts,引入页面组件,并导出组件。
示例中导出 CheckRule业务功能组件。

  • 单个vue组件导出
// index.ts

import CheckRuleIndex from "./index.vue";
export default CheckRuleIndex;
  • 多个vue组件模块导出(推荐)
// index.ts

import CheckRuleIndex from "./index.vue";
import CheckRuleDetail from "./detail.vue";
export { CheckRuleIndex, CheckRuleDetail };
  1. 创建 package.json文件,并添加依赖。
{
  "name": "【根据包名规则填写包名】",
  "version": "【根据版本号格式填写版本号】",
  "description": "【根据业务功能填写描述】",
  "main": "./dist/cjs/index.cjs.js",
  "module": "./dist/es/index.es.js",
  "types": "./dist/types/index.d.ts",
  "type": "module",
  "scripts": {
    "dev": "vite -c ./_build/vite.config.ts",
    "build": "vue-tsc --project ./_build/tsconfig.json && vite build -c ./_build/vite.config.ts",
    "build:z": "npm run build && npm version patch && npm run publish:hosted",
    "build:y": "npm run build && npm version minor && npm run publish:hosted",
    "build:x": "npm run build && npm version major && npm run publish:hosted",
    "publish:hosted": "npm publish --registry=http://192.168.181.114:8081/repository/npm-hosted/",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "vue3",
    "page",
    "imom"
  ],
  "author": "sie",
  "repository": {
    "type": "gitlab",
    "url": "http://192.168.175.208/imom"
  },
  "publishConfig": {
    "access": "public"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
    // ...根据项目情况添加依赖包
  },
  "devDependencies": {
    "@types/node": "^16.9.0",
    "@vitejs/plugin-vue": "^5.0.4",
    "typescript": "^5.2.2",
    "vite": "^5.2.0",
    "vite-plugin-css-injected-by-js": "^3.5.1",
    "vite-plugin-dts": "^3.9.1",
    "vite-plugin-vue-setup-extend": "^0.4.0",
    "vue": "^3.2.27",
    "vue-tsc": "^2.0.6"
  },
  "peerDependencies": {
    "@dme/snack-ui": "0.0.76",
    "vue": "^3.2.27"
    // ...根据需求添加其他依赖项
  }
}
  1. 检查依赖

若有打包时需要忽略打包的依赖,修改 _build/vite.config.ts文件,将需要忽略的依赖添加到 build.rollupOptions.external中。

// _build/vite.config.ts

// ...引入依赖
export default defineConfig({
  // ...其他配置
  build: {
    // ...其他配置
    rollupOptions: {
      external: [
        "vue"
        // ...根据需求添加其他依赖项
      ]
      // ...其他配置
    }
  }
});
  1. 安装依赖
    在业务功能目录下执行安装依赖命令
yarn install
  1. 打包、发版

在业务功能所在目录下执行对应命令进行打包、发版:
::: danger
!按照自己需要执行相关命令,不是全部都要执行!
:::

# 只打包,不升版本,不发版
yarn build

# 不打包,不升版本,只发版
yarn publish:hosted

# 打包,升级 修订版本号(1.0.0 => 1.0.1),发版
yarn build:z

# 打包,升级 次版本号(1.0.0 => 1.1.0),发版
yarn build:y

# 打包,升级 主版本号(1.0.0 => 2.0.0),发版
yarn build:x

更多打包规则请查看本地部署运行-打包与发布

  1. 检查

执行 打包操作执行成功后,生成的 dist目录结构大致如下:

├─dist                          // 构建输出目录
|  ├─es                         // esm格式
|  | ├─index.es.js
|  | ├─index.vue.es.js
|  | ├─pageConfig.es.js
|  | ├─_virtual
|  | |    └_plugin-vue_export-helper.es.js
|  | ├─api
|  | |  └index.es.js
|  ├─cjs                        // cjs格式
|  |  ├─index.cjs.js
|  |  ├─index.vue.cjs.js
|  |  ├─pageConfig.cjs.js
|  |  ├─_virtual
|  |  |    └_plugin-vue_export-helper.cjs.js
|  |  ├─api
|  |  |  └index.cjs.js

若出现 node_modules目录,请检查打包配置 vite.config.ts是否正确。


Comment