Parcourir la source

😎1、优化集成FastCrud低代码模式 2、升级npm依赖

zuohuaijun il y a 1 an
Parent
commit
87865c2a65

+ 1 - 1
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -38,7 +38,7 @@
     <PackageReference Include="SqlSugarCore" Version="5.1.4.166" />
     <PackageReference Include="SSH.NET" Version="2024.1.0" />
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.4" />
-    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1061" />
+    <PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1062" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
   </ItemGroup>

+ 1 - 1
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -193,7 +193,7 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             new SysMenu{ Id=1310000000621, Pid=1310000000601, Title="代码生成", Path="/develop/codeGen", Name="sysCodeGen", Component="/system/codeGen/index", Icon="ele-Crop", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=110 },
             new SysMenu{ Id=1310000000631, Pid=1310000000601, Title="表单设计", Path="/develop/formDes", Name="sysFormDes", Component="/system/formDes/index", Icon="ele-Edit", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=120 },
             new SysMenu{ Id=1310000000641, Pid=1310000000601, Title="系统接口", Path="/develop/api", Name="sysApi", Component="layout/routerView/iframe", IsIframe=true, OutLink="http://localhost:5005", Icon="ele-Help", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=130 },
-            new SysMenu{ Id=1310000000651, Pid=1310000000601, Title="Curd仔专用", Path="/develop/crud", Name="sysCrud", Component="/system/crud/index",  Icon="ele-Edit", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=121},
+            new SysMenu{ Id=1310000000681, Pid=1310000000601, Title="FastCrud", Path="/develop/fastCrud", Name="sysFastCrud", Component="/system/fastCrud/index",  Icon="ele-CoffeeCup", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=150},
 
             new SysMenu{ Id=1310000000701, Pid=0, Title="帮助文档", Path="/doc", Name="doc", Component="Layout", Icon="ele-Notebook", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=14000 },
             new SysMenu{ Id=1310000000711, Pid=1310000000701, Title="框架教程", Path="/doc/admin", Name="sysAdmin", Component="layout/routerView/link", IsIframe=false, IsKeepAlive=false, OutLink="http://101.43.53.74:5050/", Icon="ele-Sunny", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

+ 4 - 5
Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs

@@ -66,11 +66,10 @@ public class SysAuthService : IDynamicApiController, ITransient
         var keyPasswordErrorTimes = $"{CacheConst.KeyPasswordErrorTimes}{input.Account}";
         var passwordErrorTimes = _sysCacheService.Get<int>(keyPasswordErrorTimes);
         var passwordMaxErrorTimes = await _sysConfigService.GetConfigValue<int>(ConfigConst.SysPasswordMaxErrorTimes);
-		if (passwordMaxErrorTimes < 1)
-		{
-			passwordMaxErrorTimes = 1;//如果未配置,或误配置为0、负数, 正确密码第一次也无法登录,账号全部锁定。
-		}
-		if (passwordErrorTimes >= passwordMaxErrorTimes)
+        // 若未配置或误配置为0、负数, 则正确密码也无法登录
+        if (passwordMaxErrorTimes < 1)
+            passwordMaxErrorTimes = 1;
+        if (passwordErrorTimes >= passwordMaxErrorTimes)
             throw Oops.Oh(ErrorCodeEnum.D1027);
 
         // 是否开启验证码

+ 9 - 9
Web/package.json

@@ -2,7 +2,7 @@
 	"name": "admin.net",
 	"type": "module",
 	"version": "2.4.33",
-	"lastBuildTime": "2024.08.05",
+	"lastBuildTime": "2024.08.07",
 	"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
 	"author": "zuohuaijun",
 	"license": "MIT",
@@ -44,12 +44,12 @@
 		"js-table2excel": "^1.1.2",
 		"jsplumb": "^2.15.6",
 		"lodash-es": "^4.17.21",
-		"md-editor-v3": "^4.17.4",
+		"md-editor-v3": "^4.18.0",
 		"mitt": "^3.0.1",
 		"monaco-editor": "^0.50.0",
 		"mqtt": "^4.3.8",
 		"nprogress": "^0.2.0",
-		"pinia": "^2.2.0",
+		"pinia": "^2.2.1",
 		"print-js": "^1.6.0",
 		"push.js": "^1.0.12",
 		"qrcodejs2-fixes": "^0.0.2",
@@ -61,14 +61,14 @@
 		"splitpanes": "^3.1.5",
 		"vcrontab-3": "^3.3.22",
 		"vform3-builds": "^3.0.10",
-		"vue": "^3.4.35",
+		"vue": "^3.4.36",
 		"vue-clipboard3": "^2.0.0",
 		"vue-demi": "^0.14.6",
-		"vue-draggable-plus": "^0.5.2",
+		"vue-draggable-plus": "^0.5.3",
 		"vue-grid-layout": "3.0.0-beta1",
 		"vue-i18n": "^9.13.1",
 		"vue-json-pretty": "^2.4.0",
-		"vue-plugin-hiprint": "0.0.57-beta20",
+		"vue-plugin-hiprint": "0.0.57-beta27",
 		"vue-router": "^4.4.2",
 		"vue-signature-pad": "^3.0.2",
 		"vue3-tree-org": "^4.2.2",
@@ -77,18 +77,18 @@
 	"devDependencies": {
 		"@plugin-web-update-notification/vite": "^1.7.1",
 		"@types/lodash-es": "^4.17.12",
-		"@types/node": "^20.14.13",
+		"@types/node": "^20.14.14",
 		"@types/nprogress": "^0.2.3",
 		"@types/sortablejs": "^1.15.8",
 		"@typescript-eslint/eslint-plugin": "^7.18.0",
 		"@typescript-eslint/parser": "^7.18.0",
 		"@vitejs/plugin-vue": "^5.1.2",
 		"@vitejs/plugin-vue-jsx": "^4.0.0",
-		"@vue/compiler-sfc": "^3.4.35",
+		"@vue/compiler-sfc": "^3.4.36",
 		"code-inspector-plugin": "^0.15.2",
 		"eslint": "^8.57.0",
 		"eslint-plugin-vue": "^9.27.0",
-		"globals": "^15.8.0",
+		"globals": "^15.9.0",
 		"less": "^4.2.0",
 		"prettier": "^3.3.3",
 		"rollup-plugin-visualizer": "^5.12.0",

+ 3 - 0
Web/public/print-lock.css

@@ -220,6 +220,9 @@ table.hiprint-printElement-tableTarget {
 .hiprint-printElement-tableTarget-border-td-all td:not(:nth-last-child(-n+2)) {
   border-right: 1px solid;
 }
+.hiprint-printElement-tableTarget-border-td-all td:not(last-child) {
+  border-right: 1px solid;
+}
 .hiprint-printElement-tableTarget-border-td-all td:last-child {
   border-left: 1px solid;
 }

+ 92 - 82
Web/src/main.ts

@@ -5,18 +5,21 @@ import router from '/@/router';
 import { directive } from '/@/directive/index';
 import { i18n } from '/@/i18n/index';
 import other from '/@/utils/other';
-
 import ElementPlus from 'element-plus';
 import '/@/theme/index.scss';
+// 动画库
+import 'animate.css';
+// 栅格布局
 import VueGridLayout from 'vue-grid-layout';
-
-import VForm3 from 'vform3-builds'; // VForm3表单设计
-import 'vform3-builds/dist/designer.style.css'; // VForm3表单设计样式
-import VueSignaturePad from 'vue-signature-pad'; // 电子签名
-import vue3TreeOrg from 'vue3-tree-org'; // 组织架构图
-import 'vue3-tree-org/lib/vue3-tree-org.css'; // 组织架构图样式
-import 'animate.css'; // 动画库
-
+// 电子签名
+import VueSignaturePad from 'vue-signature-pad';
+// 组织架构图
+import vue3TreeOrg from 'vue3-tree-org';
+import 'vue3-tree-org/lib/vue3-tree-org.css';
+// VForm3 表单设计
+import VForm3 from 'vform3-builds';
+import 'vform3-builds/dist/designer.style.css';
+// 关闭自动打印
 import { disAutoConnect } from 'vue-plugin-hiprint';
 disAutoConnect();
 
@@ -24,86 +27,93 @@ const app = createApp(App);
 
 directive(app);
 other.elSvg(app);
-//#region  FastCrud配置
-import { FastCrud } from "@fast-crud/fast-crud";
-import "@fast-crud/fast-crud/dist/style.css";
-import ui from "@fast-crud/ui-element";
-import { FsExtendsUploader, FsExtendsEditor } from "@fast-crud/fast-extends";
-import "@fast-crud/fast-extends/dist/style.css";
-import { getToken } from '/@/utils/axios-utils';
+
+// #region  FastCrud配置
+import { FastCrud } from '@fast-crud/fast-crud';
+import '@fast-crud/fast-crud/dist/style.css';
+import ui from '@fast-crud/ui-element';
+import { FsExtendsUploader, FsExtendsEditor } from '@fast-crud/fast-extends';
+import '@fast-crud/fast-extends/dist/style.css';
 app.use(ui);
 app.use(FastCrud, {
-    i18n,
-    commonOptions() {
-        return {
-            request: {
-                transformQuery: ({ page, form, sort }) => {
-                    const order = sort == null ? {} : { orderProp: sort.prop, orderAsc: sort.asc }
-                    return { page: page.currentPage, pageSize: page.pageSize, ...form, ...order };
-                },
-                // page请求结果转换
-                transformRes: ({ res }) => {
-                    const records = res.data.result.items;
-                    const total = res.data.result.total;
-                    const currentPage = res.data.result.page;
-                    const pageSize = res.data.result.pageSize;
-                    return {
-                        currentPage: currentPage, pageSize: pageSize, total: total, records
-                    }
-                },
-                form: {
-                    display: 'flex', // 表单布局
-                    labelWidth: '120px' // 表单label宽度
-                }
-            },
-        };
-    },
+	i18n,
+	commonOptions() {
+		return {
+			request: {
+				transformQuery: ({ page, form, sort }) => {
+					const order = sort == null ? {} : { orderProp: sort.prop, orderAsc: sort.asc };
+					return { page: page?.currentPage, pageSize: page?.pageSize, ...form, ...order };
+				},
+				// page请求结果转换
+				transformRes: ({ res }) => {
+					const records = res.data.result.items;
+					const total = res.data.result.total;
+					const currentPage = res.data.result.page;
+					const pageSize = res.data.result.pageSize;
+					return {
+						currentPage: currentPage,
+						pageSize: pageSize,
+						total: total,
+						records,
+					};
+				},
+				form: {
+					display: 'flex', // 表单布局
+					labelWidth: '120px', // 表单label宽度
+				},
+			},
+		};
+	},
 });
+
 const baseURL = import.meta.env.VITE_API_URL;
 import request from '/@/utils/request';
-//文件上传
+import { getToken } from '/@/utils/axios-utils';
+// 文件上传
 app.use(FsExtendsUploader, {
-    defaultType: 'form',
-    form: {
-        action: baseURL + '/api/sysFile/uploadFile',
-        name: 'file',
-        withCredentials: false,
-        uploadRequest: async props => {
-            const { action, file, onProgress } = props;
-            const data = new FormData();
-            data.append('file', file);
-            const token = getToken();
-            const Authorization = token ? `Bearer ${token}` : null;
-            const result = await request({
-                url: action,
-                method: 'post',
-                data,
-                headers: {
-                    'Content-Type': 'multipart/form-data',
-                    "Authorization": Authorization
-                },
-                timeout: 60000,
-                onUploadProgress(progress) {
-                    onProgress({ percent: Math.round((progress.loaded / progress.total!) * 100) });
-                }
-            });
-            if (result) {
-                return result.data
-            } else {
-                throw new Error(result.message);
-            }
-        },
-        async successHandle(ret: any) {
-            return {
-                url: baseURL + "/" + ret.result.filePath + "/" + ret.result.id + ret.result.suffix,
-                key: ret.result.fileName
-            };
-        }
-    }
+	defaultType: 'form',
+	form: {
+		action: baseURL + '/api/sysFile/uploadFile',
+		name: 'file',
+		withCredentials: false,
+		uploadRequest: async (props) => {
+			const { action, file, onProgress } = props;
+			const data = new FormData();
+			data.append('file', file);
+			const token = getToken();
+			const Authorization = token ? `Bearer ${token}` : null;
+			const result = await request({
+				url: action,
+				method: 'post',
+				data,
+				headers: {
+					'Content-Type': 'multipart/form-data',
+					Authorization: Authorization,
+				},
+				timeout: 60000,
+				onUploadProgress(progress) {
+					onProgress({ percent: Math.round((progress.loaded / progress.total!) * 100) });
+				},
+			});
+			if (result) {
+				return result.data;
+			} else {
+				throw new Error(result.message);
+			}
+		},
+		async successHandle(ret: any) {
+			return {
+				url: baseURL + '/' + ret.result.filePath + '/' + ret.result.id + ret.result.suffix,
+				key: ret.result.fileName,
+			};
+		},
+	},
 });
-//富文本编辑器
+
+// 富文本编辑器
 app.use(FsExtendsEditor, {
-    wangEditor: {}
+	wangEditor: {},
 });
-//#endregion
+// #endregion
+
 app.use(pinia).use(router).use(ElementPlus).use(i18n).use(VueGridLayout).use(VForm3).use(VueSignaturePad).use(vue3TreeOrg).mount('#app');

+ 0 - 181
Web/src/views/system/crud/crud.tsx

@@ -1,181 +0,0 @@
-
-import { dict, compute } from '@fast-crud/fast-crud';
-import { shallowRef, ref } from 'vue';
-import { getAPI } from '/@/utils/axios-utils';
-import { SysNoticeApi } from '/@/api-services/api';
-import { ElMessage } from 'element-plus';
-export default function ({ expose }) {
-    const pageRequest = async (query) => {
-        const params = {
-            page: query.currentPage,
-            pageSize: query.pageSize,
-            field: query.field,
-            order: query.order,
-            descStr: 'desc'
-        } as PageFileInput;
-        const result = await getAPI(SysNoticeApi).apiSysNoticePagePost(params);
-        return result;
-    };
-    const editRequest = async ({ form, row }: EditReq) => {
-        if (form.id == null) {
-            form.id = row.id;
-        }
-        return await getAPI(SysNoticeApi)
-            .apiSysNoticeUpdatePost(form)
-            .then((rsp: any) => {
-                if (rsp.data.code == 200) {
-                    ElMessage.success('修改成功!');
-                } else {
-                    ElMessage.error('修改失败:' + rsp.data.message);
-                }
-            });
-    };
-    const delRequest = async ({ row }: DelReq) => {
-        return await getAPI(SysNoticeApi)
-            .apiSysNoticeDeletePost(row);
-    };
-    const addRequest = async ({ form }: AddReq) => {
-        return await getAPI(SysNoticeApi)
-            .apiSysNoticeAddPost(form);
-    };
-    const selectedIds = ref([]);
-    const onSelectionChange = (changed) => {
-        selectedIds.value = changed;
-    };
-    return {
-        selectedIds,
-        crudOptions: {
-            container: {
-                is: 'fs-layout-card'
-            },
-            form: {
-                wrapper: {
-                    // is: 'el-drawer',
-                    // width: '80%',
-                    draggable: false,
-                    closeOnEsc: false,
-                    maskClosable: false,
-                }
-            },
-            search: {
-                show: true,
-            },
-            actionbar: {
-            },
-            toolbar: {
-                show: true,
-                buttons: {
-                    search: { show: true },
-                    refresh: { show: true },
-                    compact: { show: true },
-                    export: { show: true },
-                    columns: { show: true },
-                },
-            },
-            table: {
-                scrollX: 725,
-                bordered: false,
-                rowKey: (row) => row.id,
-                checkedRowKeys: selectedIds,
-                'onUpdate:checkedRowKeys': onSelectionChange,
-            },
-            pagination: {
-                show: true
-            },
-            request: {
-                pageRequest,
-                addRequest,
-                editRequest,
-            },
-            rowHandle: {
-                fixed: "right",
-                align: "center",
-                width: 200,
-                buttons: {
-                    view: { show: true },
-                    edit: { show: true }
-                }
-            },
-            columns: {
-                _checked: {
-                    title: '选择',
-                    form: { show: false },
-                    column: {
-                        type: 'selection',
-                        align: 'center',
-                        width: '55px',
-                        columnSetDisabled: true,
-                        disabled(row) {
-                            return row.account === 'gvanet';
-                        },
-                    },
-                },
-                type: {
-                    title: '类型',
-                    type: 'dict-select',
-                    search: { show: true, col: { span: 6 } },
-                    column: {
-                        align: "center",
-                        width: '120px',
-                    },
-                    dict: dict({
-                        value: 'id',
-                        label: 'text',
-                        data: [
-                            { id: '1', text: '通知' },
-                            { id: '2', text: '公告' }
-                        ],
-                    }),
-                    form: {
-                        col: { span: 24 },
-                        rule: [
-                            { required: true, message: '请输入类型' }
-                        ],
-                    }
-                },
-                title: {
-                    title: '标题',
-                    type: 'text',
-                    search: { show: true, col: { span: 6 } },
-                    column: {
-                        align: "center",
-                        width: 'auto',
-                    },
-                    form: {
-                        col: { span: 24 },
-                        rule: [
-                            { required: true, message: '请输入标题' }
-                        ],
-                    }
-                },
-                content: {
-                    title: '内容',
-                    type: 'editor-wang5',
-                    search: { show: false, col: { span: 6 } },
-                    column: {
-                        show: false,
-                    },
-                    form: {
-                        col: { span: 24 },
-                        rule: [
-                            { required: true, message: '请输入内容' }
-                        ],
-                        component: {
-                            disabled: compute(({ form }) => {
-                                return form.disabled;
-                            }),
-                            id: '1', // 当同一个页面有多个editor时,需要配置不同的id
-                            config: {},
-                            uploader: {
-                                type: 'form',
-                                buildUrl(res) {
-                                    return res.url;
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-        },
-    };
-}

+ 0 - 64
Web/src/views/system/crud/index.vue

@@ -1,64 +0,0 @@
-<template>
-    <div class="h-full">
-        <fs-crud ref="crudRef" v-bind="crudBinding">
-            <template #pagination-left>
-                <fs-button icon="ion:trash-outline" @click="handleBatchDelete" />
-            </template>
-            <template #cell_url="scope">
-                <n-tooltip trigger="hover">
-                    <template #trigger>
-                        <n-button>预览 </n-button>
-                    </template>
-                    <n-image width="120px" height="120px" :src="baseURL + '/' + scope.row.url"></n-image>
-                </n-tooltip>
-            </template>
-        </fs-crud>
-    </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, onMounted, ref, nextTick } from 'vue';
-import { useFs, useExpose, useCrud } from '@fast-crud/fast-crud';
-import createCrudOptions from './crud';
-import { getAPI } from '/@/utils/axios-utils';
-import { SysNoticeApi } from '/@/api-services/api';
-import { ElMessage, ElMessageBox } from 'element-plus';
-const baseURL = import.meta.env.VITE_API_URL;
-export default defineComponent({
-    name: 'ComponentCrud',
-    setup() {
-        const crudRef = ref();
-        const crudBinding = ref();
-        const { expose } = useExpose({ crudRef, crudBinding });
-        const { crudOptions, selectedIds } = createCrudOptions({ expose });
-        const { resetCrudOptions } = useCrud({ expose, crudOptions });
-        onMounted(() => {
-            expose.doRefresh();
-        });
-        const handleBatchDelete = async () => {
-            if (selectedIds.value?.length > 0) {
-                // ElMessageBox.confirm(`确定要批量删除这${selectedIds.value.length}条记录吗`, '确认', {
-                //     confirmButtonText: '确定',
-                //     cancelButtonText: '取消',
-                //     type: 'info',
-                // }).then(async () => {
-                //        await delBatchSysFile(selectedIds.value);
-                //       message.success('删除成功');
-                //       selectedIds.value = [];
-                //       await expose.doRefresh();
-                //         ElMessage.success('删除成功');
-                //     })
-                //     .catch(() => { });
-            } else {
-                // ElMessage.success('请勾选要删除的记录');
-            }
-        };
-        return {
-            crudBinding,
-            crudRef,
-            handleBatchDelete,
-            baseURL
-        };
-    }
-});
-</script>

+ 180 - 0
Web/src/views/system/fastCrud/crud.tsx

@@ -0,0 +1,180 @@
+import { ref } from 'vue';
+import { ElMessage } from 'element-plus';
+import { dict, compute, EditReq, DelReq, AddReq } from '@fast-crud/fast-crud';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysNoticeApi } from '/@/api-services/api';
+import { PageFileInput } from '/@/api-services/models';
+
+export default function ({ expose }) {
+	// 分页查询
+	const pageRequest = async (query: any) => {
+		const params = {
+			page: query.currentPage,
+			pageSize: query.pageSize,
+			field: query.field,
+			order: query.order,
+			descStr: 'desc',
+		} as PageFileInput;
+		const result = await getAPI(SysNoticeApi).apiSysNoticePagePost(params);
+		return result;
+	};
+	// 编辑
+	const editRequest = async ({ form, row }: EditReq) => {
+		if (form.id == null) {
+			form.id = row.id;
+		}
+		return await getAPI(SysNoticeApi)
+			.apiSysNoticeUpdatePost(form)
+			.then((rsp: any) => {
+				if (rsp.data.code == 200) {
+					ElMessage.success('修改成功!');
+				} else {
+					ElMessage.error('修改失败:' + rsp.data.message);
+				}
+			});
+	};
+	// 删除
+	const delRequest = async ({ row }: DelReq) => {
+		return await getAPI(SysNoticeApi).apiSysNoticeDeletePost(row);
+	};
+	// 增加
+	const addRequest = async ({ form }: AddReq) => {
+		return await getAPI(SysNoticeApi).apiSysNoticeAddPost(form);
+	};
+	// 选择
+	const selectedIds = ref([]);
+	const onSelectionChange = (changed: any) => {
+		selectedIds.value = changed;
+	};
+	return {
+		selectedIds,
+		crudOptions: {
+			container: {
+				is: 'fs-layout-card',
+			},
+			form: {
+				wrapper: {
+					// is: 'el-drawer',
+					// width: '80%',
+					draggable: false,
+					closeOnEsc: false,
+					maskClosable: false,
+				},
+			},
+			search: {
+				show: true,
+			},
+			actionbar: {},
+			toolbar: {
+				show: true,
+				buttons: {
+					search: { show: true },
+					refresh: { show: true },
+					compact: { show: true },
+					export: { show: true },
+					columns: { show: true },
+				},
+			},
+			table: {
+				scrollX: 725,
+				bordered: false,
+				rowKey: (row: any) => row.id,
+				checkedRowKeys: selectedIds,
+				'onUpdate:checkedRowKeys': onSelectionChange,
+			},
+			pagination: {
+				show: true,
+			},
+			request: {
+				pageRequest,
+				addRequest,
+				editRequest,
+				delRequest,
+			},
+			rowHandle: {
+				fixed: 'right',
+				align: 'center',
+				width: 200,
+				buttons: {
+					view: { show: true },
+					edit: { show: true },
+				},
+			},
+			columns: {
+				_checked: {
+					title: '选择',
+					form: { show: false },
+					column: {
+						type: 'selection',
+						align: 'center',
+						width: '55px',
+						columnSetDisabled: true,
+						disabled(row: any) {
+							return row.account === 'gvanet';
+						},
+					},
+				},
+				type: {
+					title: '类型',
+					type: 'dict-select',
+					search: { show: true, col: { span: 6 } },
+					column: {
+						align: 'center',
+						width: '120px',
+					},
+					dict: dict({
+						value: 'id',
+						label: 'text',
+						data: [
+							{ id: '1', text: '通知' },
+							{ id: '2', text: '公告' },
+						],
+					}),
+					form: {
+						col: { span: 24 },
+						rule: [{ required: true, message: '请输入类型' }],
+					},
+				},
+				title: {
+					title: '标题',
+					type: 'text',
+					search: { show: true, col: { span: 6 } },
+					column: {
+						align: 'center',
+						width: 'auto',
+					},
+					form: {
+						col: { span: 24 },
+						rule: [{ required: true, message: '请输入标题' }],
+					},
+				},
+				content: {
+					title: '内容',
+					type: 'editor-wang5',
+					search: { show: false, col: { span: 6 } },
+					column: {
+						show: false,
+					},
+					form: {
+						col: { span: 24 },
+						rule: [{ required: true, message: '请输入内容' }],
+						component: {
+							disabled: compute(({ form }) => {
+								return form.disabled;
+							}),
+							id: '1', // 当同一个页面有多个editor时,需要配置不同的id
+							config: {},
+							uploader: {
+								type: 'form',
+								buildUrl(res: any) {
+									return res.url;
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	};
+}

+ 66 - 0
Web/src/views/system/fastCrud/index.vue

@@ -0,0 +1,66 @@
+<template>
+	<div class="h-full">
+		<fs-crud ref="crudRef" v-bind="crudBinding">
+			<template #pagination-left>
+				<fs-button icon="ion:trash-outline" @click="handleBatchDelete" />
+			</template>
+			<template #cell_url="scope">
+				<n-tooltip trigger="hover">
+					<template #trigger>
+						<n-button> 预览 </n-button>
+					</template>
+					<n-image width="120px" height="120px" :src="baseURL + '/' + scope.row.url"></n-image>
+				</n-tooltip>
+			</template>
+		</fs-crud>
+	</div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted, ref } from 'vue';
+import { useExpose, useCrud } from '@fast-crud/fast-crud';
+import createCrudOptions from './crud';
+
+const baseURL = import.meta.env.VITE_API_URL;
+
+export default defineComponent({
+	name: 'FastCrud',
+	setup() {
+		const crudRef = ref();
+		const crudBinding = ref();
+		const { expose } = useExpose({ crudRef, crudBinding });
+		const { crudOptions, selectedIds } = createCrudOptions({ expose });
+		const { resetCrudOptions } = useCrud({ expose, crudOptions });
+
+		// 页面初始化
+		onMounted(() => {
+			expose.doRefresh();
+		});
+		// 批量删除
+		const handleBatchDelete = async () => {
+			if (selectedIds.value?.length > 0) {
+				// ElMessageBox.confirm(`确定要批量删除这${selectedIds.value.length}条记录吗`, '确认', {
+				//     confirmButtonText: '确定',
+				//     cancelButtonText: '取消',
+				//     type: 'info',
+				// }).then(async () => {
+				//        await delBatchSysFile(selectedIds.value);
+				//       message.success('删除成功');
+				//       selectedIds.value = [];
+				//       await expose.doRefresh();
+				//         ElMessage.success('删除成功');
+				//     })
+				//     .catch(() => { });
+			} else {
+				// ElMessage.success('请勾选要删除的记录');
+			}
+		};
+		return {
+			crudBinding,
+			crudRef,
+			handleBatchDelete,
+			baseURL,
+		};
+	},
+});
+</script>