npm package: @qingbing/ts-v3-form-dialog
Description: 以 vue3 + element-plus 为基础封装 dialog 组件, 主要用于表单展示或查看
Author: qingbing
Latest version: 2.0.16

# FormDialog 插件介绍

- [FormDialog 插件介绍](#formdialog-插件介绍)
  - [1. 概要说明](#1-概要说明)
    - [1.1 地址](#11-地址)
    - [1.2 插件描述](#12-插件描述)
    - [1.3 重要依赖](#13-重要依赖)
    - [1.4 插件安装](#14-插件安装)
  - [2. 包说明](#2-包说明)
    - [2.1 属性说明](#21-属性说明)
      - [2.1.1 TFormDialogButtonConf 类型说明](#211-tformdialogbuttonconf-类型说明)
      - [2.1.2 TFormDialogSaveCall 类型说明](#212-tformdialogsavecall-类型说明)
    - [2.2 实例暴露说明](#22-实例暴露说明)
      - [2.2.1 TFormDialogRefresh 原型](#221-tformdialogrefresh-原型)
  - [3. 示例](#3-示例)
    - [3.1 全局注册使用](#31-全局注册使用)
    - [3.2 模板组件使用](#32-模板组件使用)

## 1. 概要说明
### 1.1 地址

[https://gitee.com/duqingbing/ts-v3-package/tree/ts-v3-form-dialog](https://gitee.com/duqingbing/ts-v3-package/tree/ts-v3-form-dialog)

### 1.2 插件描述

以 vue3 + element-plus 为基础封装 dialog 组件, 主要用于表单展示或查看

### 1.3 重要依赖

- @qingbing/ts-v3-element-plus
- @qingbing/ts-v3-form-button
- @qingbing/ts-v3-utils
- @qingbing/ts-v3-xz-form
- element-plus
- vue

### 1.4 插件安装

```bash
# yarn 安装
yarn add @qingbing/ts-v3-form-dialog

# npm 安装
npm i @qingbing/ts-v3-form-dialog
```

## 2. 包说明

### 2.1 属性说明

| 属性名     | 类型                  | 是否必需 | 默认值 | 意义                                                                                       |
| :--------- | :-------------------- | :------- | :----- | :----------------------------------------------------------------------------------------- |
| modelValue | boolean               | 是       | false  | 监控 父组件的 v-model 值, 控制 dialog 是否展开                                             |
| conf       | TCDialogProps         | 否       | {}     | dialog 参数配置， 可参考: https://element-plus.org/zh-CN/component/dialog.html#attributes  |
| formConf   | TCFormProps           | 否       | {}     | form 表单配置， 可参考: https://element-plus.org/zh-CN/component/form.html#form-attributes |
| buttonConf | TFormDialogButtonConf | 否       | {}     | operate-button 组件配置， 主要用于控制操作按钮                                             |
| saveCall   | TFormDialogSaveCall   | 否       | -      | dialog 数据保存回调函数                                                                    |

#### 2.1.1 TFormDialogButtonConf 类型说明 

```ts
/**
 * 针对 dialog 的 button 组件配置
 */
export type TFormDialogButtonConf = {
    refForm?: string,
    buttons?: Record<string, TFormButtonButton>,
    submitType?: string,
    submitLabel?: string,
    submitCallback?: (params?: TFormButtonSubmitParams, vm?: FormInstance) => void, // 该函数组件内部覆盖
    resetType?: string,
    resetLabel?: string,
    resetCallback?: (vm: FormInstance) => void,
    cancelType?: string,
    cancelLabel?: string,
    cancelCallback?: (vm: FormInstance) => void,
}
```

#### 2.1.2 TFormDialogSaveCall 类型说明 

```ts
/**
 * dialog 数据保存函数参数类型
 */
export type TFormDialogSaveParams = {
    vm: FormInstance;
    callSuccess: TStringCallableVoid;
    callFailure: TStringCallableVoid;
    data: TRecordss
}

/**
 * dialog 数据保存函数
 */
export type TFormDialogSaveCall = (params: TFormDialogSaveParams) => void
```

### 2.2 实例暴露说明

| 属性名  | 类型               | 含义                                             |
| :------ | :----------------- | :----------------------------------------------- |
| refresh | TFormDialogRefresh | 组件外部调用该暴露接口可重新刷新 dialog 内部数据 |

#### 2.2.1 TFormDialogRefresh 原型

```ts
export type TFormDialogRefresh = (data: {
    isForm?: boolean,
    title?: string,
    items: TXzFormItem[] | Record<PropertyKey, TXzFormItem>,
    data?: TRecord,
    viewFields?: string[],
    showFields?: string[],
    buttons?: string[]
}) => void
/**
 * 将对话框数据重置暴露出去
 */
defineExpose({
    refresh: <TFormDialogRefresh>refresh
})
```
## 3. 示例
### 3.1 全局注册使用

- 一旦注册， `FormDialog` 作为组件全局通用 
- 使用方法参考 [3.2 模板组件使用](#32-模板组件使用), 去掉组件导入的语句即可

```ts
// main.ts
import { FormDialogPlugin } from '@qingbing/ts-v3-form-dialog'
app.use(FormDialogPlugin, {
  name: 'FormDialog',
  options: {}
})
```

### 3.2 模板组件使用

```vue
<template>
    <!-- 搜索 -->
    <el-form :inline="true" :model="queryData">
        <QueryForm :items="queryItems" :data="queryData" />
        <el-form-item>
            <el-button type="info" @click="refreshTable">Query</el-button>
            <el-button type="warning" @click="handleAdd">Add</el-button>
        </el-form-item>
    </el-form>

    <!-- 列表 -->
    <XzTable :headers="headers" :get-data="getData" :params="queryData" :refresh-key="tableKey" />

    <!-- dialog 对话框 -->
    <FormDialog v-model="dialogFlag" ref="dialog" :conf="dialogConf" :buttonConf="buttonConf" :saveCall="saveCall" />
</template>

<script lang="ts" setup>
import '@qingbing/ts-v3-xz-form/dist/style.css' // xz-form 组件样式(整合了 json-editor 和 xz-editor 组件样式)

import type { TFormDialogRefresh, TFormDialogButtonConf, TFormDialogSaveCall, TFormDialogSaveParams } from "@qingbing/ts-v3-form-dialog"
import type { TCDialogProps } from "@qingbing/ts-v3-element-plus"
import type { TXzFormItem } from '@qingbing/ts-v3-xz-form'
import type { TXzTableFetchData, TXzTableItem } from "@qingbing/ts-v3-xz-table"
import type { TRecord, TObject } from "@qingbing/ts-v3-utils"
import type { TQueryFormItem } from "@qingbing/ts-v3-query-form"
import { randomInt } from "@qingbing/ts-v3-utils"
import { XzTable } from "@qingbing/ts-v3-xz-table"
import { QueryForm } from "@qingbing/ts-v3-query-form";
import { reactive, ref } from 'vue'
import request from "axios";
import { FormDialog } from "@qingbing/ts-v3-form-dialog"

/**
 * 搜索表单
 */
// 搜索表单项目
const queryItems: TQueryFormItem[] = [
    {
        input_type: "input-text",
        field: "keyword",
        label: "关键字",
    },
    {
        input_type: "sex",
        field: "sex"
    },
]
// 搜索表单数据
const queryData = reactive<TRecord>({})
/**
 * 表格处理
 */
// 表格刷新标志
const tableKey = ref(1)
// 刷新表格函数
const refreshTable = () => {
    tableKey.value++
}
// 表格显示项目配置
const headers: TXzTableItem[] = [
    {
        field: "_idx",
        label: "序号",
        tableConf: {
            width: "160",
            align: "left",
            fixed: "left"
        },
    },
    {
        field: "username",
        label: "用户名",
        default: '-',
        tableConf: {
            width: "160",
            align: "left",
        },
    },
    {
        field: "sex",
        label: "性别",
        default: '0',
        tableConf: {
            width: "100",
        },
        type: 'option',
        exts: {
            options: {
                '0': "秘",
                '1': "女",
                '2': "男",
            }
        }
    },
    {
        field: "operate",
        label: "操作",
        default: '',
        tableConf: {
            fixed: "right",
            align: "left",
            minWidth: 300,
        },
        type: 'component',
        exts: {
            component: {
                name: "operate",
                conf: {
                    isButton: true,
                    buttons: [
                        {
                            type: 'view',
                            handle: (row: TRecord) => {
                                handleView(row)
                            }
                        }, {
                            type: 'edit',
                            handle: (row: TRecord) => {
                                handleEdit(row)
                            }
                        },
                        {
                            type: 'delete',
                            handle: (row: TRecord, successCall: (msg: string) => void, failureCall: (msg: string) => void) => {
                                successCall('删除成功')
                                refreshTable()
                            }
                        }
                    ]
                }
            },
        }
    },
]
// 获取表格数据
const getData: TXzTableFetchData = (cb, params?: TObject) => {
    request.create({
        baseURL: 'http://mock.qiyezhu.net/mock/64e35b8e4a2b7b001dd2e4ec/example/table-pagination-data', // 默认就是 "/",
        timeout: 5000, // 5000毫秒，5秒
        headers: {
            'Content-Type': 'application/json;charset=utf-8'
        }
    })
        .post('/', params)
        .then(res => cb(res.data.data))
        .catch(err => err)
}

/**
 * dialog 数据保存函数
 * 
 * @param params 
 */
const saveCall: TFormDialogSaveCall = (params: TFormDialogSaveParams) => {
    const state = ref(false)
    /**
     * // TODO data save coding
     */
    // 保存数据在 params.data 中
    console.log(params);
    console.log(params.data);
    console.log(params.data.address);
    // 模拟保存状态
    state.value = !randomInt(2, 0)
    if (state.value) {
        // 保存成功， 执行回调, 并刷新 table
        params.callSuccess('哦业， 保存成功了')
        // 数据在列表中不会更新， 这里刷新 table 就好了
        refreshTable()
        // 是否关闭 dialog 对话框根据自己需要
        closeDialog()
    } else {
        // 保存失败， 执行回调
        params.callFailure('哦或， 保存失败了吧')
    }
}

/**
 * dialog 处理
 */
// dialog 状态标志
const dialogFlag = ref(false)
// 开启 dialog
const openDialog = () => {
    dialogFlag.value = true
}
// 关闭 dialog
const closeDialog = () => {
    dialogFlag.value = false
}
// dialog 组件， 务必这样定义， 将暴露接口指出，后续好使用
const dialog = ref<{ refresh: TFormDialogRefresh }>()
// dialog 配置， 通常默认配置即可
const dialogConf: TCDialogProps = reactive({})
/**
 * 操作按钮配置
 */
const buttonConf: TFormDialogButtonConf = {
    buttons: {
        add: {
            "label": "添加",
            "type": "warning",
            callback: () => {
                console.log("callback");
            }
        }
    }
}
// dialog 数据项目定义，参考 @qingbing/ts-v3-xz-form 插件
const dialogItems: TXzFormItem[] = [
    {
        input_type: "input-text",
        field: "username",
        default: "",
        label: "用户名",
        rules: [{ required: true, type: "username" }],
    },
    {
        input_type: "input-select",
        field: "sex",
        default: 2,
        label: "性别",
        rules: [{ type: "required" }],
        exts: {
            options: [
                { value: "0", label: "秘密" },
                { value: "1", label: "女士" },
                { value: "2", label: "男士" },
            ]
        }
    },
    {
        input_type: "input-number",
        field: "age",
        default: 18,
        label: "年龄",
        exts: {
            min: 0,
            max: 127
        },
        rules: [{ required: true, type: "number" }],
    },
    {
        input_type: "input-area",
        field: "address",
        default: "",
        label: "地址",
        rules: [{ required: true }],
    }
]


/**
 * 触发 dialog 的开启
 */
const handleAdd = () => {
    dialog.value && dialog.value.refresh({
        isForm: true,
        title: "添加项目",
        items: dialogItems,
        data: {},
        viewFields: [],
        showFields: [],
        buttons: ["submit", 'reset', "cancel"]
    })
    openDialog()
}
const handleEdit = (row) => {
    dialog.value && dialog.value.refresh({
        isForm: true,
        title: "编辑项目",
        items: dialogItems,
        data: row,
        viewFields: [],
        showFields: [],
        buttons: ["submit", 'reset', "cancel"]
    })
    openDialog()
}
const handleView = (row) => {
    dialog.value && dialog.value.refresh({
        isForm: false,
        title: "查看详情",
        items: dialogItems,
        data: row,
        viewFields: [],
        showFields: [],
        buttons: ["cancel"]
    })
    openDialog()
}
</script>
```