NewLife/NewLife.QuickVue

usePage增加许多配置项,另外增加一些回调函数、插槽等等,修改逻辑增强配置灵活性
zk authored at 2023-11-21 17:42:23
3b6bfc3
Tree
1 Parent(s) 2fecf5f
Summary: 40 changed files with 1171 additions and 468 deletions.
Modified +4 -1
Modified +5 -1
Modified +233 -169
Modified +1 -1
Modified +44 -6
Modified +19 -7
Added +20 -0
Added +108 -0
Modified +3 -3
Modified +3 -2
Modified +1 -1
Modified +51 -19
Modified +15 -15
Modified +53 -14
Modified +36 -20
Modified +92 -27
Modified +52 -8
Modified +3 -2
Modified +3 -2
Modified +48 -25
Modified +6 -2
Modified +25 -1
Modified +38 -31
Modified +9 -5
Renamed +10 -4
src/hook/usePageSetting.ts → src/hook/usePage.ts
Modified +1 -1
Modified +1 -1
Modified +4 -2
Modified +3 -0
Added +8 -0
Modified +1 -1
Modified +2 -0
Modified +6 -0
Modified +78 -2
Modified +96 -16
Modified +26 -25
Modified +60 -47
Modified +1 -4
Modified +1 -1
Modified +1 -2
Modified +4 -1
diff --git a/.env.development b/.env.development
index 5e4721a..c78524c 100644
--- a/.env.development
+++ b/.env.development
@@ -3,4 +3,7 @@ ENV = development
 
 # 本地环境接口地址
 # VITE_API_URL = https://cube3.newlifex.com
-VITE_API_URL = /base-api
\ No newline at end of file
+VITE_API_URL = /base-api
+
+# 图片前缀地址
+VITE_IMG_BASE_URL = https://rengleme.oss-cn-beijing.aliyuncs.com/
\ No newline at end of file
Modified +5 -1
diff --git a/.env.production b/.env.production
index 6f65e04..75fba85 100644
--- a/.env.production
+++ b/.env.production
@@ -3,4 +3,8 @@ ENV = production
 
 # 线上环境接口地址
 # VITE_API_URL = https://cube3.newlifex.com
-VITE_API_URL = http://120.78.152.40:5000
\ No newline at end of file
+# VITE_API_URL = http://120.78.152.40:5000
+VITE_API_URL = 
+
+# 图片前缀地址
+VITE_IMG_BASE_URL = https://rengleme.oss-cn-beijing.aliyuncs.com/
\ No newline at end of file
Modified +233 -169
diff --git a/package-lock.json b/package-lock.json
index aafc793..76033f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
 {
-	"name": "vue-next-admin",
+	"name": "NewLife.QuickVue",
 	"version": "2.4.3",
 	"lockfileVersion": 2,
 	"requires": true,
 	"packages": {
 		"": {
-			"name": "vue-next-admin",
+			"name": "NewLife.QuickVue",
 			"version": "2.4.3",
 			"license": "MIT",
 			"dependencies": {
@@ -33,7 +33,7 @@
 				"sortablejs": "^1.15.0",
 				"splitpanes": "^3.1.5",
 				"ts-enum-util": "^4.0.2",
-				"vue": "^3.2.47",
+				"vue": "^3.3.8",
 				"vue-clipboard3": "^2.0.0",
 				"vue-grid-layout": "^3.0.0-beta1",
 				"vue-i18n": "^9.2.2",
@@ -67,9 +67,9 @@
 			}
 		},
 		"node_modules/@babel/parser": {
-			"version": "7.21.1",
-			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz",
-			"integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg==",
+			"version": "7.23.4",
+			"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.4.tgz",
+			"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==",
 			"bin": {
 				"parser": "bin/babel-parser.js"
 			},
@@ -869,6 +869,11 @@
 				"node": ">= 14"
 			}
 		},
+		"node_modules/@jridgewell/sourcemap-codec": {
+			"version": "1.4.15",
+			"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+			"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+		},
 		"node_modules/@nodelib/fs.scandir": {
 			"version": "2.1.5",
 			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1224,49 +1229,60 @@
 			}
 		},
 		"node_modules/@vue/compiler-core": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
-			"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
+			"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
 			"dependencies": {
-				"@babel/parser": "^7.16.4",
-				"@vue/shared": "3.2.47",
+				"@babel/parser": "^7.23.0",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"source-map": "^0.6.1"
+				"source-map-js": "^1.0.2"
 			}
 		},
 		"node_modules/@vue/compiler-dom": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
-			"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
+			"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
 			"dependencies": {
-				"@vue/compiler-core": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-core": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"node_modules/@vue/compiler-sfc": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
-			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
-			"dependencies": {
-				"@babel/parser": "^7.16.4",
-				"@vue/compiler-core": "3.2.47",
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/compiler-ssr": "3.2.47",
-				"@vue/reactivity-transform": "3.2.47",
-				"@vue/shared": "3.2.47",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
+			"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
+			"dependencies": {
+				"@babel/parser": "^7.23.0",
+				"@vue/compiler-core": "3.3.8",
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/compiler-ssr": "3.3.8",
+				"@vue/reactivity-transform": "3.3.8",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"magic-string": "^0.25.7",
-				"postcss": "^8.1.10",
-				"source-map": "^0.6.1"
+				"magic-string": "^0.30.5",
+				"postcss": "^8.4.31",
+				"source-map-js": "^1.0.2"
+			}
+		},
+		"node_modules/@vue/compiler-sfc/node_modules/magic-string": {
+			"version": "0.30.5",
+			"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
+			"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.4.15"
+			},
+			"engines": {
+				"node": ">=12"
 			}
 		},
 		"node_modules/@vue/compiler-ssr": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
-			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
+			"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
 			"dependencies": {
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"node_modules/@vue/devtools-api": {
@@ -1275,60 +1291,71 @@
 			"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
 		},
 		"node_modules/@vue/reactivity": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
-			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
+			"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
 			"dependencies": {
-				"@vue/shared": "3.2.47"
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"node_modules/@vue/reactivity-transform": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
-			"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
+			"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
 			"dependencies": {
-				"@babel/parser": "^7.16.4",
-				"@vue/compiler-core": "3.2.47",
-				"@vue/shared": "3.2.47",
+				"@babel/parser": "^7.23.0",
+				"@vue/compiler-core": "3.3.8",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"magic-string": "^0.25.7"
+				"magic-string": "^0.30.5"
+			}
+		},
+		"node_modules/@vue/reactivity-transform/node_modules/magic-string": {
+			"version": "0.30.5",
+			"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
+			"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.4.15"
+			},
+			"engines": {
+				"node": ">=12"
 			}
 		},
 		"node_modules/@vue/runtime-core": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
-			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
+			"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
 			"dependencies": {
-				"@vue/reactivity": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/reactivity": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"node_modules/@vue/runtime-dom": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
-			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
+			"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
 			"dependencies": {
-				"@vue/runtime-core": "3.2.47",
-				"@vue/shared": "3.2.47",
-				"csstype": "^2.6.8"
+				"@vue/runtime-core": "3.3.8",
+				"@vue/shared": "3.3.8",
+				"csstype": "^3.1.2"
 			}
 		},
 		"node_modules/@vue/server-renderer": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
-			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
+			"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
 			"dependencies": {
-				"@vue/compiler-ssr": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-ssr": "3.3.8",
+				"@vue/shared": "3.3.8"
 			},
 			"peerDependencies": {
-				"vue": "3.2.47"
+				"vue": "3.3.8"
 			}
 		},
 		"node_modules/@vue/shared": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
-			"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
+			"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
 		},
 		"node_modules/@vueuse/core": {
 			"version": "9.13.0",
@@ -1982,9 +2009,9 @@
 			}
 		},
 		"node_modules/csstype": {
-			"version": "2.6.21",
-			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
-			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+			"version": "3.1.2",
+			"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz",
+			"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
 		},
 		"node_modules/d": {
 			"version": "1.0.1",
@@ -2485,7 +2512,7 @@
 		},
 		"node_modules/estree-walker": {
 			"version": "2.0.2",
-			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
 			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
 		},
 		"node_modules/esutils": {
@@ -3159,6 +3186,7 @@
 			"version": "0.25.9",
 			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
 			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"dev": true,
 			"dependencies": {
 				"sourcemap-codec": "^1.4.8"
 			}
@@ -3252,9 +3280,9 @@
 			"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
 		},
 		"node_modules/nanoid": {
-			"version": "3.3.4",
-			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-			"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+			"version": "3.3.7",
+			"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+			"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
 			"bin": {
 				"nanoid": "bin/nanoid.cjs"
 			},
@@ -3529,11 +3557,11 @@
 			}
 		},
 		"node_modules/postcss": {
-			"version": "8.4.21",
-			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.21.tgz",
-			"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+			"version": "8.4.31",
+			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz",
+			"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
 			"dependencies": {
-				"nanoid": "^3.3.4",
+				"nanoid": "^3.3.6",
 				"picocolors": "^1.0.0",
 				"source-map-js": "^1.0.2"
 			},
@@ -4021,7 +4049,8 @@
 			"version": "1.4.8",
 			"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
 			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
-			"deprecated": "Please use @jridgewell/sourcemap-codec instead"
+			"deprecated": "Please use @jridgewell/sourcemap-codec instead",
+			"dev": true
 		},
 		"node_modules/splitpanes": {
 			"version": "3.1.5",
@@ -4320,15 +4349,23 @@
 			}
 		},
 		"node_modules/vue": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
-			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
+			"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
 			"dependencies": {
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/compiler-sfc": "3.2.47",
-				"@vue/runtime-dom": "3.2.47",
-				"@vue/server-renderer": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/compiler-sfc": "3.3.8",
+				"@vue/runtime-dom": "3.3.8",
+				"@vue/server-renderer": "3.3.8",
+				"@vue/shared": "3.3.8"
+			},
+			"peerDependencies": {
+				"typescript": "*"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
 			}
 		},
 		"node_modules/vue-clipboard3": {
@@ -4542,9 +4579,9 @@
 	},
 	"dependencies": {
 		"@babel/parser": {
-			"version": "7.21.1",
-			"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.1.tgz",
-			"integrity": "sha512-JzhBFpkuhBNYUY7qs+wTzNmyCWUHEaAFpQQD2YfU1rPL38/L43Wvid0fFkiOCnHvsGncRZgEPyGnltABLcVDTg=="
+			"version": "7.23.4",
+			"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.4.tgz",
+			"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ=="
 		},
 		"@babel/runtime": {
 			"version": "7.21.0",
@@ -5047,6 +5084,11 @@
 				"@intlify/shared": "9.2.2"
 			}
 		},
+		"@jridgewell/sourcemap-codec": {
+			"version": "1.4.15",
+			"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+			"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
+		},
 		"@nodelib/fs.scandir": {
 			"version": "2.1.5",
 			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -5290,49 +5332,59 @@
 			"requires": {}
 		},
 		"@vue/compiler-core": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
-			"integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
+			"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
 			"requires": {
-				"@babel/parser": "^7.16.4",
-				"@vue/shared": "3.2.47",
+				"@babel/parser": "^7.23.0",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"source-map": "^0.6.1"
+				"source-map-js": "^1.0.2"
 			}
 		},
 		"@vue/compiler-dom": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
-			"integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
+			"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
 			"requires": {
-				"@vue/compiler-core": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-core": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"@vue/compiler-sfc": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
-			"integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
-			"requires": {
-				"@babel/parser": "^7.16.4",
-				"@vue/compiler-core": "3.2.47",
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/compiler-ssr": "3.2.47",
-				"@vue/reactivity-transform": "3.2.47",
-				"@vue/shared": "3.2.47",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
+			"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
+			"requires": {
+				"@babel/parser": "^7.23.0",
+				"@vue/compiler-core": "3.3.8",
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/compiler-ssr": "3.3.8",
+				"@vue/reactivity-transform": "3.3.8",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"magic-string": "^0.25.7",
-				"postcss": "^8.1.10",
-				"source-map": "^0.6.1"
+				"magic-string": "^0.30.5",
+				"postcss": "^8.4.31",
+				"source-map-js": "^1.0.2"
+			},
+			"dependencies": {
+				"magic-string": {
+					"version": "0.30.5",
+					"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
+					"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+					"requires": {
+						"@jridgewell/sourcemap-codec": "^1.4.15"
+					}
+				}
 			}
 		},
 		"@vue/compiler-ssr": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
-			"integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
+			"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
 			"requires": {
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"@vue/devtools-api": {
@@ -5341,57 +5393,67 @@
 			"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
 		},
 		"@vue/reactivity": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
-			"integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
+			"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
 			"requires": {
-				"@vue/shared": "3.2.47"
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"@vue/reactivity-transform": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
-			"integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
+			"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
 			"requires": {
-				"@babel/parser": "^7.16.4",
-				"@vue/compiler-core": "3.2.47",
-				"@vue/shared": "3.2.47",
+				"@babel/parser": "^7.23.0",
+				"@vue/compiler-core": "3.3.8",
+				"@vue/shared": "3.3.8",
 				"estree-walker": "^2.0.2",
-				"magic-string": "^0.25.7"
+				"magic-string": "^0.30.5"
+			},
+			"dependencies": {
+				"magic-string": {
+					"version": "0.30.5",
+					"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz",
+					"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
+					"requires": {
+						"@jridgewell/sourcemap-codec": "^1.4.15"
+					}
+				}
 			}
 		},
 		"@vue/runtime-core": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
-			"integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
+			"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
 			"requires": {
-				"@vue/reactivity": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/reactivity": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"@vue/runtime-dom": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
-			"integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
+			"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
 			"requires": {
-				"@vue/runtime-core": "3.2.47",
-				"@vue/shared": "3.2.47",
-				"csstype": "^2.6.8"
+				"@vue/runtime-core": "3.3.8",
+				"@vue/shared": "3.3.8",
+				"csstype": "^3.1.2"
 			}
 		},
 		"@vue/server-renderer": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
-			"integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
+			"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
 			"requires": {
-				"@vue/compiler-ssr": "3.2.47",
-				"@vue/shared": "3.2.47"
+				"@vue/compiler-ssr": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"@vue/shared": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
-			"integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
+			"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
 		},
 		"@vueuse/core": {
 			"version": "9.13.0",
@@ -5842,9 +5904,9 @@
 			"dev": true
 		},
 		"csstype": {
-			"version": "2.6.21",
-			"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
-			"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+			"version": "3.1.2",
+			"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz",
+			"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
 		},
 		"d": {
 			"version": "1.0.1",
@@ -6242,7 +6304,7 @@
 		},
 		"estree-walker": {
 			"version": "2.0.2",
-			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
 			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
 		},
 		"esutils": {
@@ -6763,6 +6825,7 @@
 			"version": "0.25.9",
 			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
 			"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+			"dev": true,
 			"requires": {
 				"sourcemap-codec": "^1.4.8"
 			}
@@ -6841,9 +6904,9 @@
 			"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
 		},
 		"nanoid": {
-			"version": "3.3.4",
-			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-			"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+			"version": "3.3.7",
+			"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
+			"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g=="
 		},
 		"natural-compare": {
 			"version": "1.4.0",
@@ -7025,11 +7088,11 @@
 			}
 		},
 		"postcss": {
-			"version": "8.4.21",
-			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.21.tgz",
-			"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+			"version": "8.4.31",
+			"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz",
+			"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
 			"requires": {
-				"nanoid": "^3.3.4",
+				"nanoid": "^3.3.6",
 				"picocolors": "^1.0.0",
 				"source-map-js": "^1.0.2"
 			}
@@ -7341,7 +7404,8 @@
 		"sourcemap-codec": {
 			"version": "1.4.8",
 			"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
-			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+			"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+			"dev": true
 		},
 		"splitpanes": {
 			"version": "3.1.5",
@@ -7541,15 +7605,15 @@
 			}
 		},
 		"vue": {
-			"version": "3.2.47",
-			"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
-			"integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
-			"requires": {
-				"@vue/compiler-dom": "3.2.47",
-				"@vue/compiler-sfc": "3.2.47",
-				"@vue/runtime-dom": "3.2.47",
-				"@vue/server-renderer": "3.2.47",
-				"@vue/shared": "3.2.47"
+			"version": "3.3.8",
+			"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
+			"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
+			"requires": {
+				"@vue/compiler-dom": "3.3.8",
+				"@vue/compiler-sfc": "3.3.8",
+				"@vue/runtime-dom": "3.3.8",
+				"@vue/server-renderer": "3.3.8",
+				"@vue/shared": "3.3.8"
 			}
 		},
 		"vue-clipboard3": {
Modified +1 -1
diff --git a/package.json b/package.json
index a913505..8951143 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
 		"sortablejs": "^1.15.0",
 		"splitpanes": "^3.1.5",
 		"ts-enum-util": "^4.0.2",
-		"vue": "^3.2.47",
+		"vue": "^3.3.8",
 		"vue-clipboard3": "^2.0.0",
 		"vue-grid-layout": "^3.0.0-beta1",
 		"vue-i18n": "^9.2.2",
Modified +44 -6
diff --git a/README.md b/README.md
index 2b3fdda..2511e0e 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ https://antd.newlifex.com
 
 3.  页面路由规则从接口读取,所有路由自动引用 view/modules/index 文件,当页面需要手工修改时,可按后台路由规则在项目里建立文件复制模板代码(view/modules/index 文件)进行修改,无需在前端代码另外配置路由
 
-4.  完成动态表单、表格组件的初步封装,当接口配置不足以满足页面需求时,调用 usePageSetting 函数即可对页面进行组件配置
+4.  完成动态表单、表格组件的初步封装,当接口配置不足以满足页面需求时,调用 usePage 函数即可对页面进行组件配置
 
 5.  组件不够时,可继续封装组件并配置到 form/component.ts 文件里即可引用
 
@@ -45,7 +45,7 @@ https://antd.newlifex.com
 </script>
 ```
 
-2.  usePageSetting 函数参数配置
+2.  usePage 函数参数配置
 
 ```javascript
 {
@@ -85,11 +85,47 @@ https://antd.newlifex.com
         width?: string | number;
         // 是否勾选显示
         isCheck?: boolean;
-    }
+        // 是否可排序
+        sort?: boolean;
+    },
+    /** 表格配置(支持element-plus table的所有配置) */
+    tableConfig?: Partial<TableProps<EmptyObjectType>> & Partial<TableMoreProps> & {
+      api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+      url?: string;
+      requestProps?: EmptyObjectType;
+      handleWidth?: number;
+    },
+    /** 详情相关配置 */
+    detailConfig?: {
+      api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+      url?: string;
+      requestProps?: EmptyObjectType | ((row: EmptyObjectType) => EmptyObjectType);
+    },
+    /** 添加相关配置 */
+    addConfig?: {
+      api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+      url?: string;
+      requestProps?: EmptyObjectType;
+    },
+    /** 编辑相关配置 */
+    editConfig?: {
+      api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+      url?: string;
+      requestProps?: EmptyObjectType;
+    },
+    /** 添加修改删除请求点击以及执行前后的回调钩子 */
+    onAddClick?: () => void;
+    onAddBefore?: (data: EmptyObjectType, addFun: Function) => void;
+    onAddAfter?: (data: EmptyObjectType) => void;
+    onEditClick?: (data: EmptyObjectType) => void;
+    onEditBefore?: (data: EmptyObjectType, editFun: Function) => void;
+    onEditAfter?: (data: EmptyObjectType) => void;
+    onDelBefore?: (data: EmptyObjectType, delFun: Function) => void;
+    onDelAfter?: (data: EmptyObjectType) => void;
 }
 ```
 
-3.  usePageSetting 返回值
+3.  usePage 返回值
 
 ```javascript
 {
@@ -102,6 +138,8 @@ https://antd.newlifex.com
   // 表单相关
   searchForm: Ref<EmptyObjectType>;
   infoForm: Ref<EmptyObjectType>;
+  // 相关操作
+  handle: PageHandle
 }
 ```
 
@@ -150,9 +188,9 @@ https://antd.newlifex.com
 </template>
 
 <script setup lang="ts">
-import usePageSetting from '/@/hook/usePageSetting'
+import usePage from '/@/hook/usePage'
 import { ColumnKind, usePageApi } from '/@/api/page';
-const { columns, forms } = usePageSetting({
+const { columns, forms } = usePage({
   columns: [
     {
       in: ColumnKind.ADD,
Modified +19 -7
diff --git a/src/api/page.ts b/src/api/page.ts
index cf60317..d42b348 100644
--- a/src/api/page.ts
+++ b/src/api/page.ts
@@ -34,12 +34,23 @@ export function usePageApi() {
 				params
 			});
 		},
-		getTableDetail: <T extends {}>(type: string, id: number) => {
+		getTableDetail: <T extends {}>(type: string, id: number, data?: EmptyObjectType) => {
 			return request<T>({
 				url: type + '/Detail',
 				method: 'get',
 				params: {
-					id
+					id,
+					...data
+				}
+			});
+		},
+		getTableDetailByUrl: <T extends {}>(url: string, id: number, data?: EmptyObjectType) => {
+			return request<T>({
+				url: url,
+				method: 'get',
+				params: {
+					id,
+					...data
 				}
 			});
 		},
@@ -76,17 +87,18 @@ export function usePageApi() {
 			});
 		},
 		upload: (data: { r: string, file: File }) => {
-			// console.log('upload')
 			const formData = new FormData();
-			formData.append('r', data.r);
 			formData.append('file', data.file);
 			return request<{ [k in string]: EmptyObjectType[] }>({
-				url: '/Admin/File/Upload',
+				url: '/Admin/File/UploadLayui',
 				method: 'post',
 				headers: {
-					"Content-Type": 'multipart/form-data;charset=UTF-8'
+					"Content-Type": 'multipart/form-data'
 				},
-				params: formData
+				data: formData,
+				params: {
+					r: 'user'
+				}
 			});
 		}
 	};
Added +20 -0
diff --git a/src/api/project.ts b/src/api/project.ts
new file mode 100644
index 0000000..f903f18
--- /dev/null
+++ b/src/api/project.ts
@@ -0,0 +1,20 @@
+import request from '/@/utils/request';
+
+/**
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 登录api接口集合
+ * @method signIn 用户登录
+ * @method signOut 用户退出登录
+ */
+export function useProjectApi() {
+	return {
+    workExamine: (data: { id: number; examine: 0 | 1 | 2 }) => {
+			return request({
+				url: '/Projects/Work/WorkExamine',
+				method: 'post',
+				data,
+			});
+		},
+	};
+}
\ No newline at end of file
Added +108 -0
diff --git a/src/components/cascader/index.vue b/src/components/cascader/index.vue
new file mode 100644
index 0000000..a16876b
--- /dev/null
+++ b/src/components/cascader/index.vue
@@ -0,0 +1,108 @@
+<template>
+  <el-cascader class="w-full" :props="caProps" v-model="caValue" />
+</template>
+
+<script setup lang="ts" name="cascader">
+import { useVModel } from '@vueuse/core';
+import type { CascaderProps } from 'element-plus'
+import { computed, ref, useAttrs, watch } from 'vue';
+import { usePageApi } from '/@/api/page';
+import { deepMerge } from '/@/utils/other';
+type PropApi = (...arr: any) => Promise<Array<EmptyObjectType>>
+interface Props {
+  // 读取store缓存
+  storeKey?: string;
+  // 值字段名
+  valueKey?: string;
+  // 名称字段名
+  labelKey?: string;
+  // 子集字段名
+  childrenKey?: string;
+  // 请求返回值字段名
+  resultKey?: string;
+  // 父级地段名
+  parentKey?: string;
+  // 用与将数组拆分赋值给多个字段
+  modelKeys?: string[];
+  // 绑定的值,可绑定表单对象或者属性值,当绑定表单对象时,需要搭配modelKeys一起使用
+  modelValue?: any;
+  // 请求方法
+  api?: PropApi | Array<PropApi>
+  // 请求地址
+  url?: string | Array<string>
+  // 总层级
+  level?: number;
+  // 最后以及标识字段
+  leafKey?: string;
+  // 请求携带的参数
+  requestProps?: EmptyObjectType;
+}
+interface Emits {
+  (e: 'update:model-value', val: any): void;
+  (e: 'optionRequestAfter', val: EmptyArrayType, level: number): void;
+}
+const props = withDefaults(defineProps<Props>(), {
+  valueKey: 'id',
+  labelKey: 'name',
+  resultKey: 'data',
+  childrenKey: 'children',
+  parentKey: 'parentId'
+})
+const emits = defineEmits<Emits>()
+
+const vModel = useVModel(props, 'modelValue', emits)
+
+const caValue = computed({
+  get () {
+    if (props.modelKeys) {
+      let val = props.modelKeys.map(key => props.modelValue[key])
+      for (let i = val.length - 1; i >= 0; i--) {
+        if (!val[i]) val.splice(i, 1)
+        else break;
+      }
+      return val
+    }
+    return props.modelValue
+  },
+  set (val: Array<string | number>) {
+    let value = val || []
+    if (props.modelKeys) {
+      props.modelKeys.forEach((key, i) => {
+        vModel.value[key] = value[i]
+      })
+    } else {
+      vModel.value = value
+    }
+  }
+})
+
+const attrs = useAttrs();
+const caProps = (attrs.props || {}) as CascaderProps;
+if (props.api || props.url) {
+  caProps.lazy = true;
+  caProps.value = props.valueKey!;
+  caProps.label = props.labelKey!;
+  caProps.children = props.childrenKey!;
+  const { getTableData } = usePageApi()
+  caProps.lazyLoad = async (node, resolve) => {
+    const { level, value } = node
+    let res
+    const requestProps = deepMerge(props.requestProps, level ? {[props.parentKey]: value} : {})
+    if (props.api) {
+      res = await Array.isArray(props.api) ? props.api[level](requestProps) : (props.api as PropApi)(requestProps)
+    } else {
+      res = await getTableData(Array.isArray(props.url) ? props.url[level] : props.url!, requestProps)
+    }
+    emits('optionRequestAfter', res[props.resultKey], level)
+    const nodes = res[props.resultKey].map((item: EmptyObjectType) => ({
+      ...item,
+      leaf: props.leafKey ? item[props.leafKey] : props.level ? level >= props.level : true
+    }))
+    resolve(nodes)
+  }
+}
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
Modified +3 -3
diff --git a/src/components/checkboxGroup/index.vue b/src/components/checkboxGroup/index.vue
index 5163beb..1513a97 100644
--- a/src/components/checkboxGroup/index.vue
+++ b/src/components/checkboxGroup/index.vue
@@ -4,13 +4,13 @@
       {{item[props.labelKey]}}
     </el-checkbox>
   </el-checkbox-group>
-  
 </template>
 
 <script setup lang="ts">
 import useOptions from '/@/hook/useOptions';
-import { optionProps } from '/@/utils/optionProps';
+import { OptionEmits, optionProps } from '/@/utils/optionProps';
 
 const props = defineProps(optionProps);
-const { myOptions } = useOptions(props)
+const emits = defineEmits<OptionEmits>()
+const { myOptions } = useOptions(props, emits)
 </script>
Modified +3 -2
diff --git a/src/components/form/component.ts b/src/components/form/component.ts
index 642dade..f77e5c8 100644
--- a/src/components/form/component.ts
+++ b/src/components/form/component.ts
@@ -1,12 +1,12 @@
-import { ElAutocomplete, ElCascader, ElCheckbox, ElColorPicker, ElDatePicker, ElDivider, ElInput, ElInputNumber, ElRadio, ElRate, ElSlider, ElSwitch, ElTimePicker, ElTimeSelect } from "element-plus";
+import { ElAutocomplete, ElCheckbox, ElColorPicker, ElDatePicker, ElDivider, ElInput, ElInputNumber, ElRadio, ElRate, ElSlider, ElSwitch, ElTimePicker, ElTimeSelect } from "element-plus";
 import Select from '../select/index.vue'
 import CheckboxGroup from '../checkboxGroup/index.vue'
 import RadioGroup from '../radioGroup/index.vue'
 import Editor from '../editor/index.vue'
 import Upload from '../upload/index.vue'
+import Cascader from '../cascader/index.vue'
 export const forms = {
   autocomplete: ElAutocomplete,
-  cascader: ElCascader,
   checkbox: ElCheckbox,
   checkboxGroup: CheckboxGroup,
   colorPicker: ElColorPicker,
@@ -24,4 +24,5 @@ export const forms = {
   editor: Editor,
   divider: ElDivider,
   upload: Upload,
+  cascader: Cascader,
 }
Modified +1 -1
diff --git a/src/components/form/hooks/useForm.ts b/src/components/form/hooks/useForm.ts
index f4a2889..7331273 100644
--- a/src/components/form/hooks/useForm.ts
+++ b/src/components/form/hooks/useForm.ts
@@ -27,6 +27,6 @@ export default function useForm (props: FormProps, emits: FormEmits) {
     formEl,
     formValue,
     onChange,
-    getColBind
+    getColBind,
   }
 }
\ No newline at end of file
Modified +51 -19
diff --git a/src/components/form/index.vue b/src/components/form/index.vue
index 3e18ada..8191416 100644
--- a/src/components/form/index.vue
+++ b/src/components/form/index.vue
@@ -1,10 +1,10 @@
 <template>
   <component :is="layoutComponent" v-bind="layoutBind" v-model="layoutVisible" class="form-wrapper" :class="{'h-full': wrapper !== 'dialog'}">
     <div class="h-full flex flex-col overflow-auto form-body">
-      <el-tabs type="border-card" :class="{'border-t-0': wrapper === 'drawer'}" v-model="activeName" v-if="tabs.length">
+      <el-tabs type="border-card" :class="{'border-t-0': wrapper === 'drawer'}" v-model="activeName" v-if="tabs.length > 1">
         <el-tab-pane :name="item" :key="item" v-for="item in tabs">
           <template #label>
-            {{ item || '常规' }}
+            {{ item || DEFAULT_TAB_NAME }}
           </template>
         </el-tab-pane>
       </el-tabs>
@@ -12,17 +12,21 @@
         <el-form ref="formEl" :model="formValue" size="default" label-width="120px" class="table-form">
           <el-row>
             <template v-for="(item, key) in config" :key="key">
-              <el-col v-bind="getColBind(item.col)" v-if="item.if === undefined ? true : item.if" v-show="getItemVShow(item)">
-                <el-form-item
-                  class="form-item"
-                  :label="item.label"
-                  :prop="item.prop"
-                  :rules="[{ required: item.required, message: `${item.label}不能为空`, trigger: item.component === 'input' || item.component === 'inputNumber' ? 'blur' : 'change' }]"
-                  >
-                  <slot v-if="item.slot" :name="item.slot" :model="formValue" :prop="item.prop"></slot>
-                  <component v-else v-bind="item.props" v-model="formValue[item.prop]" :is="forms[item.component || 'input']" @change="onChange"></component>
-                </el-form-item>
-              </el-col>
+              <template v-if="item.if === undefined || (typeof item.if === 'function' ? item.if(formValue) : item.if)">
+                <el-divider v-if="item.component === 'divider'" v-show="getItemVShow(item, formValue)" v-bind="item.props">{{ item.label }}</el-divider>
+                <el-col v-bind="getColBind(item.col)" v-else v-show="getItemVShow(item, formValue)">
+                  <el-form-item
+                    class="form-item"
+                    :label="item.label"
+                    :prop="item.prop.toString()"
+                    :rules="[{ required: item.required, message: `${item.label}不能为空`, trigger: item.component === 'input' || item.component === 'inputNumber' ? 'blur' : 'change' }]"
+                    >
+                    <slot v-if="item.slot" :name="item.slot" :model="formValue" :prop="item.prop"></slot>
+                    <cascader v-else-if="Array.isArray(item.prop) && item.component === 'cascader'" v-bind="item.props" v-model="formValue" :modelKeys="item.prop" @change="onChange"></cascader>
+                    <component v-else v-bind="item.props" :model-value="getFormValueByKey(formValue, item.prop as string)" @update:model-value="(val: any) => setFormValueByKey(val, item.prop as string)" :is="forms[item.component || 'input']" @change="onChange"></component>
+                  </el-form-item>
+                </el-col>
+              </template>
             </template>
             <slot name="form-after"></slot>
           </el-row>
@@ -49,6 +53,10 @@ import useFormWrapper from './hooks/useFormWrapper';
 import useForm from './hooks/useForm';
 import { EditWrapper } from '../page/model';
 import { computed, ref, watch } from 'vue';
+import Cascader from '../../components/cascader/index.vue';
+import { isObjTrue } from '/@/utils/other';
+
+const DEFAULT_TAB_NAME = '常规'
 
 interface Props {
   wrapper?: EditWrapper;
@@ -82,7 +90,14 @@ const { formEl, formValue, onChange, getColBind } = useForm(props, emits)
 
 const activeName = ref('');
 const tabs = computed(() => {
-  let groups = props.groups || [...new Set(props.config.map(item => item.group))]
+  let groups = props.groups || [
+    ...new Set(
+      props.config.filter(
+        item => (typeof item.if === 'function' ? item.if(formValue.value) : isObjTrue(item.if)) && (typeof item.show === 'function' ? item.show(formValue.value) : isObjTrue(item.show))
+      ).map(item => item.group || DEFAULT_TAB_NAME)
+    )
+  ]
+  // console.log('groups', groups)
   if (!groups.length || (groups.length === 1 && !groups[0])) {
     return []
   }
@@ -102,10 +117,13 @@ watch(tabs, (val) => {
 //   activeName.value = tabs.value[tabs.value.findIndex(name => name === activeName.value) - 1]!
 // }
 
-const getItemVShow = (item: ColumnConfig) => {
-  const show = item.show === undefined ? true : item.show
-  return tabs.value.length ? item.group === activeName.value && show : show
+const getItemVShow = (item: ColumnConfig, formData: EmptyObjectType) => {
+  // const show = item.show === undefined ? true : item.show
+  const show = typeof item.show === 'function' ? item.show(formData) : isObjTrue(item.show)
+  const group = item.group || ""
+  return tabs.value.length ? (group === activeName.value || (!group && activeName.value === DEFAULT_TAB_NAME)) && show : show
 }
+
 const submit = async () => {
   if (!formEl.value) return
   await formEl.value.validate((valid, fields) => {
@@ -113,8 +131,7 @@ const submit = async () => {
       emits('submit', formValue.value)
       layoutVisible.value = false;
     } else {
-      // console.log('error submit!', fields)
-      ElMessage.error(fields)
+      ElMessage.error(Object.values(fields || {}).reduce((total, item) => total.concat(item), []).map(item => item.message).join('\n'))
     }
   })
 }
@@ -122,6 +139,21 @@ const cancel = () => {
   layoutVisible.value = false;
   emits('cancel')
 }
+
+const getFormValueByKey = (value: EmptyObjectType, key: string) => {
+  return key.split('.').reduce((total, key) => total ? total[key] : undefined, value)
+}
+const setFormValueByKey = (val: any, key: string) => {
+  let keys = key.split('.')
+  keys.reduce((total, key, i) => {
+    if (i === keys.length - 1) {
+      total[key] = val
+    } else if (!total[key]) {
+      total[key] = {}
+    }
+    return total[key] 
+  }, formValue.value)
+}
 defineExpose({
   formEl
 })
Modified +15 -15
diff --git a/src/components/form/model/form.ts b/src/components/form/model/form.ts
index f5f3690..abc7d06 100644
--- a/src/components/form/model/form.ts
+++ b/src/components/form/model/form.ts
@@ -7,29 +7,29 @@ export type InstanceProps<T extends DefineComponent> = InstanceType<T>['$props']
 export type ColumnProp<T> = T extends keyof FormType ? InstanceType<FormType[T]>['$props'] : undefined;
 
 export interface ColumnConfig {
-  // 字段名
-	prop: string;
-  // 字段中文名称
+  /** 字段名 */
+	prop: string | string[];
+  /** 字段中文名称 */
   label?: string;
-  // 组件
+  /** 组件 */
 	component?: keyof FormType;
-  // 是否渲染
-	if?: boolean;
-  // 是否显示
-	show?: boolean;
-  // 自定义组件插槽
+  /** 是否渲染 */
+	if?: boolean | ((data: EmptyObjectType) => boolean);
+  /** 是否显示 */
+	show?: boolean | ((data: EmptyObjectType) => boolean);
+  /** 自定义组件插槽 */
   slot?: string;
-  // 必填
+  /** 必填 */
 	required?: boolean;
-  // 组件参数
+  /** 组件参数 */
 	props?: ColumnProp<ColumnConfig['component']>;
-  // 校验规则
+  /** 校验规则 */
   rules?: FormRule[];
-  // 排序下标
+  /** 排序下标 */
   index?: number;
-  // 所占列数
+  /** 所占列数 */
 	col?: number | Col;
-  // 所属分组
+  /** 所属分组 */
 	group?: string;
   // isCheck?: boolean;
 }
Modified +53 -14
diff --git a/src/components/page/edit.vue b/src/components/page/edit.vue
index 5340a96..50df5b1 100644
--- a/src/components/page/edit.vue
+++ b/src/components/page/edit.vue
@@ -1,20 +1,21 @@
 <template>
   <Form :config="config" v-model="formData" v-model:visible="layoutVisible" :wrapper="wrapper" :title="myIsUpdate?'修改':'添加'" @submit="submit">
-    <template v-for="item in config.filter(item => item.slot)" :key="item.prop" #[`${item.slot!}`]="data">
+    <template v-for="item in config.filter(item => item.slot)" :key="item.prop.toString()" #[`${item.slot!}`]="data">
       <slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
     </template>
   </Form>
 </template>
 
 <script setup lang="ts">
-import { computed, ref, watch } from 'vue';
+import { computed, inject, ref, watch } from 'vue';
 import { usePageApi } from '/@/api/page';
 import Form from '/@/components/form/index.vue';
 import { ColumnConfig } from '../form/model/form';
 import { ElMessage } from 'element-plus';
-import { EditWrapper } from './model';
+import { EditWrapper, UsePageProps } from './model';
+import providePageKey from './provide/key';
 interface Props {
-  type: string;
+  type?: string;
   wrapper?: EditWrapper;
   visible: boolean;
   modelValue?: EmptyObjectType;
@@ -52,6 +53,9 @@ watch(() => props.isUpdate, (val) => {
   myIsUpdate.value = val
 })
 const pageApi = usePageApi();
+const providePage = inject(providePageKey)
+const { onAddBefore, onAddAfter, onEditBefore, onEditAfter } = providePage?.pageProps || {}
+let { detailConfig, addConfig, editConfig } = providePage?.pageProps || ({} as UsePageProps)
 
 const handleAdd = () => {
   if (myIsUpdate.value) {
@@ -60,24 +64,59 @@ const handleAdd = () => {
   myIsUpdate.value = false;
   layoutVisible.value = true;
 }
-const handleEdit = (id: number) => {
+const handleEdit = async (row: EmptyObjectType) => {
   formData.value = {}
   myIsUpdate.value = true;
   layoutVisible.value = true;
   loading.value = true
-  pageApi.getTableDetail(props.type, id).then(res => {
+  let res;
+  try {
+    const requestProps = typeof detailConfig?.requestProps === 'function' ? detailConfig.requestProps(row) : detailConfig?.requestProps;
+    if (detailConfig?.api) {
+      res = await detailConfig?.api({ id: row.id, ...requestProps })
+    } else if (detailConfig?.url) {
+      res = await pageApi.getTableDetailByUrl(detailConfig?.url, row.id, requestProps)
+    } else {
+      res = await pageApi.getTableDetail(props.type!, row.id, requestProps)
+    }
     loading.value = false
-		formData.value = res.data
-	}).catch(() => {
+    formData.value = res.data
+  } catch (error) {
     loading.value = false
-  })
+  }
 }
 const submit = () => {
-  const submitApi = myIsUpdate.value ? pageApi.setTableItem : pageApi.addTableItem
-  submitApi(props.type, formData.value).then(() => {
-    ElMessage.success('保存成功!')
+  async function submitFun () {
+    if (myIsUpdate.value) {
+      const requestProps = typeof editConfig?.requestProps === 'function' ? editConfig.requestProps(formData.value) : editConfig?.requestProps;
+      if (editConfig?.api) {
+        await editConfig?.api({ ...formData.value, ...requestProps })
+      } else if (editConfig?.url) {
+        await pageApi.setTableItem(editConfig?.url, { ...formData.value, ...requestProps })
+      } else {
+        await pageApi.setTableItem(props.type!, { ...formData.value, ...requestProps })
+      }
+      onEditAfter && onEditAfter(formData);
+    } else {
+      const requestProps = typeof addConfig?.requestProps === 'function' ? addConfig.requestProps(formData.value) : addConfig?.requestProps;
+      if (addConfig?.api) {
+        await addConfig?.api({ ...formData.value, ...requestProps })
+      } else if (addConfig?.url) {
+        await pageApi.addTableItem(addConfig?.url, { ...formData.value, ...requestProps })
+      } else {
+        await pageApi.addTableItem(props.type!, { ...formData.value, ...requestProps })
+      }
+      onAddAfter && onAddAfter(formData);
+    }
     emits('submitSuccess')
-  })
+  }
+  if (myIsUpdate.value) {
+    if (onEditBefore) onEditBefore(formData, submitFun)
+    else submitFun()
+  } else {
+    if (onAddBefore) onAddBefore(formData, submitFun)
+    else submitFun()
+  }
 }
 defineExpose({
   handleAdd,
@@ -87,4 +126,4 @@ defineExpose({
 
 <style scoped>
 
-</style>
\ No newline at end of file
+</style>
Modified +36 -20
diff --git a/src/components/page/hook/useGetColumnsForm.ts b/src/components/page/hook/useGetColumnsForm.ts
index 3a92193..7113d41 100644
--- a/src/components/page/hook/useGetColumnsForm.ts
+++ b/src/components/page/hook/useGetColumnsForm.ts
@@ -1,20 +1,20 @@
 import { inject, ref, Ref, unref, watch } from "vue";
 import { ColumnConfig } from "../../form/model/form";
 import { TableColumn } from "../../table/type";
-import { EventSettingRef } from "../model";
-import providePageKey from "../provide/key";
+import { ColumnIn, UsePageProps, EventSettingRef, ProvidePage } from "../model";
+// import providePageKey from "../provide/key";
 import { ColumnKind, usePageApi } from "/@/api/page";
 import { deepMerge, getInfoFields, getSearchFields, getTableFields, toCamelCase } from "/@/utils/other";
 interface Props {
-  type: string;
+  type?: string;
 	searchData?: EmptyObjectType;
 }
 interface Emits {
 	(e: 'update:searchData', val: EmptyObjectType): void;
 }
 
-export default function useGetColumnsForm (props: Props, emits: Emits) {  
-  const providePage = inject(providePageKey)
+export default function useGetColumnsForm (props: Props, emits: Emits, providePage?: ProvidePage) {  
+  // const providePage = inject(providePageKey)
   
   const editConfig = (providePage?.editColumns || ref([])) as Ref<ColumnConfig[]>;
   const addConfig = (providePage?.addColumns || ref([])) as Ref<ColumnConfig[]>;
@@ -32,26 +32,41 @@ export default function useGetColumnsForm (props: Props, emits: Emits) {
     
   // 配置修改
   const setting = (oldSetting: EventSettingRef) => {
-    if (providePage?.newSettings.columns) {
-      let newArr = providePage.newSettings.columns.filter(item => item.in === undefined || item.in === oldSetting.type || (Array.isArray(item.in) && item.in.some(v => v === oldSetting.type)))
-      let oldArrVal: Array<ColumnConfig | TableColumn> = unref(oldSetting.config)
-      newArr.forEach((newItem) => {
-        let oldItem = oldArrVal.find(v => v.prop === newItem.prop)
+    // 当prop配置数组时需要拆分成单个的配置(cascader组件除外),若存在同名配置则合并配置
+    let columns = providePage?.pageProps.columns?.reduce((total, item) => {
+      if (item.component === 'cascader') {
+        total.push(item)
+      } else {
+        let propArray = Array.isArray(item.prop) ? item.prop : [item.prop]
+        propArray.forEach((prop, i) => {
+          total.push({ ...item, prop, index: item.index !== undefined ? item.index + i : undefined})
+        })
+      }
+      return total
+    }, [] as Array<(ColumnConfig | TableColumn) & ColumnIn>) || []
+    let newArr = columns.filter(item => item.in === undefined || item.in === oldSetting.type || (Array.isArray(item.in) && item.in.some(v => v === oldSetting.type)))
+    let oldArrVal: Ref<Array<ColumnConfig | TableColumn>> = oldSetting.config
+    newArr.forEach((newItem) => {
+      if (newItem.component === 'cascader' && Array.isArray(newItem.prop)) {
+        oldArrVal.value = oldArrVal.value.filter(oldItem => (newItem.prop as string[]).every(v => v !== oldItem.prop))
+        oldArrVal.value.push(newItem)
+      } else {
+        let oldItem = oldArrVal.value.find(v => v.prop === newItem.prop)
         if (oldItem) {
           // 合并配置
           oldItem = deepMerge(oldItem, newItem)
-        } else if (newItem.in) {
+        } else {
           // 追加配置
-          oldArrVal.push(newItem)
+          oldArrVal.value.push(newItem)
         }
-      })
-      // 排序
-      let sortArr = oldArrVal.filter(item => item.index !== undefined).sort((item1, item2) => item1.index! - item2.index!)
-      oldArrVal = oldArrVal.filter(item => item.index === undefined)
-      sortArr.forEach(item => {
-        oldArrVal.splice(item.index!, 0, item)
-      })
-    }
+      }
+    })
+    // 排序
+    let sortArr = oldArrVal.value.filter(item => item.index !== undefined).sort((item1, item2) => item1.index! - item2.index!)
+    oldArrVal.value = oldArrVal.value.filter(item => item.index === undefined)
+    sortArr.forEach(item => {
+      oldArrVal.value.splice(item.index!, 0, item)
+    })
   }
 
   const pageApi = usePageApi()
@@ -66,6 +81,7 @@ export default function useGetColumnsForm (props: Props, emits: Emits) {
   pageApi.getColumns(props.type, ColumnKind.EDIT).then(res => {
     editConfig.value = getInfoFields(res.data)
     setting({ type: ColumnKind.EDIT, config: editConfig })
+    console.log('editConfig', editConfig.value)
   })
   pageApi.getColumns(props.type, ColumnKind.ADD).then(res => {
     addConfig.value = getInfoFields(res.data)
Modified +92 -27
diff --git a/src/components/page/index.vue b/src/components/page/index.vue
index 117e7dc..36e829a 100644
--- a/src/components/page/index.vue
+++ b/src/components/page/index.vue
@@ -3,34 +3,53 @@
 		<div class="table-demo-padding layout-padding-view layout-padding-auto h-full">
 			<Table
 				v-if="wrapper !== 'div'"
+				class="table-demo"
 				ref="tableRef"
+				v-bind="config"
+				v-model:columns="columns"
+				v-model:search-data="searchForm"
 				:authId="authId"
 				:data="data"
-				:config="config"
-				v-model:columns="columns"
 				:search="search"
 				:param="param"
 				:pager-visible="pagerVisible"
-				v-model:search-data="searchForm"
-				class="table-demo"
 				@del="onTableDelRow"
 				@add="onTableAdd"
 				@edit="onTableEdit"
 				@search="onSearch"
 				@sortHeader="onSortHeader">
-				<template v-for="item in search.filter(v => v.slot)" :key="item.prop" #[`${item.slot!}`]="data">
+				<template v-for="item in search.filter(v => v.slot)" :key="item.prop.toString()" #[`${item.slot!}`]="data">
 					<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
 				</template>
 				<template v-for="item in columns.filter(v => v.slot)" :key="item.prop" #[`${item.slot!}`]="data">
 					<slot :name="item.slot" :scope="data.scope"></slot>
 				</template>
-			</Table>
-			<Edit ref="editEl" v-model:visible="editVisible" :type="type" :wrapper="wrapper" :config="isUpdate ? editConfig : addConfig" :key="type" v-model="editForm" :isUpdate="isUpdate" @submit-success="getTableData">
-				<template v-for="item in editConfig.filter(item => item.slot)" :key="item.prop" #[`${item.slot!}`]="data">
-					<slot v-if="isUpdate" :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+				<template #table-top>
+					<slot name="table-top"></slot>
+				</template>
+				<template #table-handle-before="{ scope }">
+					<slot name="table-handle-before" :scope="scope"></slot>
+				</template>
+				<template #table-handle-after="{ scope }">
+					<slot name="table-handle-after" :scope="scope"></slot>
 				</template>
-				<template v-for="item in addConfig.filter(item => item.slot)" :key="item.prop" #[`${item.slot!}`]="data">
-					<slot v-if="!isUpdate" :name="item.slot" :model="data.model" :prop="data.prop"></slot>
+			</Table>
+			<Edit
+				ref="editEl"
+				v-model:visible="editVisible"
+				:type="type"
+				:wrapper="wrapper"
+				:config="isUpdate ? editConfig : addConfig"
+				:key="type"
+				v-model="editForm"
+				:isUpdate="isUpdate"
+				@submit-success="getTableData"
+				@add-before="$attrs.onAddBefore"
+				@add-after="$attrs.onAddAfter"
+				@edit-before="$attrs.onEditBefore"
+				@edit-after="$attrs.onEditAfter">
+				<template v-for="item in slots" :key="item.prop.toString()" #[`${item.slot!}`]="data">
+					<slot v-if="(isUpdate && item.in?.indexOf(ColumnKind.EDIT) !== -1) || (!isUpdate && item.in?.indexOf(ColumnKind.ADD) !== -1)" :name="item.slot" :model="data.model" :prop="data.prop"></slot>
 				</template>
 			</Edit>
 		</div>
@@ -38,20 +57,22 @@
 </template>
 
 <script setup lang="ts">
-import { defineAsyncComponent, ref } from 'vue';
+import { computed, defineAsyncComponent, inject, ref } from 'vue';
 import { ElMessage } from 'element-plus';
-import { usePageApi } from '../../api/page';
+import { ColumnKind, usePageApi } from '../../api/page';
 import Edit from './edit.vue';
 import { TableColumn } from '../table/type';
 import { EditWrapper } from './model';
 import useGetColumnsForm from './hook/useGetColumnsForm';
+import { ColumnConfig } from '../form/model/form';
+import providePageKey from './provide/key';
 
 interface Props {
-	type: string;
+	type?: string;
 	authId?: number;
 	searchData?: EmptyObjectType;
 	editWrapper?: EditWrapper;
-	tableConfig?: TableConfigType;
+	// tableConfig?: TableConfigType;
 }
 interface Emits {
 	(e: 'update:searchData', val: EmptyObjectType): void;
@@ -65,8 +86,28 @@ const editVisible = ref(false);
 // 引入组件
 const Table = defineAsyncComponent(() => import('/@/components/table/index.vue'));
 const wrapper = ref<EditWrapper>(props.editWrapper || 'drawer');
+
+const providePage = inject(providePageKey)
+const tableConfig = providePage?.pageProps.tableConfig || {}
+
 // 初始化配置项数据与表单数据
-const { searchForm, editForm, editConfig, addConfig, search, columns } = useGetColumnsForm(props, emits)
+const { searchForm, editForm, editConfig, addConfig, search, columns } = useGetColumnsForm(props, emits, providePage)
+const { onAddClick, onEditClick, onDelBefore, onDelAfter } = providePage?.pageProps || {};
+
+const slots = computed(() => {
+	let values: Array<ColumnConfig & { in?: ColumnKind[] }> = editConfig.value.filter(item => item.slot).map(item => ({ ...item, in: [ColumnKind.EDIT] }))
+	addConfig.value.forEach(item => {
+		if (item.slot) {
+			let i = values.findIndex(val => val.prop === item.prop)
+			if (i !== -1) {
+				values[i].in?.push(ColumnKind.ADD)
+			} else {
+				values.push({...item, in: [ColumnKind.ADD]})
+			}
+		}
+	})
+	return values
+})
 
 // 定义变量内容
 const tableRef = ref<RefType>();
@@ -81,18 +122,28 @@ const config = ref<TableConfigType>({
 	isSerialNo: false, // 是否显示表格序号
 	isSelection: true, // 是否显示表格多选
 	isOperate: true, // 是否显示表格操作栏
-	...props.tableConfig
+	...providePage?.pageProps.tableConfig
 })
 const param = ref({
 	pageIndex: 1,
 	pageSize: 20,
+	sort: '',
+	desc: false
 })
 
 // 初始化列表数据
-const getTableData = () => {
+const getTableData = async () => {
 	config.value.loading = true;
 	data.value = [];
-	pageApi.getTableData(props.type, { ...param.value, ...searchForm.value }).then(res => {
+	let res;
+	try {
+		const requestProps = tableConfig?.requestProps;
+		if (tableConfig?.api) {
+			res = await tableConfig?.api({ ...requestProps, ...param.value, ...searchForm.value })
+		} else {
+			let url = tableConfig?.url || props.type!
+			res = await pageApi.getTableData(url, { ...requestProps, ...param.value, ...searchForm.value })
+		}
 		if (Array.isArray(res.data)) {
 			data.value = res.data || []
 		} else if (typeof res.data === 'object') {
@@ -105,31 +156,40 @@ const getTableData = () => {
 			config.value.total = Number(res.page.totalCount)
 		}
 		config.value.loading = false;
-	}).catch(() => {
+	} catch (error) {
 		config.value.loading = false;
-	})
+	}
 };
 
 // 删除当前项回调
 const onTableDelRow = (item: EmptyObjectType) => {
-	pageApi.delTableItem(props.type, item.id).then(() =>{
-		ElMessage.success(`删除成功!`);
-		getTableData();
-	})
-	getTableData();
+	function delFun () {
+		pageApi.delTableItem(props.type!, item.id).then(() =>{
+			ElMessage.success(`删除成功!`);
+			getTableData();
+			onDelAfter && onDelAfter(item);
+		})
+	}
+	// emits('delBefore', item, delFun)
+	if (onDelBefore) onDelBefore(item, delFun)
+	else delFun()
 };
 const onTableAdd = () => {
 	isUpdate.value = false
 	editEl.value?.handleAdd();
+	onAddClick && onAddClick()
 }
 const onTableEdit = (item: EmptyObjectType) => {
 	isUpdate.value = true
-	editEl.value?.handleEdit(item.id);
+	editEl.value?.handleEdit(item);
+	onEditClick && onEditClick(item);
 }
 // 分页改变时回调
 const onSearch = (page: TableDemoPageType) => {
 	param.value.pageIndex = page.pageIndex;
 	param.value.pageSize = page.pageSize;
+	param.value.sort = page.sort;
+	param.value.desc = page.desc;
 	getTableData();
 };
 // 拖动显示列排序回调
@@ -138,6 +198,11 @@ const onSortHeader = (data: TableColumn[]) => {
 };
 getTableData();
 
+defineExpose({
+	getTableData
+})
+providePage && (providePage.handle.reload = getTableData)
+
 </script>
 
 <style scoped lang="scss">
Modified +52 -8
diff --git a/src/components/page/model/index.ts b/src/components/page/model/index.ts
index 29e8562..a0fdc6a 100644
--- a/src/components/page/model/index.ts
+++ b/src/components/page/model/index.ts
@@ -1,6 +1,7 @@
+import { TableProps } from "element-plus";
 import { Ref } from "vue";
 import { ColumnConfig } from "../../form/model/form";
-import { TableColumn } from "../../table/type";
+import { TableColumn, TableMoreProps } from "../../table/type";
 import { ColumnKind } from "/@/api/page";
 
 export type EditWrapper = 'div' | 'dialog' | 'drawer';
@@ -33,16 +34,58 @@ export interface EventSettingRef {
 	formData?: Ref<EmptyObjectType>;
 }
 
-// type EventSettingPropsList = {
+// type UsePagePropsList = {
 // 	in: ColumnKind.LIST
 // } & TableColumn
-// type EventSettingPropsForm = {
+// type UsePagePropsForm = {
 // 	in: ColumnKind.ADD | ColumnKind.DETAIL | ColumnKind.EDIT | ColumnKind.SEARCH
 // } & ColumnConfig
-// type EventSettingPropsCommon = ({in?: ColumnKind[]} & TableColumn) | ({ in?: ColumnKind[]} & ColumnConfig);
+// type UsePagePropsCommon = ({in?: ColumnKind[]} & TableColumn) | ({ in?: ColumnKind[]} & ColumnConfig);
 
-export interface EventSettingProps {
-	columns?: Array<({ in?: ColumnKind | Array<ColumnKind> } & TableColumn) | ({ in?: ColumnKind | Array<ColumnKind> } & ColumnConfig)>
+export interface ColumnIn {
+	in?: ColumnKind | Array<ColumnKind>;
+}
+export interface MoreProp extends ColumnIn {
+	/**参数名 */
+	prop: string | Array<string>;
+}
+export interface UsePageProps {
+	/** 表单配置 */
+	columns?: Array<(Omit<TableColumn, 'prop'> & MoreProp) | (Omit<ColumnConfig, 'prop'> & MoreProp)>;
+	/** 表格配置 */
+	tableConfig?: Partial<TableProps<EmptyObjectType>> & Partial<TableMoreProps> & {
+		api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+		url?: string;
+		requestProps?: EmptyObjectType;
+		handleWidth?: number;
+	},
+	detailConfig?: {
+		api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+		url?: string;
+		requestProps?: EmptyObjectType | ((row: EmptyObjectType) => EmptyObjectType);
+	},
+	addConfig?: {
+		api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+		url?: string;
+		requestProps?: EmptyObjectType;
+	},
+	editConfig?: {
+		api?: (...props: EmptyArrayType) => Promise<EmptyObjectType | Array<EmptyObjectType>>;
+		url?: string;
+		requestProps?: EmptyObjectType;
+	},
+	onAddClick?: () => void;
+	onAddBefore?: (data: EmptyObjectType, addFun: Function) => void;
+	onAddAfter?: (data: EmptyObjectType) => void;
+	onEditClick?: (data: EmptyObjectType) => void;
+	onEditBefore?: (data: EmptyObjectType, editFun: Function) => void;
+	onEditAfter?: (data: EmptyObjectType) => void;
+	onDelBefore?: (data: EmptyObjectType, delFun: Function) => void;
+	onDelAfter?: (data: EmptyObjectType) => void;
+}
+
+export interface PageHandle {
+	reload: () => void;
 }
 
 export interface ProvidePage {
@@ -53,5 +96,6 @@ export interface ProvidePage {
 	detailColumns: Ref<ColumnConfig[]>;
 	searchForm: Ref<EmptyObjectType>;
 	infoForm: Ref<EmptyObjectType>;
-	newSettings: EventSettingProps;
-}
\ No newline at end of file
+	pageProps: UsePageProps;
+	handle: PageHandle,
+}
Modified +3 -2
diff --git a/src/components/radioGroup/index.vue b/src/components/radioGroup/index.vue
index 6a6e985..d3ffac5 100644
--- a/src/components/radioGroup/index.vue
+++ b/src/components/radioGroup/index.vue
@@ -9,8 +9,9 @@
 
 <script setup lang="ts">
 import useOptions from '/@/hook/useOptions';
-import { optionProps } from '/@/utils/optionProps';
+import { OptionEmits, optionProps } from '/@/utils/optionProps';
 
 const props = defineProps(optionProps);
-const { myOptions } = useOptions(props)
+const emits = defineEmits<OptionEmits>()
+const { myOptions } = useOptions(props, emits)
 </script>
Modified +3 -2
diff --git a/src/components/select/index.vue b/src/components/select/index.vue
index c2723fb..aa8087c 100644
--- a/src/components/select/index.vue
+++ b/src/components/select/index.vue
@@ -11,10 +11,11 @@
 
 <script setup lang="ts">
 import useOptions from '/@/hook/useOptions';
-import { optionProps } from '/@/utils/optionProps';
+import { OptionEmits, optionProps } from '/@/utils/optionProps';
 
 const props = defineProps(optionProps);
-const { myOptions } = useOptions(props)
+const emits = defineEmits<OptionEmits>()
+const { myOptions } = useOptions(props, emits)
 
 </script>
 
Modified +48 -25
diff --git a/src/components/table/index.vue b/src/components/table/index.vue
index 84905a8..e56cdcf 100644
--- a/src/components/table/index.vue
+++ b/src/components/table/index.vue
@@ -8,29 +8,33 @@
 				<slot :name="item.slot" :model="data.model" :prop="data.prop"></slot>
 			</template>
 		</Search>
+		<slot name="table-top"></slot>
 		<el-table
+			style="width: 100%"
+			row-key="id"
+			stripe
 			:data="data"
 			:border="setBorder"
 			v-bind="$attrs"
-			row-key="id"
-			stripe
-			style="width: 100%"
-			v-loading="config.loading"
-			@selection-change="onSelectionChange">
-			<el-table-column type="selection" :reserve-selection="true" width="40" fixed="left" v-if="config.isSelection && tableColumns.length" />
-			<el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo && tableColumns.length" />
+			v-loading="loading"
+			@selection-change="onSelectionChange"
+			@sort-change="onChangeSort">
+			<el-table-column type="selection" :reserve-selection="true" width="40" fixed="left" v-if="isSelection && tableColumns.length" />
+			<el-table-column type="index" label="序号" width="60" v-if="isSerialNo && tableColumns.length" />
 			<template v-for="(item, index) in tableColumns" :key="index">
 				<el-table-column
 					show-overflow-tooltip
 					:prop="item.prop"
 					:width="item.width"
 					:label="item.label"
+					:sortable="item.sort ? 'custom' : false"
 					v-if="(item.if === undefined || item.if) && (item.isCheck === undefined || item.isCheck)"
 				>
 					<template v-slot="scope">
 						<slot v-if="item.slot" :name="item.slot" :scope="scope" :model="scope.row" :prop="item.prop"></slot>
 						<template v-else-if="item.component === 'upload'">
-							<img :src="scope.row[item.prop]" width="50px" height="50px" />
+							<!-- <img :src="scope.row[item.prop]" width="50px" height="50px" /> -->
+							<el-image :src="imgBaseUrl + scope.row[item.prop]" class="w-12 h-12 rounded" :preview-src-list="[imgBaseUrl + scope.row[item.prop]]" preview-teleported fit="fill"></el-image>
 						</template>
 						<template v-else-if="item.component === 'switch'">
 							<el-tag :type="scope.row[item.prop] ? '' : 'info'" effect="dark">
@@ -48,17 +52,19 @@
 				</el-table-column>
 			</template>
 			<el-table-column
-				v-if="config.isOperate && tableColumns.length && auths(getBtnAuth(Auth.SET, Auth.DEL))"
+				v-if="isOperate && tableColumns.length && auths(getBtnAuth(Auth.SET, Auth.DEL))"
 				label="操作"
 				:width="operateWidth"
 				fixed="right">
 				<template v-slot="scope">
+					<slot name="table-handle-before" :scope="scope"></slot>
 					<el-button v-auths="getBtnAuth(Auth.SET)" text type="primary" @click="onEdit(scope.row)">修改</el-button>
 					<el-popconfirm title="确定删除吗?" @confirm="onDel(scope.row)">
 						<template #reference>
 							<el-button v-auths="getBtnAuth(Auth.DEL)" text type="danger">删除</el-button>
 						</template>
 					</el-popconfirm>
+					<slot name="table-handle-after" :scope="scope"></slot>
 				</template>
 			</el-table-column>
 			<template #empty>
@@ -73,7 +79,7 @@
 				v-model:page-size="state.page.pageSize"
 				:pager-count="5"
 				:page-sizes="[10, 20, 30]"
-				:total="config.total"
+				:total="total"
 				layout="total, sizes, prev, pager, next, jumper"
 				background
 				@size-change="onHandleSizeChange"
@@ -138,7 +144,7 @@
 
 <script setup lang="ts" name="netxTable">
 import { reactive, computed, nextTick, watch, Ref } from 'vue';
-import { ElMessage } from 'element-plus';
+import { ColumnSortHandler, ColumnSortParams, ElMessage } from 'element-plus';
 import table2excel from 'js-table2excel';
 import { storeToRefs } from 'pinia';
 import { useThemeConfig } from '/@/stores/themeConfig';
@@ -154,33 +160,42 @@ import { useEnumOptions } from '/@/stores/enumOptions';
 interface Param {
 	pageIndex?: number;
 	pageSize?: number;
+	sort?: string,
+	desc?: boolean,
 }
-interface Props {
+export interface Props {
+	total?: number;
+  loading?: boolean;
+  isBorder?: boolean;
+  isSerialNo?: boolean;
+  isSelection?: boolean;
+  isOperate?: boolean;
+	operateWidth?: number;
 	authId?: number;
 	data: EmptyObjectType[];
 	columns: TableColumn[];
-	config: TableConfigType;
 	search: ColumnConfig[];
 	param: Param;
 	pagerVisible: boolean;
 	searchData: EmptyObjectType;
 }
+const imgBaseUrl = import.meta.env.VITE_IMG_BASE_URL
 // 定义父组件传过来的值
 const props = withDefaults(defineProps<Props>(), {
+	total: 0, // 列表总数
+	loading: true, // loading 加载
+	isBorder: false, // 是否显示表格边框
+	isSerialNo: false, // 是否显示表格序号
+	isSelection: true, // 是否显示表格多选
+	isOperate: true, // 是否显示表格操作栏
 	data: () => [],
 	columns: () => [],
-	config: () => ({
-		total: 0, // 列表总数
-		loading: true, // loading 加载
-		isBorder: false, // 是否显示表格边框
-		isSerialNo: false, // 是否显示表格序号
-		isSelection: true, // 是否显示表格多选
-		isOperate: true, // 是否显示表格操作栏
-	}),
 	search: () => [],
 	param: () => ({
 		pageIndex: 1,
 		pageSize: 10,
+		sort: '',
+		desc: false,
 	}),
 	pagerVisible: true,
 	searchData: () => ({})
@@ -189,8 +204,8 @@ const props = withDefaults(defineProps<Props>(), {
 const { options } = useEnumOptions()
 
 const operateWidth = computed(() => {
-	if (props.config.operateWidth)
-		return props.config.operateWidth
+	if (props.operateWidth)
+		return props.operateWidth
 	return (auths(getBtnAuth(Auth.SET)) ? 55 : 0) + (auths(getBtnAuth(Auth.DEL)) ? 55 : 0)
 })
 
@@ -219,11 +234,14 @@ const searchDataRef = computed({
 
 // 设置边框显示/隐藏
 const setBorder = computed(() => {
-	return props.config.isBorder ? true : false;
+	return props.isBorder ? true : false;
 });
 // 获取父组件 配置项(必传)
 const getConfig = computed(() => {
-	return props.config;
+	return {
+		isSerialNo: props.isSerialNo,
+		isSelection: props.isSelection,
+	};
 });
 // 设置 tool columns 数据
 // const setHeader = computed(() => {
@@ -291,6 +309,11 @@ const onImportTable = () => {
 const onRefreshTable = () => {
 	emit('search', state.page);
 };
+const onChangeSort = (data: { prop: string; order: 'descending' | 'ascending' | null }) => {
+	state.page.sort = data.order ? data.prop : '';
+	state.page.desc = data.order === 'descending';
+	emit('search', state.page);
+}
 // 设置
 const onSetTable = () => {
 	nextTick(() => {
Modified +6 -2
diff --git a/src/components/table/search.vue b/src/components/table/search.vue
index ff436e6..eaf9476 100644
--- a/src/components/table/search.vue
+++ b/src/components/table/search.vue
@@ -36,6 +36,7 @@
 import { reactive, ref, onMounted, computed } from 'vue';
 import Form from '/@/components/form/index.vue'
 import { ColumnConfig } from '../form/model/form';
+import { isObjTrue } from '/@/utils/other';
 interface Props {
 	search: ColumnConfig[];
 	modelValue: EmptyObjectType;
@@ -60,7 +61,10 @@ const formData = computed({
 	}
 })
 const config = computed(() => {
-	return props.search.map((item, index) => ({
+	return props.search.filter(item => 
+		(typeof item.if === 'function' ? item.if(formData.value) : isObjTrue(item.if)) &&
+		(typeof item.show === 'function' ? item.show(formData.value) : isObjTrue(item.show))
+	).map((item, index) => ({
 		...item,
 		show: index === 0 || state.isToggle
 	}))
@@ -87,7 +91,7 @@ const onReset = () => {
 // 初始化 form 字段,取自父组件 search.prop
 const initFormField = () => {
 	if (props.search.length <= 0) return false;
-	props.search.forEach((v) => (formData.value[v.prop] = ''));
+	props.search.forEach((v) => (formData.value[v.prop.toString()] = ''));
 };
 // 页面加载时
 onMounted(() => {
Modified +25 -1
diff --git a/src/components/table/type.ts b/src/components/table/type.ts
index 4dc006c..47aa09b 100644
--- a/src/components/table/type.ts
+++ b/src/components/table/type.ts
@@ -1,5 +1,5 @@
 import { FormRule } from "@form-create/element-ui";
-import { ColumnProp, FormType } from "../form/model/form";
+import { ColumnConfig, ColumnProp, FormType } from "../form/model/form";
 
 export interface TableColumn {
   // 字段名
@@ -26,6 +26,8 @@ export interface TableColumn {
   index?: number;
   // 是否渲染
   if?: boolean;
+  // 是否可排序
+  sort?: boolean;
 }
 
 export enum Auth {
@@ -35,3 +37,25 @@ export enum Auth {
   'DEL' = 8,
   'ALL' = 255,
 }
+
+interface Param {
+	pageIndex?: number;
+	pageSize?: number;
+}
+
+export interface TableMoreProps {
+	total?: number;
+  loading?: boolean;
+  isBorder?: boolean;
+  isSerialNo?: boolean;
+  isSelection?: boolean;
+  isOperate?: boolean;
+	operateWidth?: number;
+	authId?: number;
+	data: EmptyObjectType[];
+	columns: TableColumn[];
+	search: ColumnConfig[];
+	param: Param;
+	pagerVisible: boolean;
+	searchData: EmptyObjectType;
+}
Modified +38 -31
diff --git a/src/components/upload/index.vue b/src/components/upload/index.vue
index ff35d3a..bbef816 100644
--- a/src/components/upload/index.vue
+++ b/src/components/upload/index.vue
@@ -1,58 +1,66 @@
 <template>
   <el-upload
     class="avatar-uploader"
-    :http-request="request"
-    :show-file-list="fileListVisible"
+    :action="action"
+    :show-file-list="false"
     :on-success="handleAvatarSuccess"
     :before-upload="beforeAvatarUpload"
   >
-    <img v-if="imageUrl" :src="imageUrl" class="avatar" />
+    <img v-if="imageUrl" :src="baseUrl + imageUrl" class="avatar" />
     <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
   </el-upload>
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
-import { ElMessage, UploadRequestOptions } from 'element-plus'
+import { computed, ref } from 'vue'
+import { ElMessage } from 'element-plus'
 import { Plus } from '@element-plus/icons-vue'
-
 import type { UploadProps } from 'element-plus'
-import { Session } from '/@/utils/storage';
-import { usePageApi } from '/@/api/page';
 
 interface Props {
+  modelValue?: string;
   maxSize?: number;
+  limit?: number;
+  url?: string;
+  resultKey?: string;
 }
-const headers = {
-  Authorization: `${Session.get('token')}`,
-  // 'Content-Type': 'multipart/form-data'
+interface Emits {
+  (e: 'update:modelValue', val: string): void
 }
-const props = defineProps<Props>()
 
-const action = import.meta.env.VITE_API_URL + '/Admin/File/Upload'
+const props = withDefaults(defineProps<Props>(), {
+  limit: 1
+})
+const emits = defineEmits<Emits>()
 
-const imageUrl = ref('')
-const fileListVisible = ref(false)
+const action = import.meta.env.VITE_API_URL + props.url
+const baseUrl = import.meta.env.VITE_IMG_BASE_URL
 
-const { upload } = usePageApi()
-const request = (options: UploadRequestOptions) => {
-  return upload({ r: 'user', file: options.file })
-}
+const imageUrl = computed({
+  get () {
+    return props.modelValue
+  },
+  set (val) {
+    emits('update:modelValue', val || '')
+  }
+})
+// const imageUrl = ref('')
 
 const handleAvatarSuccess: UploadProps['onSuccess'] = (
   response,
   uploadFile
 ) => {
-  imageUrl.value = URL.createObjectURL(uploadFile.raw!)
+  // console.log('response', response)
+  // imageUrl.value = URL.createObjectURL(uploadFile.raw!)
+  imageUrl.value = response.data.shortPath
 }
 
 const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
-  // if (rawFile.type !== 'image/jpeg') {
-  //   ElMessage.error('Avatar picture must be JPG format!')
-  //   return false
-  // } else 
-  if (props.maxSize && rawFile.size / 1024 / 1024 > props.maxSize) {
-    ElMessage.error('最大不超过 2MB!')
+  if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/jpg' && rawFile.type !== 'image/png') {
+    ElMessage.error('图片格式不支持!')
+    return false  
+  } else if (rawFile.size / 1024 / 1024 > 2) {
+    ElMessage.error('Avatar picture size can not exceed 2MB!')
     return false
   }
   return true
@@ -61,10 +69,9 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
 
 <style scoped>
 .avatar-uploader .avatar {
-  width: 100px;
-  height: 100px;
+  width: 178px;
+  height: 178px;
   display: block;
-  object-fit: cover;
 }
 </style>
 
@@ -85,8 +92,8 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
 .el-icon.avatar-uploader-icon {
   font-size: 28px;
   color: #8c939d;
-  width: 100px;
-  height: 100px;
+  width: 178px;
+  height: 178px;
   text-align: center;
 }
 </style>
\ No newline at end of file
Modified +9 -5
diff --git a/src/hook/useOptions.ts b/src/hook/useOptions.ts
index 7228ca4..36e38a1 100644
--- a/src/hook/useOptions.ts
+++ b/src/hook/useOptions.ts
@@ -2,24 +2,28 @@ import { storeToRefs } from "pinia";
 import { Ref, ref, toRef, toRefs, watch } from "vue";
 import { usePageApi } from "../api/page";
 import { useEnumOptions } from "../stores/enumOptions";
-import { OptionProps } from "../utils/optionProps";
+import { OptionEmits, OptionProps } from "../utils/optionProps";
 
-export default function useOptions (props: OptionProps) {
+export default function useOptions (props: OptionProps, emits: OptionEmits) {
   const enumOptions = useEnumOptions()
   // console.log('enumOptions', toRef(enumOptions.options, props.storeKey), props.storeKey)
   const myOptions = props.storeKey ? (toRef(enumOptions.options, props.storeKey) as Ref<EmptyObjectType[] | undefined>) : ref(props.options);
-  watch(() => props.options, (val) => {
+  watch(() => props.options, (val: EmptyArrayType) => {
     myOptions.value = val
   })
   if (props.storeKey) {
-    enumOptions.setOptions(props.storeKey)
+    enumOptions.setOptions(props.storeKey).then((res: EmptyObjectType) => {
+      emits('optionRequestAfter', myOptions)
+    })
   } else if (props.api) {
-    props.api().then(res => {
+    props.api().then((res: EmptyObjectType) => {
       myOptions.value = props.resultKey ? getByKey(res, props.resultKey) : res
+      emits('optionRequestAfter', myOptions)
     })
   } else if (props.url) {
     usePageApi().getTableData(props.url, { pageIndex: 0 }).then(res => {
       myOptions.value = props.resultKey ? getByKey(res, props.resultKey) : res
+      emits('optionRequestAfter', myOptions)
     })
   }
   return {
Renamed +10 -4
src/hook/usePageSetting.ts → src/hook/usePage.ts
diff --git a/src/hook/usePageSetting.ts b/src/hook/usePage.ts
similarity index 85%
rename from src/hook/usePageSetting.ts
rename to src/hook/usePage.ts
index 6f23cc2..94b61ca 100644
--- a/src/hook/usePageSetting.ts
+++ b/src/hook/usePage.ts
@@ -1,16 +1,18 @@
 import { provide, ref, Ref } from "vue";
 import { ColumnConfig } from "../components/form/model/form";
 import { TableColumn } from "../components/table/type";
-import { EventSettingProps } from "../components/page/model";
+import { PageHandle, UsePageProps } from "../components/page/model";
 import providePageKey from "../components/page/provide/key";
+import { getCurrentInstance } from "vue";
+import { reactive } from "vue";
 // 各区域配置项(响应式)
 // 各区域表单值(响应式)
 /**
  * 页面配置
- * @param newSettings 配置规则
+ * @param pageProps 配置规则
  * @returns  setting: 回调方法; columns: 配置; forms: 表单;
  */
-export default function usePageSetting (newSettings: EventSettingProps) {
+export default function usePage (props: UsePageProps) {
   const tableColumns = ref([]) as Ref<TableColumn[]>;
   const searchColumns = ref([]) as Ref<ColumnConfig[]>;
   const editColumns = ref([]) as Ref<ColumnConfig[]>;
@@ -19,8 +21,11 @@ export default function usePageSetting (newSettings: EventSettingProps) {
 
   const searchForm = ref<EmptyObjectType>({});
   const infoForm = ref<EmptyObjectType>({});
+
+  const handle = reactive({} as PageHandle)
   
   provide(providePageKey, {
+    pageProps: props,
     tableColumns,
     searchColumns,
     editColumns,
@@ -28,7 +33,7 @@ export default function usePageSetting (newSettings: EventSettingProps) {
     detailColumns,
     searchForm,
     infoForm,
-    newSettings
+    handle,
   });
 
   return {
@@ -41,5 +46,6 @@ export default function usePageSetting (newSettings: EventSettingProps) {
     // 表单相关
     searchForm,
     infoForm,
+    handle,
   }
 }
\ No newline at end of file
Modified +1 -1
diff --git a/src/layout/routerView/parent.vue b/src/layout/routerView/parent.vue
index 4124f7f..bca5f56 100644
--- a/src/layout/routerView/parent.vue
+++ b/src/layout/routerView/parent.vue
@@ -3,7 +3,7 @@
 		<router-view v-slot="{ Component }">
 			<transition :name="setTransitionName" mode="out-in">
 				<keep-alive :include="getKeepAliveNames">
-					<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
+					<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage"/>
 				</keep-alive>
 			</transition>
 		</router-view>
Modified +1 -1
diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts
index 586be93..91d0b99 100644
--- a/src/router/backEnd.ts
+++ b/src/router/backEnd.ts
@@ -219,7 +219,7 @@ export function backEndComponent(routes: Menu[]): Array<RouteRecordRaw> {
 			meta: {
 				title: item.displayName,
 				isLink: "",
-				isHide: false,
+				isHide: !item.visible,
 				isKeepAlive: true,
 				isAffix: false,
 				isIframe: item.newWindow,
Modified +4 -2
diff --git a/src/stores/enumOptions.ts b/src/stores/enumOptions.ts
index 1d80a47..6f7e0cd 100644
--- a/src/stores/enumOptions.ts
+++ b/src/stores/enumOptions.ts
@@ -19,14 +19,16 @@ export const useEnumOptions = defineStore('enumOptions', {
   }),
 	actions: {
 		async setOptions(type: string) {
-			if (!this[type]) {
+			if (!this.options[type]) {
         this[type] = [];
-        usePageApi().lookUp(type).then(res => {
+        return usePageApi().lookUp(type).then(res => {
           this.options[type] = res.data[toCamelCase(type)]
+          return this.options[type]
         }).catch(() => {
           this.options[type] = undefined
         })
       }
+      return Promise.resolve(this.options[type])
 		},
 	},
 });
Modified +3 -0
diff --git a/src/theme/element.scss b/src/theme/element.scss
index fa4e78b..9b04220 100644
--- a/src/theme/element.scss
+++ b/src/theme/element.scss
@@ -1,5 +1,8 @@
 @import 'mixins/index.scss';
 
+.el-upload--picture-card {
+	--el-upload-picture-card-size: 126px !important;
+}
 /* Button 按钮
 ------------------------------- */
 // 第三方字体图标大小
Added +8 -0
diff --git a/src/types/component.d.ts b/src/types/component.d.ts
new file mode 100644
index 0000000..5304973
--- /dev/null
+++ b/src/types/component.d.ts
@@ -0,0 +1,8 @@
+import Page from '../components/page/index.vue'
+
+// 声明全局组件
+declare module 'vue' {
+  export interface GlobalComponents {
+    Page: typeof Page
+  }
+}
Modified +1 -1
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
index cf808ab..129fe3a 100644
--- a/src/types/global.d.ts
+++ b/src/types/global.d.ts
@@ -110,4 +110,4 @@ declare interface TableType<T = any> {
 	};
 }
 
-type ReverseMap<T> = T[keyof T];
\ No newline at end of file
+type ReverseMap<T> = T[keyof T];
Modified +2 -0
diff --git a/src/types/views.d.ts b/src/types/views.d.ts
index ff2e68d..1de979d 100644
--- a/src/types/views.d.ts
+++ b/src/types/views.d.ts
@@ -291,6 +291,8 @@ declare type WorkflowDrawerState<T = any> = {
 declare type TableDemoPageType = {
 	pageIndex: number;
 	pageSize: number;
+	sort: string;
+	desc: boolean;
 };
 
 declare type TableSearchType = {
Modified +6 -0
diff --git a/src/utils/optionProps.ts b/src/utils/optionProps.ts
index abb70dd..74b3c89 100644
--- a/src/utils/optionProps.ts
+++ b/src/utils/optionProps.ts
@@ -1,4 +1,5 @@
 import type { PropType, ExtractPropTypes } from 'vue';
+import { Ref } from 'vue';
 export const optionProps = {
   options: {
     type: Object as PropType<Array<EmptyObjectType>>,
@@ -26,3 +27,8 @@ export const optionProps = {
   }
 };
 export type OptionProps = ExtractPropTypes<typeof optionProps >;
+
+export type OptionEmits = {
+  (e: 'optionRequestAfter', options: Ref<EmptyArrayType>): void;
+  (e: 'optionRequestBefore', options: Ref<EmptyArrayType>): void;
+}
Modified +78 -2
diff --git a/src/utils/other.ts b/src/utils/other.ts
index 2226114..5ad72d9 100644
--- a/src/utils/other.ts
+++ b/src/utils/other.ts
@@ -200,7 +200,7 @@ export function getComponentBaseField (item: Column) {
 		String: 'input',
 		Boolean: 'switch',
 		DateTime: 'datePicker',
-	}
+	}	
 	const contents = {
 		mail: 'input',
 		mobile: 'input',
@@ -258,7 +258,78 @@ export function deepMerge<T = any>(src: any = {}, ...targets: Array<any>): T {
   });
   return src;
 }
+export function decomposePowerOfTwo (num: number) {
+	let powers = [];
+	let n = 0;
+	while(num > 0) {
+		if ((num & 1) === 1) {
+			// 如果当前位是1,就添加对应的2的n次幂
+			powers.push(Math.pow(2, n));
+		}
+		num >>= 1; // 右移一位
+		n++;
+	}
+	return powers;
+}
+
+export function isObjTrue (prop?: boolean) {
+	return prop || (prop === undefined)
+}
+export function toSomeLevelJSON(obj: EmptyObjectType) {
+  const result = {};
 
+  for (const key in obj) {
+    const keys = key.split('.');
+    let currentLevel = result;
+
+    for (let i = 0; i < keys.length; i++) {
+      const subKey = keys[i];
+      if (!currentLevel[subKey]) {
+        if (i === keys.length - 1) {
+          currentLevel[subKey] = obj[key];
+        } else {
+          currentLevel[subKey] = {};
+        }
+      }
+      currentLevel = currentLevel[subKey];
+    }
+  }
+	
+  return result;
+}
+export function toOneLevelJSON(obj: EmptyObjectType, parentKey = '') {
+	const result = {};
+
+	for (const key in obj) {
+		const newKey = parentKey ? `${parentKey}.${key}` : key;
+		if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+			const nestedObj = toOneLevelJSON(obj[key], newKey);
+			Object.assign(result, nestedObj);
+		} else {
+			result[newKey] = obj[key];
+		}
+	}
+
+	return result;
+}
+export function assignJSON(target: EmptyObjectType, source: EmptyObjectType) {
+  for (const key in target) {
+    if (!(key in source)) {
+      delete target[key];
+    }
+  }
+
+  for (const key in source) {
+    if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
+      if (!target[key]) {
+        target[key] = {};
+      }
+      assignJSON(target[key], source[key]);
+    } else {
+      target[key] = source[key];
+    }
+  }
+}
 /**
  * 统一批量导出
  * @method elSvg 导出全局注册 element plus svg 图标
@@ -304,7 +375,12 @@ const other = {
 	},
 	deepMerge (src: any = {}, ...targets: Array<any>) {
 		return deepMerge(src, ...targets)
-	}
+	},
+	isObjTrue,
+	decomposePowerOfTwo,
+	toSomeLevelJSON,
+	toOneLevelJSON,
+	assignJSON
 };
 
 // 统一批量导出
Modified +96 -16
diff --git a/src/views/modules/admin/role.vue b/src/views/modules/admin/role.vue
index 9a083d4..0fe4b38 100644
--- a/src/views/modules/admin/role.vue
+++ b/src/views/modules/admin/role.vue
@@ -1,23 +1,103 @@
 <template>
-  <Page></Page>
+	<Page>
+		<template #permission>
+			<el-checkbox-group v-model="permission" class="w-full">
+				<el-table :data="menus" style="width: 100%; margin-bottom: 20px" row-key="id" size="small" border default-expand-all>
+					<el-table-column prop="name" label="标识" width="150" />
+					<el-table-column prop="displayName" label="菜单名" width="150" />
+					<el-table-column label="全部授权" width="70" align="center">
+						<template #default="scope">
+							<el-checkbox
+								@change="(val: boolean) => changeCheckboxAll(scope.row, val)"
+								:label="`${scope.row.id}#255`"
+								:load="loadCheckbox(scope.row)"
+								><span></span
+							></el-checkbox>
+						</template>
+					</el-table-column>
+					<el-table-column label="权限">
+						<template #default="scope">
+							<el-checkbox :label="`${scope.row.id}#${index}`" v-for="(item, index) in scope.row.permissions" :key="index">{{ item }}</el-checkbox>
+						</template>
+					</el-table-column>
+				</el-table>
+			</el-checkbox-group>
+		</template>
+	</Page>
 </template>
 
 <script setup lang="ts">
+import { computed, ref } from 'vue';
+import { useMenuApi } from '/@/api/menu';
 import { ColumnKind } from '/@/api/page';
-import usePageSetting from '/@/hook/usePageSetting';
+import usePage from '/@/hook/usePage';
 
-const { tableColumns } = usePageSetting({
-  columns: [
-    {
-      in: ColumnKind.ADD,
-      prop: 'sort',
-      component: 'select',
-      label: '33333',
-      props: {
-        options: [{id: 1, name: '1111'}]
-      }
-    }
-  ]
-})
-console.log('tableColumns', tableColumns)
+const { infoForm } = usePage({
+	columns: [
+		{
+			in: ColumnKind.ADD,
+			prop: 'sort',
+			component: 'select',
+			label: '33333',
+			props: {
+				options: [{ id: 1, name: '1111' }],
+			},
+		},
+		{
+			prop: 'permission',
+			slot: 'permission',
+			in: [ColumnKind.EDIT, ColumnKind.ADD],
+		},
+	],
+});
+
+const loadCheckbox = (row: EmptyObjectType) => {
+	if (!row.permissionsArr) {
+		row.permissionsArr = Object.keys(row.permissions).map((key) => `${row.id}#${key}`);
+	}
+};
+
+const permission = computed({
+	get() {
+		let value = infoForm.value.permission ? infoForm.value.permission.split(',') : [];
+		let menuStr = JSON.stringify(menus.value);
+		value.forEach((val: string) => {
+			let auth = val.split('#');
+			if (Number(auth[1]) === 255) {
+				let arr = menuStr.match(new RegExp(auth[0] + '#[0-9][0-9]*', 'g'));
+				value = Array.from(new Set(value.concat(arr)));
+			}
+		});
+		return value;
+	},
+	set(val) {
+		infoForm.value.permission = val.toString();
+	},
+});
+
+const menus = ref<EmptyObjectType[]>([]);
+useMenuApi()
+	.getMenu({ pageSize: 0 })
+	.then((res) => {
+		menus.value = res.data;
+	});
+
+const changeCheckboxAll = (row: EmptyObjectType, val: boolean) => {
+	if (val) {
+		permission.value = Array.from(new Set(permission.value.concat(row.permissionsArr)));
+		if (row.children) {
+			row.children.forEach((child: EmptyObjectType) => {
+				permission.value = Array.from(new Set(permission.value.concat([child.id + '#255'])));
+				changeCheckboxAll(child, true);
+			});
+		}
+	} else {
+		permission.value = permission.value.filter((val: string) => val.indexOf(row.id + '#') !== 0);
+		if (row.children) {
+			permission.value = permission.value.filter((val: string) =>
+				row.children.every((item: EmptyObjectType) => item.id !== Number(val.split('#')[0]))
+			);
+		}
+	}
+};
 </script>
Modified +26 -25
diff --git a/src/views/modules/admin/test.vue b/src/views/modules/admin/test.vue
index ef29ad6..0cd54cf 100644
--- a/src/views/modules/admin/test.vue
+++ b/src/views/modules/admin/test.vue
@@ -1,31 +1,32 @@
 <template>
-  <Page></Page>
+	<Page></Page>
 </template>
 
 <script setup lang="ts">
-import usePageSetting from '/@/hook/usePageSetting';
+import usePage from '/@/hook/usePage';
 
-usePageSetting({
-  columns: [
-    {
-      prop: 'enable',
-      component: 'radioGroup',
-      props: {
-        options: [
-          { id: 1, name: '开' }, { id: 2, name: '关' }
-        ]
-      }
-    },
-    {
-      prop: 'upload',
-      if: false
-    },
-    {
-      prop: 'checkbox',
-      props: {
-        label: '测试'
-      }
-    }
-  ]
-})
+usePage({
+	columns: [
+		{
+			prop: 'enable',
+			component: 'radioGroup',
+			props: {
+				options: [
+					{ id: 1, name: '开' },
+					{ id: 2, name: '关' },
+				],
+			},
+		},
+		{
+			prop: 'upload',
+			if: false,
+		},
+		{
+			prop: 'checkbox',
+			props: {
+				label: '测试',
+			},
+		},
+	],
+});
 </script>
Modified +60 -47
diff --git a/src/views/modules/admin/user/index.vue b/src/views/modules/admin/user/index.vue
index 0385e6a..ee475e5 100644
--- a/src/views/modules/admin/user/index.vue
+++ b/src/views/modules/admin/user/index.vue
@@ -1,53 +1,66 @@
 <template>
-  <Page>
-    <template #mail>
-      测试
-    </template>
-  </Page>
+	<Page>
+		<template #mail> 测试 </template>
+		<template #row-handle> </template>
+	</Page>
 </template>
 
 <script setup lang="ts">
-import usePageSetting from '/@/hook/usePageSetting'
+import usePage from '/@/hook/usePage';
 import { ColumnKind, usePageApi } from '/@/api/page';
-const { columns, forms } = usePageSetting({
-  columns: [
-    // {
-    //   in: ColumnKind.ADD,
-    //   prop: 'sex',
-    //   component: 'radioGroup',
-    //   props: {
-    //     options: [{ id: 1, name: '男' }, { id: 2, name: '女' }]
-    //   },
-    // },
-    // {
-    //   in: [ColumnKind.SEARCH, ColumnKind.LIST, ColumnKind.ADD],
-    //   prop: 'mail',
-    //   slot: 'mail',
-    // },
-    {
-      prop: 'departmentID',
-      component: 'select',
-      props: {
-        api: () => usePageApi().getTableData('/admin/department', { pageIndex: 0 })
-      }
-    },
-    {
-      prop: 'roleID',
-      component: 'select',
-      props: {
-        url: '/admin/role'
-      }
-    },
-    // {
-    //   in: [ColumnKind.ADD, ColumnKind.EDIT],
-    //   prop: 'name',
-    //   props: {
-    //     onChange: (val: string) => {
-    //       forms.data!.mail = val
-    //       columns.add!.find(item => item.prop === 'sex')!.if = !val
-    //     }
-    //   }
-    // }
-  ]
-})
+const { columns, forms } = usePage({
+	columns: [
+		// {
+		//   in: ColumnKind.ADD,
+		//   prop: 'sex',
+		//   component: 'radioGroup',
+		//   props: {
+		//     options: [{ id: 1, name: '男' }, { id: 2, name: '女' }]
+		//   },
+		// },
+		// {
+		//   in: [ColumnKind.SEARCH, ColumnKind.LIST, ColumnKind.ADD],
+		//   prop: 'mail',
+		//   slot: 'mail',
+		// },
+		{
+			prop: 'departmentID',
+			component: 'select',
+			props: {
+				api: () => usePageApi().getTableData('/admin/department', { pageIndex: 0 }),
+			},
+		},
+		{
+			prop: 'roleID',
+			component: 'select',
+			props: {
+				url: '/admin/role',
+			},
+		},
+		{
+			prop: 'avatar',
+			component: 'upload',
+			props: {
+				url: '/Projects/Work/UploadFileService',
+				data: {
+					directory: 'works'
+				}
+			}
+		}
+		// {
+		//   in: [ColumnKind.ADD, ColumnKind.EDIT],
+		//   prop: 'name',
+		//   props: {
+		//     onChange: (val: string) => {
+		//       forms.data!.mail = val
+		//       columns.add!.find(item => item.prop === 'sex')!.if = !val
+		//     }
+		//   }
+		// }
+	],
+	onEditBefore: (data, fun) => {
+		data.value.ceshi = 123;
+		fun();
+	},
+});
 </script>
Modified +1 -4
diff --git a/src/views/modules/index.vue b/src/views/modules/index.vue
index 11581ac..e7300a5 100644
--- a/src/views/modules/index.vue
+++ b/src/views/modules/index.vue
@@ -1,6 +1,3 @@
 <template>
-  <Page></Page>
+	<Page></Page>
 </template>
-
-<script setup lang="ts">
-</script>
Modified +1 -1
diff --git a/tsconfig.json b/tsconfig.json
index 98f1fdc..46c1575 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -49,7 +49,7 @@
 		} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */,
 		// "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
 		// "typeRoots": [],                       /* List of folders to include type definitions from. */
-		"types": ["vite/client"] /* Type declaration files to be included in compilation. */,
+		"types": ["vite/client", "element-plus/global"] /* Type declaration files to be included in compilation. */,
 		"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
 		"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
 		// "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
Modified +1 -2
diff --git a/vite.config.ts b/vite.config.ts
index efc09aa..7cacb8d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -29,8 +29,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
 			hmr: true,
 			proxy: {
 				'/base-api': {
-					// target: 'https://cube3.newlifex.com',
-					target: 'http://120.78.152.40:5000',
+					target: 'https://cube3.newlifex.com',
 					ws: true,
 					changeOrigin: true,
 					rewrite: (path) => path.replace(/^\/base-api/, ''),