z-crud-table 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # CrudTable - 基于 Vue 3 和 Element Plus 的高级 CRUD 组件
2
+
3
+ `CrudTable` 是一个功能全面、高度可定制的 CRUD 表格组件,旨在通过声明式的方式,极大地简化数据驱动的页面开发。它封装了常见的增、删、改、查、分页、批量操作等逻辑,让开发者能更专注于业务本身。
4
+
5
+ ## ✨ 功能特性
6
+
7
+ - **API驱动**: 通过 props 传入 URL 字符串,组件内置 `axios` 请求逻辑。
8
+ - **高度可定制**: 提供丰富的插槽,用于自定义查询条件、表格列、操作按钮等。
9
+ - **配置驱动表单**: 支持通过 JSON 数组配置动态渲染新增/编辑弹窗内的表单。
10
+ - **完整的 `el-table` 功能**: 继承 `el-table` 所有原生属性和事件,无缝衔接现有使用习惯。
11
+ - **强大的生命周期**: 提供完整的 `onBefore` 和 `onAfter` 钩子,方便在操作前后进行数据处理和逻辑注入。
12
+ - **灵活的列控制**: 可通过 props 单独控制多选框、序号列、操作列以及编辑/删除按钮的显示与隐藏。
13
+ - **开箱即用**: 内置了分页、单行删除、批量删除等常用功能。
14
+
15
+ ## 📦 安装
16
+
17
+ ```
18
+ npm install your-crud-table-package-name
19
+
20
+ ```
21
+
22
+ ## 🚀 快速上手
23
+
24
+ 在您的 Vue 组件中使用 `CrudTable`。
25
+
26
+ ```
27
+ <template>
28
+ <crud-table
29
+ ref="crudTableRef"
30
+ :api-url-query="'/api/users'"
31
+ :api-url-detail="'/api/users/detail'"
32
+ :api-url-create="'/api/users'"
33
+ :api-url-update="'/api/users'"
34
+ :api-url-delete="'/api/users'"
35
+ :initial-search-form="{ pageNum: 1, pageSize: 5 }"
36
+ :dialog-form-config="dialogFormConfig"
37
+ >
38
+ <el-table-column prop="name" label="姓名" />
39
+ <el-table-column prop="email" label="邮箱" />
40
+ </crud-table>
41
+ </template>
42
+
43
+ <script setup lang="ts">
44
+ import { ref } from 'vue';
45
+ import CrudTable from 'your-crud-table-package-name';
46
+
47
+ const crudTableRef = ref(null);
48
+
49
+ const dialogFormConfig = ref([
50
+ { type: 'input', prop: 'name', label: '姓名' },
51
+ { type: 'input', prop: 'email', label: '邮箱' },
52
+ ]);
53
+ </script>
54
+
55
+ ```
56
+
57
+ ## **API 文档**
58
+
59
+ ### **属性 (Props)**
60
+
61
+ | 属性名 | 描述 | 类型 | 是否必需 | 默认值 |
62
+ | --- | --- | --- | --- | --- |
63
+ | **API 配置** | | | | |
64
+ | `apiUrlQuery` | 获取表格列表数据的接口地址。 | `String` | `true` | - |
65
+ | `apiUrlDetail` | 获取单条数据详情的接口地址(用于编辑时回填表单)。 | `String` | `true` | - |
66
+ | `apiUrlCreate` | 创建新条目的接口地址。 | `String` | `true` | - |
67
+ | `apiUrlUpdate` | 更新条目的接口地址。 | `String` | `true` | - |
68
+ | `apiUrlDelete` | 删除条目的接口地址。 | `String` | `true` | - |
69
+ | **功能控制** | | | | |
70
+ | `showSelectionColumn` | 是否显示表格的多选框列。 | `Boolean` | `false` | `true` |
71
+ | `showIndexColumn` | 是否显示表格的序号列。 | `Boolean` | `false` | `true` |
72
+ | `showActionsColumn` | 是否显示操作列。 | `Boolean` | `false` | `true` |
73
+ | `showEditButton` | 是否在操作列中显示默认的“编辑”按钮。 | `Boolean` | `false` | `true` |
74
+ | `showDeleteButton` | 是否在操作列中显示默认的“删除”按钮。 | `Boolean` | `false` | `true` |
75
+ | `actionsColumnWidth` | 操作列的宽度。 | `Number` | `false` | `120` |
76
+ | **表单与弹窗** | | | | |
77
+ | `dialogWidth` | 新增/编辑弹窗的宽度。 | `String` | `false` | `'50%'` |
78
+ | `dialogFormConfig` | **动态表单配置数组**,用于快速生成弹窗内的表单。 | `Array` | `false` | `[]` |
79
+ | `dialogFormRules` | 弹窗表单的 [Element Plus 验证规则](https://element-plus.org/zh-CN/component/form.html#%E8%A1%A8%E5%8D%95%E6%A0%A1%E9%AA%8C)。 | `Object` | `false` | `{}` |
80
+ | **分页配置** | | | | |
81
+ | `initialSearchForm` | 查询表单的初始值,**包含初始分页参数 `pageNum` 和 `pageSize`**。 | `Object` | `false` | `{ pageNum: 1, pageSize: 10 }` |
82
+ | `showPagination` | 是否显示分页组件。 | `Boolean` | `false` | `true` |
83
+ | `pageSizes` | 每页显示个数选择器的选项设置。 | `Array` | `false` | `[10, 20, 50, 100]` |
84
+ | `paginationLayout` | 分页组件布局。 | `String` | `false` | `'total, sizes, prev, pager, next, jumper'` |
85
+ | `paginationBackground` | 是否为分页按钮添加背景色。 | `Boolean` | `false` | `true` |
86
+ | `paginationSmall` | 是否使用小型分页样式。 | `Boolean` | `false` | `false` |
87
+ | `paginationHideOnSinglePage` | 只有一页时是否隐藏分页。 | `Boolean` | `false` | `false` |
88
+ | **生命周期钩子** | | | | |
89
+ | `onBeforeQuery` | 查询请求**前**执行。可用于修改请求参数。**必须返回**处理后的参数对象。 | `Function` | `false` | - |
90
+ | `onAfterQuery` | 查询请求**后**执行。可用于格式化返回的列表数据。**必须返回**处理后的数据数组。 | `Function` | `false` | - |
91
+ | `onBeforeOpenDialog` | 打开弹窗**前**执行。可用于预处理表单数据。 | `Function` | `false` | - |
92
+ | `onAfterOpenDialog` | 打开弹窗**后**执行。可用于在弹窗渲染后执行某些操作。 | `Function` | `false` | - |
93
+ | `onBeforeSubmit` | 表单提交**前**执行。可用于序列化提交的数据。**必须返回**处理后的数据对象。 | `Function` | `false` | - |
94
+ | `onAfterSubmit` | 表单提交**后**执行。可用于执行提交成功后的副作用。 | `Function` | `false` | - |
95
+ | `onBeforeDelete` | 删除操作**前**执行。可用于进行额外的确认。**返回 `false` 可中止删除**。 | `Function` | `false` | - |
96
+ | `onAfterDelete` | 删除操作**后**执行。可用于执行删除成功后的副作用。 | `Function` | `false` | - |
97
+
98
+ ### **插槽 (Slots)**
99
+
100
+ | 插槽名称 | 作用域 (Props) | 描述 |
101
+ | --- | --- | --- |
102
+ | `default` | - | **(核心)** 用于定义表格的列 (`<el-table-column>`),除了序号列、复选框列和操作列。 |
103
+ | `header` | - | 在整个组件最顶部插入内容,如页面大标题。 |
104
+ | `query-conditions` | `{ searchForm }` | 自定义查询区域的表单项。`searchForm` 是响应式的查询对象。 |
105
+ | `query-left` | - | 在“搜索”按钮左侧添加自定义按钮或内容。 |
106
+ | `query-right` | - | 在“清空”按钮右侧添加自定义按钮或内容。 |
107
+ | `action-left` | `{ selections }` | 在“新增”按钮左侧添加自定义按钮,可通过 `selections` 访问当前勾选的数据。 |
108
+ | `action-right` | - | 在“新增”按钮右侧添加自定义按钮。 |
109
+ | `actions` | `{ row }` | **(重要)** 自定义操作列的内容,可用于添加、修改或完全替换默认的编辑/删除按钮。 |
110
+ | `dialog-form-content` | `{ formData, mode }` | **完全自定义**新增/编辑弹窗的表单内容。如果使用此插槽,`dialogFormConfig` 将被忽略。 |
111
+
112
+ ### **事件 (Events)**
113
+
114
+ | 事件名称 | 载荷 (Payload) | 描述 |
115
+ | --- | --- | --- |
116
+ | `@open-dialog` | `{ mode: string, data: object }` | 在新增或编辑弹窗打开后触发。 |
117
+ | `@submit` | `{ mode: string, data: object }` | 在表单提交(新增或更新)成功**后**触发。 |
118
+ | `@delete` | `number[]` | 在删除操作成功**后**触发,载荷为被删除的 ID 数组。 |
119
+
120
+ ### **暴露的方法 (Exposed Methods)**
121
+
122
+ 通过 `ref` 可以获取到组件实例,并调用其暴露的方法。
123
+
124
+ | 方法名称 | 参数 | 描述 |
125
+ | --- | --- | --- |
126
+ | `refresh()` | - | 强制刷新表格数据,使用当前的查询条件和分页状态。 |
127
+ | `search()` | - | 重置到第一页并刷新数据,相当于点击“搜索”按钮。 |
128
+ | `handleDelete(ids: number[])` | `ids` (ID 数组) | 触发删除操作,可用于父组件中自定义的批量删除逻辑。 |
129
+ | `openDialog(mode, data)` | `mode`, `data` | 手动打开新增/编辑弹窗。 |
130
+
131
+ ## **实践案例**
132
+
133
+ ```
134
+ <template>
135
+ <crud-table
136
+ ref="crudTableRef"
137
+ :api-url-query="'/api/users'"
138
+ :api-url-detail="'/api/users/detail'"
139
+ :api-url-create="'/api/users'"
140
+ :api-url-update="'/api/users'"
141
+ :api-url-delete="'/api/users'"
142
+ :show-delete-button="false"
143
+ actions-column-width="140"
144
+ :initial-search-form="{ role: null, pageNum: 1, pageSize: 5 }"
145
+ @submit="onSubmit"
146
+ :on-after-query="handleAfterQuery"
147
+ >
148
+ <template #header>
149
+ <h2 class="text-2xl font-semibold text-slate-700 mb-6">用户管理</h2>
150
+ </template>
151
+
152
+ <el-table-column prop="name" label="姓名" sortable />
153
+ <el-table-column prop="role" label="角色">
154
+ <template #default="scope">
155
+ <el-tag :type="scope.row.role === 'admin' ? 'warning' : 'info'">
156
+ {{ scope.row.role }}
157
+ </el-tag>
158
+ </template>
159
+ </el-table-column>
160
+ <el-table-column prop="createdAtFormatted" label="创建时间" />
161
+
162
+ <template #query-conditions="{ searchForm }">
163
+ <el-form-item label="姓名">
164
+ <el-input v-model="searchForm.name" clearable />
165
+ </el-form-item>
166
+ </template>
167
+
168
+ <template #actions="{ row }">
169
+ <el-button size="small" type="primary" link @click="crudTableRef?.openDialog('edit', row)">编辑</el-button>
170
+ <el-button size="small" type="success" link @click="handleViewDetails(row)">详情</el-button>
171
+ </template>
172
+ </crud-table>
173
+ </template>
174
+
175
+ <script setup>
176
+ import { ref } from 'vue';
177
+ import { ElMessage, ElMessageBox } from 'element-plus';
178
+ import CrudTable from './components/CrudTable.vue';
179
+
180
+ const crudTableRef = ref(null);
181
+
182
+ // 使用生命周期钩子格式化数据
183
+ const handleAfterQuery = (data) => {
184
+ return data.map(item => ({
185
+ ...item,
186
+ createdAtFormatted: new Date(item.createdAt).toLocaleString('zh-CN'),
187
+ }));
188
+ };
189
+
190
+ // 监听事件
191
+ const onSubmit = (payload) => {
192
+ ElMessage.success(`[Event] ${payload.mode} success!`);
193
+ };
194
+
195
+ // 自定义方法
196
+ const handleViewDetails = (row) => {
197
+ ElMessageBox.alert(JSON.stringify(row, null, 2), '详情');
198
+ };
199
+ </script>
200
+ ```
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
Binary file
@@ -0,0 +1,9 @@
1
+ import { default as CrudTable } from './components/CrudTable.vue';
2
+ import { default as DynamicForm } from './components/DynamicForm.vue';
3
+ import { default as request } from './utils/request';
4
+
5
+ export { CrudTable, DynamicForm, request };
6
+ declare const _default: {
7
+ install: (app: App) => void;
8
+ };
9
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const service: any;
2
+ export default service;
@@ -0,0 +1,489 @@
1
+ import { defineComponent as L, ref as z, resolveComponent as g, createBlock as m, openBlock as r, mergeProps as C, withCtx as d, createElementBlock as F, Fragment as O, renderList as q, createCommentVNode as y, createTextVNode as w, toDisplayString as oe, reactive as R, computed as T, onMounted as ae, resolveDirective as le, renderSlot as _, createElementVNode as A, withDirectives as I, createVNode as v } from "vue";
2
+ import { ElMessage as B, ElMessageBox as ne } from "element-plus";
3
+ import re from "axios";
4
+ const ie = /* @__PURE__ */ L({
5
+ __name: "DynamicForm",
6
+ props: {
7
+ modelValue: {},
8
+ formConfig: {},
9
+ rules: {}
10
+ },
11
+ setup(s, { expose: n }) {
12
+ const $ = z(null);
13
+ return n({
14
+ validate: () => {
15
+ var i;
16
+ return (i = $.value) == null ? void 0 : i.validate();
17
+ }
18
+ }), (i, t) => {
19
+ const h = g("el-input"), p = g("el-option"), S = g("el-select"), k = g("el-radio"), U = g("el-radio-group"), x = g("el-form-item"), a = g("el-form");
20
+ return r(), m(a, C({
21
+ model: i.modelValue,
22
+ rules: i.rules,
23
+ ref_key: "formRef",
24
+ ref: $
25
+ }, i.$attrs), {
26
+ default: d(() => [
27
+ (r(!0), F(O, null, q(i.formConfig, (l) => (r(), m(x, {
28
+ key: l.prop,
29
+ label: l.label,
30
+ prop: l.prop
31
+ }, {
32
+ default: d(() => [
33
+ l.type === "input" ? (r(), m(h, C({
34
+ key: 0,
35
+ modelValue: i.modelValue[l.prop],
36
+ "onUpdate:modelValue": (f) => i.modelValue[l.prop] = f
37
+ }, { ref_for: !0 }, l.componentProps), null, 16, ["modelValue", "onUpdate:modelValue"])) : y("", !0),
38
+ l.type === "textarea" ? (r(), m(h, C({
39
+ key: 1,
40
+ type: "textarea",
41
+ modelValue: i.modelValue[l.prop],
42
+ "onUpdate:modelValue": (f) => i.modelValue[l.prop] = f
43
+ }, { ref_for: !0 }, l.componentProps), null, 16, ["modelValue", "onUpdate:modelValue"])) : y("", !0),
44
+ l.type === "select" ? (r(), m(S, C({
45
+ key: 2,
46
+ modelValue: i.modelValue[l.prop],
47
+ "onUpdate:modelValue": (f) => i.modelValue[l.prop] = f
48
+ }, { ref_for: !0 }, l.componentProps), {
49
+ default: d(() => [
50
+ (r(!0), F(O, null, q(l.options, (f) => (r(), m(p, {
51
+ key: f.value,
52
+ label: f.label,
53
+ value: f.value
54
+ }, null, 8, ["label", "value"]))), 128))
55
+ ]),
56
+ _: 2
57
+ }, 1040, ["modelValue", "onUpdate:modelValue"])) : y("", !0),
58
+ l.type === "radio-group" ? (r(), m(U, C({
59
+ key: 3,
60
+ modelValue: i.modelValue[l.prop],
61
+ "onUpdate:modelValue": (f) => i.modelValue[l.prop] = f
62
+ }, { ref_for: !0 }, l.componentProps), {
63
+ default: d(() => [
64
+ (r(!0), F(O, null, q(l.options, (f) => (r(), m(k, {
65
+ key: f.value,
66
+ label: f.value
67
+ }, {
68
+ default: d(() => [
69
+ w(oe(f.label), 1)
70
+ ]),
71
+ _: 2
72
+ }, 1032, ["label"]))), 128))
73
+ ]),
74
+ _: 2
75
+ }, 1040, ["modelValue", "onUpdate:modelValue"])) : y("", !0),
76
+ l.type === "input-disabled" ? (r(), m(h, C({
77
+ key: 4,
78
+ "model-value": i.modelValue[l.prop],
79
+ disabled: ""
80
+ }, { ref_for: !0 }, l.componentProps), null, 16, ["model-value"])) : y("", !0)
81
+ ]),
82
+ _: 2
83
+ }, 1032, ["label", "prop"]))), 128))
84
+ ]),
85
+ _: 1
86
+ }, 16, ["model", "rules"]);
87
+ };
88
+ }
89
+ }), D = re.create({
90
+ baseURL: "",
91
+ // 基础URL可以为空,让API URL成为完整的地址
92
+ timeout: 1e4
93
+ });
94
+ D.interceptors.request.use(
95
+ (s) => {
96
+ const n = localStorage.getItem("token");
97
+ return n && (s.headers.Authorization = "Bearer " + n), s;
98
+ },
99
+ (s) => (console.log(s), Promise.reject(s))
100
+ );
101
+ D.interceptors.response.use(
102
+ (s) => {
103
+ const n = s.data;
104
+ return n.code && n.code !== 200 ? (B({
105
+ message: n.message || "Error",
106
+ type: "error",
107
+ duration: 5 * 1e3
108
+ }), (n.code === 50008 || n.code === 50012 || n.code === 50014) && ne.confirm("您的登录已过期,可以取消以停留在此页面,或重新登录", "确认登出", {
109
+ confirmButtonText: "重新登录",
110
+ cancelButtonText: "取消",
111
+ type: "warning"
112
+ }).then(() => {
113
+ console.log("Redirecting to login page...");
114
+ }), Promise.reject(new Error(n.message || "Error"))) : n;
115
+ },
116
+ (s) => {
117
+ console.log("err: " + s);
118
+ let n = "接口错误,请刷新接口";
119
+ if (s.response)
120
+ switch (s.response.status) {
121
+ case 400:
122
+ n = "请求错误 (400)";
123
+ break;
124
+ case 401:
125
+ n = "未授权,请重新登录 (401)";
126
+ break;
127
+ case 403:
128
+ n = "拒绝访问 (403)";
129
+ break;
130
+ case 404:
131
+ n = "请求资源不存在 (404)";
132
+ break;
133
+ case 500:
134
+ n = "服务器内部错误 (500)";
135
+ break;
136
+ default:
137
+ n = `连接错误 (${s.response.status})!`;
138
+ }
139
+ return B({
140
+ message: n,
141
+ type: "error",
142
+ duration: 5 * 1e3
143
+ }), Promise.reject(s);
144
+ }
145
+ );
146
+ const se = { class: "crud-table-wrapper" }, ue = { class: "flex flex-wrap items-center justify-between gap-4 mb-6" }, de = { class: "flex items-center gap-x-2" }, pe = { class: "flex items-center gap-x-3 action-buttons flex-shrink-0" }, fe = { class: "flex items-center gap-x-2" }, me = {
147
+ key: 0,
148
+ class: "flex justify-end mt-[10px]"
149
+ }, ce = { class: "dialog-footer" }, ge = /* @__PURE__ */ L({
150
+ __name: "CrudTable",
151
+ props: {
152
+ // API URLs
153
+ apiUrlQuery: { type: String, required: !0 },
154
+ apiUrlDetail: { type: String, required: !0 },
155
+ apiUrlCreate: { type: String, required: !0 },
156
+ apiUrlUpdate: { type: String, required: !0 },
157
+ apiUrlDelete: { type: String, required: !0 },
158
+ // Lifecycle Hooks
159
+ onBeforeQuery: { type: Function },
160
+ onAfterQuery: { type: Function },
161
+ onBeforeOpenDialog: { type: Function },
162
+ onAfterOpenDialog: { type: Function },
163
+ onBeforeSubmit: { type: Function },
164
+ onAfterSubmit: { type: Function },
165
+ onBeforeDelete: { type: Function },
166
+ onAfterDelete: { type: Function },
167
+ // Column Visibility
168
+ showSelectionColumn: { type: Boolean, default: !0 },
169
+ showIndexColumn: { type: Boolean, default: !0 },
170
+ showActionsColumn: { type: Boolean, default: !0 },
171
+ showEditButton: { type: Boolean, default: !0 },
172
+ showDeleteButton: { type: Boolean, default: !0 },
173
+ actionsColumnWidth: { type: Number, default: 120 },
174
+ // Other Props
175
+ dialogWidth: { type: String, default: "50%" },
176
+ initialSearchForm: { type: Object, default: () => ({ pageNum: 1, pageSize: 10 }) },
177
+ showPagination: { type: Boolean, default: !0 },
178
+ pageSizes: { type: Array, default: () => [10, 20, 50, 100] },
179
+ paginationLayout: { type: String, default: "total, sizes, prev, pager, next, jumper" },
180
+ paginationBackground: { type: Boolean, default: !0 },
181
+ paginationSmall: { type: Boolean, default: !1 },
182
+ paginationHideOnSinglePage: { type: Boolean, default: !1 },
183
+ dialogFormConfig: { type: Array, default: () => [] },
184
+ dialogFormRules: { type: Object, default: () => ({}) }
185
+ },
186
+ emits: ["open-dialog", "submit", "delete"],
187
+ setup(s, { expose: n, emit: $ }) {
188
+ const i = $, t = s, h = (e, o) => e ? !0 : (B.error(`${o} prop is required.`), !1), p = R({ pageNum: 1, pageSize: 10, ...t.initialSearchForm }), S = z([]), k = z(0), U = z(!1), x = z([]), a = R({ visible: !1, loading: !1, submitting: !1, mode: "add", data: {}, formRef: null }), l = T(() => a.mode === "add" ? "新增" : "编辑"), f = T(() => {
189
+ if (a.mode === "add") return t.dialogFormConfig.filter((o) => o.prop !== "id");
190
+ const e = [...t.dialogFormConfig.filter((o) => o.prop !== "id")];
191
+ return e.some((o) => o.prop === "id") || e.push({ type: "input-disabled", prop: "id", label: "用户ID" }), e;
192
+ }), V = async () => {
193
+ if (h(t.apiUrlQuery, "apiUrlQuery")) {
194
+ U.value = !0;
195
+ try {
196
+ let e = { ...p };
197
+ t.onBeforeQuery && (e = await t.onBeforeQuery(e));
198
+ const o = await D.get(t.apiUrlQuery, { params: e });
199
+ if (o && Array.isArray(o.data) && typeof o.total == "number") {
200
+ let u = o.data;
201
+ t.onAfterQuery && (u = await t.onAfterQuery(u)), S.value = u, k.value = o.total;
202
+ } else
203
+ console.warn("API response is not in the expected { data: [], total: 0 } format."), S.value = [], k.value = 0;
204
+ } catch (e) {
205
+ console.error("Fetch data failed:", e);
206
+ } finally {
207
+ U.value = !1;
208
+ }
209
+ }
210
+ }, P = () => {
211
+ p.pageNum = 1, V();
212
+ }, W = () => {
213
+ const { pageNum: e, pageSize: o, ...u } = t.initialSearchForm;
214
+ Object.keys(p).forEach((b) => {
215
+ b !== "pageNum" && b !== "pageSize" && delete p[b];
216
+ }), Object.assign(p, u), P();
217
+ }, M = (e) => {
218
+ x.value = e;
219
+ }, j = async (e, o) => {
220
+ let u = e === "add" ? { role: "user" } : { ...o };
221
+ if (t.onBeforeOpenDialog) {
222
+ const b = await t.onBeforeOpenDialog(e, u);
223
+ b && (u = b);
224
+ }
225
+ if (a.mode = e, a.visible = !0, e === "edit") {
226
+ if (!h(t.apiUrlDetail, "apiUrlDetail")) return;
227
+ a.loading = !0;
228
+ try {
229
+ const b = await D.get(t.apiUrlDetail + "/" + u.id);
230
+ a.data = b.data;
231
+ } finally {
232
+ a.loading = !1, t.onAfterOpenDialog && t.onAfterOpenDialog(e, a.data), i("open-dialog", { mode: e, data: a.data });
233
+ }
234
+ } else
235
+ a.data = u, t.onAfterOpenDialog && t.onAfterOpenDialog(e, a.data), i("open-dialog", { mode: e, data: a.data });
236
+ }, H = async () => {
237
+ try {
238
+ a.formRef && await a.formRef.validate();
239
+ let e = { ...a.data };
240
+ if (t.onBeforeSubmit && (e = await t.onBeforeSubmit(e)), a.submitting = !0, a.mode === "add") {
241
+ if (!h(t.apiUrlCreate, "apiUrlCreate")) return;
242
+ await D.post(t.apiUrlCreate, e), B.success("新增成功");
243
+ } else {
244
+ if (!h(t.apiUrlUpdate, "apiUrlUpdate")) return;
245
+ await D.put(t.apiUrlUpdate, e), B.success("更新成功");
246
+ }
247
+ t.onAfterSubmit && t.onAfterSubmit(a.mode, e), i("submit", { mode: a.mode, data: e }), a.visible = !1, V();
248
+ } catch (e) {
249
+ console.log("Submit error or validation failed:", e);
250
+ } finally {
251
+ a.submitting = !1;
252
+ }
253
+ }, E = async (e) => {
254
+ if (h(t.apiUrlDelete, "apiUrlDelete"))
255
+ try {
256
+ if (t.onBeforeDelete && await t.onBeforeDelete(e) === !1)
257
+ return;
258
+ const o = e.join(",");
259
+ await D.delete(t.apiUrlDelete + "/" + o), B.success("删除成功"), t.onAfterDelete && t.onAfterDelete(e), i("delete", e), S.value.length === e.length && p.pageNum > 1 && p.pageNum--, V();
260
+ } catch (o) {
261
+ console.error("Delete failed", o);
262
+ }
263
+ }, G = (e) => {
264
+ p.pageSize = e, P();
265
+ }, J = (e) => {
266
+ p.pageNum = e, V();
267
+ };
268
+ return ae(V), n({
269
+ refresh: V,
270
+ search: P,
271
+ handleDelete: E
272
+ }), (e, o) => {
273
+ const u = g("el-button"), b = g("el-form-item"), K = g("el-form"), N = g("el-table-column"), X = g("el-popconfirm"), Y = g("el-table"), Z = g("el-pagination"), ee = g("el-dialog"), Q = le("loading");
274
+ return r(), F("div", se, [
275
+ _(e.$slots, "header"),
276
+ A("div", ue, [
277
+ v(K, {
278
+ model: p,
279
+ class: "query-form flex flex-nowrap items-center gap-x-4",
280
+ style: { "overflow-x": "auto", "padding-bottom": "8px" }
281
+ }, {
282
+ default: d(() => [
283
+ _(e.$slots, "query-conditions", { searchForm: p }),
284
+ v(b, { class: "!mr-0 flex-shrink-0" }, {
285
+ default: d(() => [
286
+ A("div", de, [
287
+ _(e.$slots, "query-left"),
288
+ v(u, {
289
+ type: "primary",
290
+ onClick: P,
291
+ loading: U.value
292
+ }, {
293
+ default: d(() => o[6] || (o[6] = [
294
+ w("搜索")
295
+ ])),
296
+ _: 1,
297
+ __: [6]
298
+ }, 8, ["loading"]),
299
+ v(u, { onClick: W }, {
300
+ default: d(() => o[7] || (o[7] = [
301
+ w("清空")
302
+ ])),
303
+ _: 1,
304
+ __: [7]
305
+ }),
306
+ _(e.$slots, "query-right")
307
+ ])
308
+ ]),
309
+ _: 3
310
+ })
311
+ ]),
312
+ _: 3
313
+ }, 8, ["model"]),
314
+ A("div", pe, [
315
+ _(e.$slots, "action-left", { selections: x.value }),
316
+ _(e.$slots, "add-button-content", {}, () => [
317
+ v(u, {
318
+ type: "success",
319
+ onClick: o[0] || (o[0] = (c) => j("add"))
320
+ }, {
321
+ default: d(() => o[8] || (o[8] = [
322
+ w("新增")
323
+ ])),
324
+ _: 1,
325
+ __: [8]
326
+ })
327
+ ]),
328
+ _(e.$slots, "action-right")
329
+ ])
330
+ ]),
331
+ I((r(), m(Y, C({
332
+ data: S.value,
333
+ onSelectionChange: M
334
+ }, e.$attrs, { style: { width: "100%" } }), {
335
+ default: d(() => [
336
+ t.showSelectionColumn ? (r(), m(N, {
337
+ key: 0,
338
+ type: "selection",
339
+ width: "55",
340
+ fixed: ""
341
+ })) : y("", !0),
342
+ t.showIndexColumn ? (r(), m(N, {
343
+ key: 1,
344
+ type: "index",
345
+ label: "序号",
346
+ width: "70",
347
+ fixed: ""
348
+ })) : y("", !0),
349
+ _(e.$slots, "default"),
350
+ t.showActionsColumn ? (r(), m(N, {
351
+ key: 2,
352
+ label: "操作",
353
+ width: s.actionsColumnWidth,
354
+ fixed: "right"
355
+ }, {
356
+ default: d((c) => [
357
+ A("div", fe, [
358
+ _(e.$slots, "actions", {
359
+ row: c.row
360
+ }, () => [
361
+ t.showEditButton ? (r(), m(u, {
362
+ key: 0,
363
+ size: "small",
364
+ type: "primary",
365
+ link: "",
366
+ onClick: (te) => j("edit", c.row)
367
+ }, {
368
+ default: d(() => o[9] || (o[9] = [
369
+ w("编辑")
370
+ ])),
371
+ _: 2,
372
+ __: [9]
373
+ }, 1032, ["onClick"])) : y("", !0),
374
+ t.showDeleteButton ? (r(), m(X, {
375
+ key: 1,
376
+ title: "确定要删除这条数据吗?",
377
+ onConfirm: (te) => E([c.row.id]),
378
+ "confirm-button-text": "确定",
379
+ "cancel-button-text": "取消",
380
+ width: "200"
381
+ }, {
382
+ reference: d(() => [
383
+ v(u, {
384
+ size: "small",
385
+ type: "danger",
386
+ link: ""
387
+ }, {
388
+ default: d(() => o[10] || (o[10] = [
389
+ w("删除")
390
+ ])),
391
+ _: 1,
392
+ __: [10]
393
+ })
394
+ ]),
395
+ _: 2
396
+ }, 1032, ["onConfirm"])) : y("", !0)
397
+ ])
398
+ ])
399
+ ]),
400
+ _: 3
401
+ }, 8, ["width"])) : y("", !0)
402
+ ]),
403
+ _: 3
404
+ }, 16, ["data"])), [
405
+ [Q, U.value]
406
+ ]),
407
+ t.showPagination && k.value > 0 ? (r(), F("div", me, [
408
+ v(Z, {
409
+ "current-page": p.pageNum,
410
+ "onUpdate:currentPage": o[1] || (o[1] = (c) => p.pageNum = c),
411
+ "page-size": p.pageSize,
412
+ "onUpdate:pageSize": o[2] || (o[2] = (c) => p.pageSize = c),
413
+ "page-sizes": t.pageSizes,
414
+ layout: t.paginationLayout,
415
+ total: k.value,
416
+ background: t.paginationBackground,
417
+ small: t.paginationSmall,
418
+ "hide-on-single-page": t.paginationHideOnSinglePage,
419
+ onSizeChange: G,
420
+ onCurrentChange: J
421
+ }, null, 8, ["current-page", "page-size", "page-sizes", "layout", "total", "background", "small", "hide-on-single-page"])
422
+ ])) : y("", !0),
423
+ v(ee, {
424
+ modelValue: a.visible,
425
+ "onUpdate:modelValue": o[5] || (o[5] = (c) => a.visible = c),
426
+ title: l.value,
427
+ width: t.dialogWidth,
428
+ "destroy-on-close": !0
429
+ }, {
430
+ footer: d(() => [
431
+ A("div", ce, [
432
+ v(u, {
433
+ onClick: o[4] || (o[4] = (c) => a.visible = !1)
434
+ }, {
435
+ default: d(() => o[11] || (o[11] = [
436
+ w("取消")
437
+ ])),
438
+ _: 1,
439
+ __: [11]
440
+ }),
441
+ v(u, {
442
+ type: "primary",
443
+ onClick: H,
444
+ loading: a.submitting
445
+ }, {
446
+ default: d(() => o[12] || (o[12] = [
447
+ w("确定")
448
+ ])),
449
+ _: 1,
450
+ __: [12]
451
+ }, 8, ["loading"])
452
+ ])
453
+ ]),
454
+ default: d(() => [
455
+ I((r(), F("div", null, [
456
+ _(e.$slots, "dialog-form-content", {
457
+ formData: a.data,
458
+ mode: a.mode
459
+ }, () => [
460
+ t.dialogFormConfig.length > 0 ? (r(), m(ie, {
461
+ key: 0,
462
+ "form-config": f.value,
463
+ modelValue: a.data,
464
+ "onUpdate:modelValue": o[3] || (o[3] = (c) => a.data = c),
465
+ ref: (c) => a.formRef = c,
466
+ rules: t.dialogFormRules,
467
+ "label-width": "80px"
468
+ }, null, 8, ["form-config", "modelValue", "rules"])) : y("", !0)
469
+ ])
470
+ ])), [
471
+ [Q, a.loading]
472
+ ])
473
+ ]),
474
+ _: 3
475
+ }, 8, ["modelValue", "title", "width"])
476
+ ]);
477
+ };
478
+ }
479
+ }), ve = {
480
+ install: (s) => {
481
+ s.component("CrudTable", ge);
482
+ }
483
+ };
484
+ export {
485
+ ge as CrudTable,
486
+ ie as DynamicForm,
487
+ ve as default,
488
+ D as request
489
+ };
@@ -0,0 +1 @@
1
+ (function(f,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("element-plus"),require("axios")):typeof define=="function"&&define.amd?define(["exports","vue","element-plus","axios"],e):(f=typeof globalThis<"u"?globalThis:f||self,e(f.Vue3CrudTable={},f.Vue,f.ElementPlus,f.axios))})(this,function(f,e,C,E){"use strict";const N=e.defineComponent({__name:"DynamicForm",props:{modelValue:{},formConfig:{},rules:{}},setup(s,{expose:r}){const b=e.ref(null);return r({validate:()=>{var i;return(i=b.value)==null?void 0:i.validate()}}),(i,o)=>{const u=e.resolveComponent("el-input"),p=e.resolveComponent("el-option"),k=e.resolveComponent("el-select"),h=e.resolveComponent("el-radio"),B=e.resolveComponent("el-radio-group"),_=e.resolveComponent("el-form-item"),a=e.resolveComponent("el-form");return e.openBlock(),e.createBlock(a,e.mergeProps({model:i.modelValue,rules:i.rules,ref_key:"formRef",ref:b},i.$attrs),{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(i.formConfig,n=>(e.openBlock(),e.createBlock(_,{key:n.prop,label:n.label,prop:n.prop},{default:e.withCtx(()=>[n.type==="input"?(e.openBlock(),e.createBlock(u,e.mergeProps({key:0,modelValue:i.modelValue[n.prop],"onUpdate:modelValue":c=>i.modelValue[n.prop]=c},{ref_for:!0},n.componentProps),null,16,["modelValue","onUpdate:modelValue"])):e.createCommentVNode("",!0),n.type==="textarea"?(e.openBlock(),e.createBlock(u,e.mergeProps({key:1,type:"textarea",modelValue:i.modelValue[n.prop],"onUpdate:modelValue":c=>i.modelValue[n.prop]=c},{ref_for:!0},n.componentProps),null,16,["modelValue","onUpdate:modelValue"])):e.createCommentVNode("",!0),n.type==="select"?(e.openBlock(),e.createBlock(k,e.mergeProps({key:2,modelValue:i.modelValue[n.prop],"onUpdate:modelValue":c=>i.modelValue[n.prop]=c},{ref_for:!0},n.componentProps),{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(n.options,c=>(e.openBlock(),e.createBlock(p,{key:c.value,label:c.label,value:c.value},null,8,["label","value"]))),128))]),_:2},1040,["modelValue","onUpdate:modelValue"])):e.createCommentVNode("",!0),n.type==="radio-group"?(e.openBlock(),e.createBlock(B,e.mergeProps({key:3,modelValue:i.modelValue[n.prop],"onUpdate:modelValue":c=>i.modelValue[n.prop]=c},{ref_for:!0},n.componentProps),{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(n.options,c=>(e.openBlock(),e.createBlock(h,{key:c.value,label:c.value},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(c.label),1)]),_:2},1032,["label"]))),128))]),_:2},1040,["modelValue","onUpdate:modelValue"])):e.createCommentVNode("",!0),n.type==="input-disabled"?(e.openBlock(),e.createBlock(u,e.mergeProps({key:4,"model-value":i.modelValue[n.prop],disabled:""},{ref_for:!0},n.componentProps),null,16,["model-value"])):e.createCommentVNode("",!0)]),_:2},1032,["label","prop"]))),128))]),_:1},16,["model","rules"])}}}),y=E.create({baseURL:"",timeout:1e4});y.interceptors.request.use(s=>{const r=localStorage.getItem("token");return r&&(s.headers.Authorization="Bearer "+r),s},s=>(console.log(s),Promise.reject(s))),y.interceptors.response.use(s=>{const r=s.data;return r.code&&r.code!==200?(C.ElMessage({message:r.message||"Error",type:"error",duration:5*1e3}),(r.code===50008||r.code===50012||r.code===50014)&&C.ElMessageBox.confirm("您的登录已过期,可以取消以停留在此页面,或重新登录","确认登出",{confirmButtonText:"重新登录",cancelButtonText:"取消",type:"warning"}).then(()=>{console.log("Redirecting to login page...")}),Promise.reject(new Error(r.message||"Error"))):r},s=>{console.log("err: "+s);let r="接口错误,请刷新接口";if(s.response)switch(s.response.status){case 400:r="请求错误 (400)";break;case 401:r="未授权,请重新登录 (401)";break;case 403:r="拒绝访问 (403)";break;case 404:r="请求资源不存在 (404)";break;case 500:r="服务器内部错误 (500)";break;default:r=`连接错误 (${s.response.status})!`}return C.ElMessage({message:r,type:"error",duration:5*1e3}),Promise.reject(s)});const A={class:"crud-table-wrapper"},z={class:"flex flex-wrap items-center justify-between gap-4 mb-6"},$={class:"flex items-center gap-x-2"},P={class:"flex items-center gap-x-3 action-buttons flex-shrink-0"},T={class:"flex items-center gap-x-2"},q={key:0,class:"flex justify-end mt-[10px]"},O={class:"dialog-footer"},D=e.defineComponent({__name:"CrudTable",props:{apiUrlQuery:{type:String,required:!0},apiUrlDetail:{type:String,required:!0},apiUrlCreate:{type:String,required:!0},apiUrlUpdate:{type:String,required:!0},apiUrlDelete:{type:String,required:!0},onBeforeQuery:{type:Function},onAfterQuery:{type:Function},onBeforeOpenDialog:{type:Function},onAfterOpenDialog:{type:Function},onBeforeSubmit:{type:Function},onAfterSubmit:{type:Function},onBeforeDelete:{type:Function},onAfterDelete:{type:Function},showSelectionColumn:{type:Boolean,default:!0},showIndexColumn:{type:Boolean,default:!0},showActionsColumn:{type:Boolean,default:!0},showEditButton:{type:Boolean,default:!0},showDeleteButton:{type:Boolean,default:!0},actionsColumnWidth:{type:Number,default:120},dialogWidth:{type:String,default:"50%"},initialSearchForm:{type:Object,default:()=>({pageNum:1,pageSize:10})},showPagination:{type:Boolean,default:!0},pageSizes:{type:Array,default:()=>[10,20,50,100]},paginationLayout:{type:String,default:"total, sizes, prev, pager, next, jumper"},paginationBackground:{type:Boolean,default:!0},paginationSmall:{type:Boolean,default:!1},paginationHideOnSinglePage:{type:Boolean,default:!1},dialogFormConfig:{type:Array,default:()=>[]},dialogFormRules:{type:Object,default:()=>({})}},emits:["open-dialog","submit","delete"],setup(s,{expose:r,emit:b}){const i=b,o=s,u=(t,l)=>t?!0:(C.ElMessage.error(`${l} prop is required.`),!1),p=e.reactive({pageNum:1,pageSize:10,...o.initialSearchForm}),k=e.ref([]),h=e.ref(0),B=e.ref(!1),_=e.ref([]),a=e.reactive({visible:!1,loading:!1,submitting:!1,mode:"add",data:{},formRef:null}),n=e.computed(()=>a.mode==="add"?"新增":"编辑"),c=e.computed(()=>{if(a.mode==="add")return o.dialogFormConfig.filter(l=>l.prop!=="id");const t=[...o.dialogFormConfig.filter(l=>l.prop!=="id")];return t.some(l=>l.prop==="id")||t.push({type:"input-disabled",prop:"id",label:"用户ID"}),t}),V=async()=>{if(u(o.apiUrlQuery,"apiUrlQuery")){B.value=!0;try{let t={...p};o.onBeforeQuery&&(t=await o.onBeforeQuery(t));const l=await y.get(o.apiUrlQuery,{params:t});if(l&&Array.isArray(l.data)&&typeof l.total=="number"){let d=l.data;o.onAfterQuery&&(d=await o.onAfterQuery(d)),k.value=d,h.value=l.total}else console.warn("API response is not in the expected { data: [], total: 0 } format."),k.value=[],h.value=0}catch(t){console.error("Fetch data failed:",t)}finally{B.value=!1}}},w=()=>{p.pageNum=1,V()},M=()=>{const{pageNum:t,pageSize:l,...d}=o.initialSearchForm;Object.keys(p).forEach(g=>{g!=="pageNum"&&g!=="pageSize"&&delete p[g]}),Object.assign(p,d),w()},Q=t=>{_.value=t},x=async(t,l)=>{let d=t==="add"?{role:"user"}:{...l};if(o.onBeforeOpenDialog){const g=await o.onBeforeOpenDialog(t,d);g&&(d=g)}if(a.mode=t,a.visible=!0,t==="edit"){if(!u(o.apiUrlDetail,"apiUrlDetail"))return;a.loading=!0;try{const g=await y.get(o.apiUrlDetail+"/"+d.id);a.data=g.data}finally{a.loading=!1,o.onAfterOpenDialog&&o.onAfterOpenDialog(t,a.data),i("open-dialog",{mode:t,data:a.data})}}else a.data=d,o.onAfterOpenDialog&&o.onAfterOpenDialog(t,a.data),i("open-dialog",{mode:t,data:a.data})},R=async()=>{try{a.formRef&&await a.formRef.validate();let t={...a.data};if(o.onBeforeSubmit&&(t=await o.onBeforeSubmit(t)),a.submitting=!0,a.mode==="add"){if(!u(o.apiUrlCreate,"apiUrlCreate"))return;await y.post(o.apiUrlCreate,t),C.ElMessage.success("新增成功")}else{if(!u(o.apiUrlUpdate,"apiUrlUpdate"))return;await y.put(o.apiUrlUpdate,t),C.ElMessage.success("更新成功")}o.onAfterSubmit&&o.onAfterSubmit(a.mode,t),i("submit",{mode:a.mode,data:t}),a.visible=!1,V()}catch(t){console.log("Submit error or validation failed:",t)}finally{a.submitting=!1}},U=async t=>{if(u(o.apiUrlDelete,"apiUrlDelete"))try{if(o.onBeforeDelete&&await o.onBeforeDelete(t)===!1)return;const l=t.join(",");await y.delete(o.apiUrlDelete+"/"+l),C.ElMessage.success("删除成功"),o.onAfterDelete&&o.onAfterDelete(t),i("delete",t),k.value.length===t.length&&p.pageNum>1&&p.pageNum--,V()}catch(l){console.error("Delete failed",l)}},L=t=>{p.pageSize=t,w()},I=t=>{p.pageNum=t,V()};return e.onMounted(V),r({refresh:V,search:w,handleDelete:U}),(t,l)=>{const d=e.resolveComponent("el-button"),g=e.resolveComponent("el-form-item"),W=e.resolveComponent("el-form"),S=e.resolveComponent("el-table-column"),H=e.resolveComponent("el-popconfirm"),G=e.resolveComponent("el-table"),J=e.resolveComponent("el-pagination"),K=e.resolveComponent("el-dialog"),F=e.resolveDirective("loading");return e.openBlock(),e.createElementBlock("div",A,[e.renderSlot(t.$slots,"header"),e.createElementVNode("div",z,[e.createVNode(W,{model:p,class:"query-form flex flex-nowrap items-center gap-x-4",style:{"overflow-x":"auto","padding-bottom":"8px"}},{default:e.withCtx(()=>[e.renderSlot(t.$slots,"query-conditions",{searchForm:p}),e.createVNode(g,{class:"!mr-0 flex-shrink-0"},{default:e.withCtx(()=>[e.createElementVNode("div",$,[e.renderSlot(t.$slots,"query-left"),e.createVNode(d,{type:"primary",onClick:w,loading:B.value},{default:e.withCtx(()=>l[6]||(l[6]=[e.createTextVNode("搜索")])),_:1,__:[6]},8,["loading"]),e.createVNode(d,{onClick:M},{default:e.withCtx(()=>l[7]||(l[7]=[e.createTextVNode("清空")])),_:1,__:[7]}),e.renderSlot(t.$slots,"query-right")])]),_:3})]),_:3},8,["model"]),e.createElementVNode("div",P,[e.renderSlot(t.$slots,"action-left",{selections:_.value}),e.renderSlot(t.$slots,"add-button-content",{},()=>[e.createVNode(d,{type:"success",onClick:l[0]||(l[0]=m=>x("add"))},{default:e.withCtx(()=>l[8]||(l[8]=[e.createTextVNode("新增")])),_:1,__:[8]})]),e.renderSlot(t.$slots,"action-right")])]),e.withDirectives((e.openBlock(),e.createBlock(G,e.mergeProps({data:k.value,onSelectionChange:Q},t.$attrs,{style:{width:"100%"}}),{default:e.withCtx(()=>[o.showSelectionColumn?(e.openBlock(),e.createBlock(S,{key:0,type:"selection",width:"55",fixed:""})):e.createCommentVNode("",!0),o.showIndexColumn?(e.openBlock(),e.createBlock(S,{key:1,type:"index",label:"序号",width:"70",fixed:""})):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"default"),o.showActionsColumn?(e.openBlock(),e.createBlock(S,{key:2,label:"操作",width:s.actionsColumnWidth,fixed:"right"},{default:e.withCtx(m=>[e.createElementVNode("div",T,[e.renderSlot(t.$slots,"actions",{row:m.row},()=>[o.showEditButton?(e.openBlock(),e.createBlock(d,{key:0,size:"small",type:"primary",link:"",onClick:X=>x("edit",m.row)},{default:e.withCtx(()=>l[9]||(l[9]=[e.createTextVNode("编辑")])),_:2,__:[9]},1032,["onClick"])):e.createCommentVNode("",!0),o.showDeleteButton?(e.openBlock(),e.createBlock(H,{key:1,title:"确定要删除这条数据吗?",onConfirm:X=>U([m.row.id]),"confirm-button-text":"确定","cancel-button-text":"取消",width:"200"},{reference:e.withCtx(()=>[e.createVNode(d,{size:"small",type:"danger",link:""},{default:e.withCtx(()=>l[10]||(l[10]=[e.createTextVNode("删除")])),_:1,__:[10]})]),_:2},1032,["onConfirm"])):e.createCommentVNode("",!0)])])]),_:3},8,["width"])):e.createCommentVNode("",!0)]),_:3},16,["data"])),[[F,B.value]]),o.showPagination&&h.value>0?(e.openBlock(),e.createElementBlock("div",q,[e.createVNode(J,{"current-page":p.pageNum,"onUpdate:currentPage":l[1]||(l[1]=m=>p.pageNum=m),"page-size":p.pageSize,"onUpdate:pageSize":l[2]||(l[2]=m=>p.pageSize=m),"page-sizes":o.pageSizes,layout:o.paginationLayout,total:h.value,background:o.paginationBackground,small:o.paginationSmall,"hide-on-single-page":o.paginationHideOnSinglePage,onSizeChange:L,onCurrentChange:I},null,8,["current-page","page-size","page-sizes","layout","total","background","small","hide-on-single-page"])])):e.createCommentVNode("",!0),e.createVNode(K,{modelValue:a.visible,"onUpdate:modelValue":l[5]||(l[5]=m=>a.visible=m),title:n.value,width:o.dialogWidth,"destroy-on-close":!0},{footer:e.withCtx(()=>[e.createElementVNode("div",O,[e.createVNode(d,{onClick:l[4]||(l[4]=m=>a.visible=!1)},{default:e.withCtx(()=>l[11]||(l[11]=[e.createTextVNode("取消")])),_:1,__:[11]}),e.createVNode(d,{type:"primary",onClick:R,loading:a.submitting},{default:e.withCtx(()=>l[12]||(l[12]=[e.createTextVNode("确定")])),_:1,__:[12]},8,["loading"])])]),default:e.withCtx(()=>[e.withDirectives((e.openBlock(),e.createElementBlock("div",null,[e.renderSlot(t.$slots,"dialog-form-content",{formData:a.data,mode:a.mode},()=>[o.dialogFormConfig.length>0?(e.openBlock(),e.createBlock(N,{key:0,"form-config":c.value,modelValue:a.data,"onUpdate:modelValue":l[3]||(l[3]=m=>a.data=m),ref:m=>a.formRef=m,rules:o.dialogFormRules,"label-width":"80px"},null,8,["form-config","modelValue","rules"])):e.createCommentVNode("",!0)])])),[[F,a.loading]])]),_:3},8,["modelValue","title","width"])])}}}),j={install:s=>{s.component("CrudTable",D)}};f.CrudTable=D,f.DynamicForm=N,f.default=j,f.request=y,Object.defineProperties(f,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "z-crud-table",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./dist/vue3-crud-table.umd.cjs",
7
+ "module": "./dist/vue3-crud-table.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/vue3-crud-table.js",
12
+ "require": "./dist/vue3-crud-table.umd.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "dev": "vite",
20
+ "build": "vue-tsc && vite build",
21
+ "preview": "vite preview",
22
+ "build-only": "vite build",
23
+ "type-check": "vue-tsc --build"
24
+ },
25
+ "dependencies": {
26
+ "axios": "^1.7.2"
27
+ },
28
+ "peerDependencies": {
29
+ "element-plus": ">=2.0.0",
30
+ "vue": ">=3.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@tsconfig/node22": "^22.0.1",
34
+ "@types/node": "^22.14.0",
35
+ "@vitejs/plugin-vue": "^5.2.3",
36
+ "@vue/tsconfig": "^0.7.0",
37
+ "autoprefixer": "^10.4.19",
38
+ "element-plus": "^2.10.2",
39
+ "npm-run-all2": "^7.0.2",
40
+ "postcss": "^8.4.38",
41
+ "tailwindcss": "^3.4.4",
42
+ "typescript": "~5.4.5",
43
+ "vite": "^5.2.0",
44
+ "vite-plugin-dts": "^3.9.1",
45
+ "vite-plugin-vue-devtools": "^7.7.2",
46
+ "vue": "^3.5.13",
47
+ "vue-tsc": "^2.0.21"
48
+ }
49
+ }