vue更新3.5,以及mime打更新

This commit is contained in:
2024-09-06 10:48:22 +08:00
parent 9984041eec
commit 3914762c85
117 changed files with 4348 additions and 8000 deletions

271
cdTMP/package-lock.json generated
View File

@@ -25,7 +25,7 @@
"postcss-import": "^16.1.0", "postcss-import": "^16.1.0",
"qs": "^6.13.0", "qs": "^6.13.0",
"tinymce": "^7.3.0", "tinymce": "^7.3.0",
"vue": "3.4.38", "vue": "^3.5.2",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-color-kit": "^1.0.6", "vue-color-kit": "^1.0.6",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
@@ -34,7 +34,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.3", "@types/node": "^22.5.4",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qs": "^6.9.15", "@types/qs": "^6.9.15",
"@vitejs/plugin-vue": "^5.1.3", "@vitejs/plugin-vue": "^5.1.3",
@@ -42,17 +42,17 @@
"@vue/babel-plugin-jsx": "^1.1.1", "@vue/babel-plugin-jsx": "^1.1.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"browserslist": "^4.23.0", "browserslist": "^4.23.0",
"caniuse-lite": "^1.0.30001655", "caniuse-lite": "^1.0.30001657",
"eslint": "^9.9.1", "eslint": "^9.9.1",
"eslint-plugin-vue": "^9.28.0", "eslint-plugin-vue": "^9.28.0",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"postcss": "^8.4.44", "postcss": "^8.4.45",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.10", "tailwindcss": "^3.4.10",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"vite": "5.4.2" "vite": "^5.4.3"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@@ -1421,9 +1421,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.5.3", "version": "22.5.4",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-22.5.3.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.5.4.tgz",
"integrity": "sha512-njripolh85IA9SQGTAqbmnNZTdxv7X/4OYGPz8tgy5JDr8MP+uDBa921GpYEoDDnwm0Hmn5ZPeJgiiSTPoOzkQ==", "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1556,42 +1556,39 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.2.tgz",
"integrity": "sha512-ja7cpqAOfw4tyFAxgBz70Z42miNDeaqTxExTsnXDLomRpqfyCgyvZvFp482fmsElpfvsoMJUsvzULhvxUTW6Iw==", "integrity": "sha512-1aP7FL2GkqfcskHWGg3lfWQpJnrmewKc+rNJ/hq9WNaAw4BEyJ5QbNChnqmbw+tJ409zdy1XWmUeXXMrCKJcQQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/shared": "3.5.0", "@vue/shared": "3.5.2",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.2.tgz",
"integrity": "sha512-xYjUybWZXl+1R/toDy815i4PbeehL2hThiSGkcpmIOCy2HoYyeeC/gAWK/Y/xsoK+GSw198/T5O31bYuQx5uvQ==", "integrity": "sha512-QY4DpT8ZIUyu/ZA5gErpSEDocGNEbHmpkZIC/d5jbp/rUF0iOJNigAy3HCCKc0PMMhDlrcysO3ufQ6Ab4MpEcQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.0", "@vue/compiler-core": "3.5.2",
"@vue/shared": "3.5.0" "@vue/shared": "3.5.2"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.2.tgz",
"integrity": "sha512-B9DgLtrqok2GLuaFjLlSL15ZG3ZDBiitUH1ecex9guh/ZcA5MCdwuVE6nsfQxktuZY/QY0awJ35/ripIviCQTQ==", "integrity": "sha512-vErEtybSU290LbMW+ChYllI9tNJEdTW1oU+8cZWINZyjlWeTSa9YqDl4/pZJSnozOI+HmcaC1Vz2eFKmXNSXZA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.0", "@vue/compiler-core": "3.5.2",
"@vue/compiler-dom": "3.5.0", "@vue/compiler-dom": "3.5.2",
"@vue/compiler-ssr": "3.5.0", "@vue/compiler-ssr": "3.5.2",
"@vue/shared": "3.5.0", "@vue/shared": "3.5.2",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.11", "magic-string": "^0.30.11",
"postcss": "^8.4.44", "postcss": "^8.4.44",
@@ -1599,14 +1596,13 @@
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.2.tgz",
"integrity": "sha512-E263QZmA1dqRd7c3u/sWTLRMpQOT0aZ8av/L9SoD/v/BVMZaWFHPUUBswS+bzrfvG2suJF8vSLKx6k6ba5SUdA==", "integrity": "sha512-vMtA4tQK/AM3UAYJsmouQzQpgG+h9TKiD5BV+Zt+ZyAMdicxzSEEFGWf/CykRnDpqj9fMfIHPhOezJVNxiXe2A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.0", "@vue/compiler-dom": "3.5.2",
"@vue/shared": "3.5.0" "@vue/shared": "3.5.2"
} }
}, },
"node_modules/@vue/devtools-api": { "node_modules/@vue/devtools-api": {
@@ -1616,122 +1612,53 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.2.tgz",
"integrity": "sha512-Ew3F5riP3B3ZDGjD3ZKb9uZylTTPSqt8hAf4sGbvbjrjDjrFb3Jm15Tk1/w7WwTE5GbQ2Qhwxx1moc9hr8A/OQ==", "integrity": "sha512-lJwWL5bNht+2vIwU/+lnGdH+FKFxzz6z8WkoIJityPLiasWU+HDUvEsC7gm3JFwbTf7Kk+Nr9kJMaPy0HXwwxQ==",
"license": "MIT", "license": "MIT",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"@vue/shared": "3.5.0" "@vue/shared": "3.5.2"
} }
}, },
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.2.tgz",
"integrity": "sha512-mQyW0F9FaNRdt8ghkAs+BMG3iQ7LGgWKOpkzUzR5AI5swPNydHGL5hvVTqFaeMzwecF1g0c86H4yFQsSxJhH1w==", "integrity": "sha512-oU+i9sJjGEMfEhlrJ7SZv7CdSIgUNyBHnWHa0SqU2RF48V3/ATajzpWq1/DkiVJ1mtx+cQFAMKs8s/3cB3YlLQ==",
"license": "MIT", "license": "MIT",
"optional": true,
"peer": true,
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.0", "@vue/reactivity": "3.5.2",
"@vue/shared": "3.5.0" "@vue/shared": "3.5.2"
} }
}, },
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.4.38", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.38.tgz", "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.2.tgz",
"integrity": "sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==", "integrity": "sha512-2qvysn+oR0QnFKaWZxQ90iVpWAK/WPpYmODHCv24IDXjsBrdHbjLBj9s6YBdPaMuQhs0LNsmhsgZYZBkszLg6g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.4.38", "@vue/reactivity": "3.5.2",
"@vue/runtime-core": "3.4.38", "@vue/runtime-core": "3.5.2",
"@vue/shared": "3.4.38", "@vue/shared": "3.5.2",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
"node_modules/@vue/runtime-dom/node_modules/@vue/reactivity": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.38.tgz",
"integrity": "sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.4.38"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/runtime-core": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.38.tgz",
"integrity": "sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.4.38",
"@vue/shared": "3.4.38"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.38.tgz",
"integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==",
"license": "MIT"
},
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.4.38", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.38.tgz", "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.2.tgz",
"integrity": "sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==", "integrity": "sha512-3POhYCA8KfbmuDuUiNbMXnpdh9pwE4SvAqo7VvACjklLkf3AaMkY3TvV7APeEa/WQezrnL+E4X2ASpJsKeS4cQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.4.38", "@vue/compiler-ssr": "3.5.2",
"@vue/shared": "3.4.38" "@vue/shared": "3.5.2"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "3.4.38" "vue": "3.5.2"
} }
}, },
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-core": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.38.tgz",
"integrity": "sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.24.7",
"@vue/shared": "3.4.38",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-dom": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.38.tgz",
"integrity": "sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.4.38",
"@vue/shared": "3.4.38"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/compiler-ssr": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.38.tgz",
"integrity": "sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.4.38",
"@vue/shared": "3.4.38"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.38.tgz",
"integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==",
"license": "MIT"
},
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.5.0", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.0.tgz", "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.2.tgz",
"integrity": "sha512-m9IgiteBpCkFaMNwCOBkFksA7z8QiKc30ooRuoXWUFRDu0mGyNPlFHmbncF0/Kra1RlX8QrmBbRaIxVvikaR0Q==", "integrity": "sha512-Ce89WNFBzcDca/AgFTxgX4/K4iAyF7oFIp8Z5aBbFBNbtpwnQr+5pZOoHndxnjE2h+YFcipVMzs9UL11XB6dwA==",
"devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vueuse/core": { "node_modules/@vueuse/core": {
@@ -2339,9 +2266,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001655", "version": "1.0.30001657",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz",
"integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4535,9 +4462,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.44", "version": "8.4.45",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.44.tgz", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.45.tgz",
"integrity": "sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==", "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -5502,14 +5429,14 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.2", "version": "5.4.3",
"resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.2.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.3.tgz",
"integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.41", "postcss": "^8.4.43",
"rollup": "^4.20.0" "rollup": "^4.20.0"
}, },
"bin": { "bin": {
@@ -5562,16 +5489,16 @@
} }
}, },
"node_modules/vue": { "node_modules/vue": {
"version": "3.4.38", "version": "3.5.2",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.4.38.tgz", "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.2.tgz",
"integrity": "sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==", "integrity": "sha512-w1YB4lAwC9ByH6AnFY0JvZF+y70Usul9jDfKIKtM5xA97q/JPS5R7mqq0fhA6D2PQxYPZdgb5jzFKLyOga5pnw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.4.38", "@vue/compiler-dom": "3.5.2",
"@vue/compiler-sfc": "3.4.38", "@vue/compiler-sfc": "3.5.2",
"@vue/runtime-dom": "3.4.38", "@vue/runtime-dom": "3.5.2",
"@vue/server-renderer": "3.4.38", "@vue/server-renderer": "3.5.2",
"@vue/shared": "3.4.38" "@vue/shared": "3.5.2"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"
@@ -5691,62 +5618,6 @@
"vue": "^3.2.0" "vue": "^3.2.0"
} }
}, },
"node_modules/vue/node_modules/@vue/compiler-core": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.38.tgz",
"integrity": "sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.24.7",
"@vue/shared": "3.4.38",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/vue/node_modules/@vue/compiler-dom": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.38.tgz",
"integrity": "sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.4.38",
"@vue/shared": "3.4.38"
}
},
"node_modules/vue/node_modules/@vue/compiler-sfc": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.38.tgz",
"integrity": "sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.24.7",
"@vue/compiler-core": "3.4.38",
"@vue/compiler-dom": "3.4.38",
"@vue/compiler-ssr": "3.4.38",
"@vue/shared": "3.4.38",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.10",
"postcss": "^8.4.40",
"source-map-js": "^1.2.0"
}
},
"node_modules/vue/node_modules/@vue/compiler-ssr": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.38.tgz",
"integrity": "sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.4.38",
"@vue/shared": "3.4.38"
}
},
"node_modules/vue/node_modules/@vue/shared": {
"version": "3.4.38",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.38.tgz",
"integrity": "sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==",
"license": "MIT"
},
"node_modules/vuedraggable": { "node_modules/vuedraggable": {
"version": "2.24.3", "version": "2.24.3",
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-2.24.3.tgz", "resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-2.24.3.tgz",

View File

@@ -28,16 +28,16 @@
"postcss-import": "^16.1.0", "postcss-import": "^16.1.0",
"qs": "^6.13.0", "qs": "^6.13.0",
"tinymce": "^7.3.0", "tinymce": "^7.3.0",
"vue": "^3.5.2",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-color-kit": "^1.0.6", "vue-color-kit": "^1.0.6",
"vue-echarts": "^7.0.3", "vue-echarts": "^7.0.3",
"vue-router": "^4.4.3", "vue-router": "^4.4.3",
"vuedraggable": "^2.24.3", "vuedraggable": "^2.24.3"
"vue": "3.4.38"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^22.5.3", "@types/node": "^22.5.4",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qs": "^6.9.15", "@types/qs": "^6.9.15",
"@vitejs/plugin-vue": "^5.1.3", "@vitejs/plugin-vue": "^5.1.3",
@@ -45,16 +45,16 @@
"@vue/babel-plugin-jsx": "^1.1.1", "@vue/babel-plugin-jsx": "^1.1.1",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"browserslist": "^4.23.0", "browserslist": "^4.23.0",
"caniuse-lite": "^1.0.30001655", "caniuse-lite": "^1.0.30001657",
"eslint": "^9.9.1", "eslint": "^9.9.1",
"eslint-plugin-vue": "^9.28.0", "eslint-plugin-vue": "^9.28.0",
"less": "^4.2.0", "less": "^4.2.0",
"less-loader": "^12.2.0", "less-loader": "^12.2.0",
"postcss": "^8.4.44", "postcss": "^8.4.45",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"tailwindcss": "^3.4.10", "tailwindcss": "^3.4.10",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"vite": "5.4.2" "vite": "^5.4.3"
} }
} }

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 1024 1024" class="icon" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"> <svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<v-charts v-if="renderChart" :option="options" :autoresize="autoresize" :style="{ width, height }" /> <v-charts v-if="renderChart" :option="options" :autoresize="autoresize" :style="{ width, height }" />
</template> </template>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<a-cascader <a-cascader
v-if="props.type === 'cascader'" v-if="props.type === 'cascader'"
@@ -70,7 +74,7 @@
<script setup> <script setup>
import jsonData from "./lib/city.json" import jsonData from "./lib/city.json"
import { ref, watch } from "vue" import { ref, watch } from "vue"
import { isObject } from "lodash" import { isObject } from "lodash-es"
const val = ref() const val = ref()
const selectData = ref({ province: [], city: [], area: [] }) const selectData = ref({ province: [], city: [], area: [] })
@@ -139,7 +143,7 @@ val.value = props.modelValue
watch( watch(
() => props.modelValue, () => props.modelValue,
(vl) => () => { (vl) => {
val.value = vl val.value = vl
setSelectData() setSelectData()
}, },

File diff suppressed because one or more lines are too long

View File

@@ -22,19 +22,28 @@
</a-input-group> </a-input-group>
</template> </template>
<script setup> <script setup>
import { ref, watch, reactive } from "vue" import { reactive, computed } from "vue"
import { ColorPicker } from "vue-color-kit" import { ColorPicker } from "vue-color-kit"
import "vue-color-kit/dist/vue-color-kit.css" import "vue-color-kit/dist/vue-color-kit.css"
import { generate, getRgbStr } from "@arco-design/color"
import useClipboard from "vue-clipboard3" import useClipboard from "vue-clipboard3"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
const val = ref()
const props = defineProps({ const props = defineProps({
modelValue: String, modelValue: String,
placeholder: { type: String, default: "请选择颜色" } placeholder: { type: String, default: "请选择颜色" }
}) })
const emit = defineEmits(["update:modelValue"])
const val = computed({
get() {
return props.modelValue
},
set(newVal) {
emit("update:modelValue", newVal)
}
})
const selectColor = (color) => { const selectColor = (color) => {
val.value = color.hex val.value = color.hex
} }
@@ -66,11 +75,4 @@ const defaultColorList = reactive([
"#86909c", "#86909c",
"#6d4c41" "#6d4c41"
]) ])
const emit = defineEmits(["update:modelValue"])
watch(
() => val.value,
(vl) => emit("update:modelValue", vl)
)
</script> </script>

View File

@@ -1,9 +1,10 @@
<template> <template>
<template v-for="row in props.columns" :key="row[options.pk]"> <template v-for="row in props.columns" :key="row[options.pk]">
<template v-if="!row.hide"> <template v-if="isFunction(row.hide) ? row.hide() : !row.hide">
<a-table-column <a-table-column
:title="row.title" :title="row.title"
:width="row.width" :width="row.width"
:min-width="row.minWidth"
:ellipsis="row.ellipsis ?? true" :ellipsis="row.ellipsis ?? true"
:filterable="row.filterable" :filterable="row.filterable"
:cell-class="row.cellClass" :cell-class="row.cellClass"
@@ -38,6 +39,7 @@
:title="row.title" :title="row.title"
:data-index="row.dataIndex" :data-index="row.dataIndex"
:width="row.width" :width="row.width"
:min-width="row.minWidth"
:ellipsis="row.ellipsis ?? true" :ellipsis="row.ellipsis ?? true"
:filterable="row.filterable" :filterable="row.filterable"
:cell-class="row.cellClass" :cell-class="row.cellClass"
@@ -48,12 +50,15 @@
:header-cell-style="row.headerCellStyle" :header-cell-style="row.headerCellStyle"
:body-cell-style="row.bodyCellStyle" :body-cell-style="row.bodyCellStyle"
:summary-cell-style="row.summaryCellStyle" :summary-cell-style="row.summaryCellStyle"
:tooltip="row.dataIndex === '__operation' ? false : row.tooltip ?? true" :tooltip="row.dataIndex === '__operation' ? false : (row.tooltip ?? true)"
:align="row.align || 'left'" :align="row.align || 'left'"
:fixed="row.fixed" :fixed="row.fixed"
:sortable="row.sortable" :sortable="row.sortable"
v-else v-else
> >
<template #title>
<slot :name="`tableTitle-${row.dataIndex}`" v-bind="{ column: row }">{{ row.title }}</slot>
</template>
<template #cell="{ record, column, rowIndex }"> <template #cell="{ record, column, rowIndex }">
<!-- 操作栏 --> <!-- 操作栏 -->
<template v-if="row.dataIndex === '__operation'"> <template v-if="row.dataIndex === '__operation'">
@@ -61,22 +66,41 @@
<a-space size="mini"> <a-space size="mini">
<slot name="operationBeforeExtend" v-bind="{ record, column, rowIndex }"></slot> <slot name="operationBeforeExtend" v-bind="{ record, column, rowIndex }"></slot>
<slot name="operationCell" v-bind="{ record, column, rowIndex }"> <slot name="operationCell" v-bind="{ record, column, rowIndex }">
<!-- <a-link <a-link
v-if=" v-if="
options.see.show (isFunction(options.see.show)
&& ($common.auth(options.see.auth || []) ? options.see.show(record)
|| (options.see.role || [])) : options.see.show) && !props.isRecovery
" "
type="primary" v-auth="options.see.auth || []"
><icon-eye /> {{ options.see.text || '查看' }}</a-link> --> v-role="options.see.role || []"
type="primary"
:status="options.see.status || 'success'"
:style="{ color: options.see.color || '' }"
:disabled="
isFunction(options.see.disabled)
? options.see.disabled(record)
: options.see.disabled
"
@click="seeAction(record)"
><icon-eye /> {{ options.see.text || "查看" }}</a-link
>
<a-link <a-link
v-if=" v-if="
(isFunction(options.edit.show) (isFunction(options.edit.show)
? options.edit.show(record) ? options.edit.show(record)
: options.edit.show) && !props.isRecovery : options.edit.show) && !props.isRecovery
" "
v-auth="options.edit.auth || []"
v-role="options.edit.role || []"
type="primary" type="primary"
:status="options.edit.status || 'normal'"
:style="{ color: options.edit.color || '' }"
:disabled="
isFunction(options.edit.disabled)
? options.edit.disabled(record)
: options.edit.disabled
"
@click="editAction(record)" @click="editAction(record)"
> >
<icon-edit /> {{ options.edit.text || "编辑" }} <icon-edit /> {{ options.edit.text || "编辑" }}
@@ -91,6 +115,8 @@
? options.recovery.show(record) ? options.recovery.show(record)
: options.recovery.show) && props.isRecovery : options.recovery.show) && props.isRecovery
" "
v-auth="options.recovery.auth || []"
v-role="options.recovery.role || []"
> >
<a-link type="primary" <a-link type="primary"
><icon-undo /> {{ options.recovery.text || "恢复" }} ><icon-undo /> {{ options.recovery.text || "恢复" }}
@@ -107,7 +133,18 @@
: options.delete.show : options.delete.show
" "
> >
<a-link type="primary"> <a-link
type="primary"
:status="options.delete.status || 'danger'"
:style="{ color: options.delete.color || '' }"
v-auth="options.delete.auth || []"
v-role="options.delete.role || []"
:disabled="
isFunction(options.delete.disabled)
? options.delete.disabled(record)
: options.delete.disabled
"
>
<icon-delete /> <icon-delete />
{{ {{
props.isRecovery props.isRecovery
@@ -131,28 +168,66 @@
</template> </template>
<slot :name="row.dataIndex" v-bind="{ record, column, rowIndex }" v-else> <slot :name="row.dataIndex" v-bind="{ record, column, rowIndex }" v-else>
<template v-if="row.dataIndex === '__index'">{{ getIndex(rowIndex) }}</template> <template v-if="row.dataIndex === '__index'">{{ getIndex(rowIndex) }}</template>
<span :class="['relative', row?.quickEdit && allowQuickEdit(row.formType) ? 'flex' : '']">
<template v-if="row.dict && row.dict.translation"> <template v-if="row.isEdit">
<template v-if="isArray(get(record, row.dataIndex))"> <a-form>
<a-tag v-for="item in get(record, row.dataIndex)" class="ml-1">{{ <a-button-group class="flex">
getDataIndex(row, item) <a-input
}}</a-tag> v-if="row?.formType === undefined || row.formType === 'input'"
v-bind="row"
v-model="record[row.dataIndex]"
/>
<a-input-number
v-if="row.formType === 'input-number'"
v-bind="row"
v-model="record[row.dataIndex]"
/>
<a-select
v-if="['select', 'radio'].includes(row.formType)"
v-bind="row"
v-model="record[row.dataIndex]"
:options="dicts[row.dataIndex]"
/>
<a-link
class="block"
v-if="row?.quickEdit && allowQuickEdit(row.formType) && row.isEdit === true"
@click="updateQuickEditData(row, record)"
>
<icon-check />
</a-link>
</a-button-group>
</a-form>
</template> </template>
<a-tag v-else-if="row.dict.tagColors" :color="getTagColor(row, record)"> <template v-else-if="row.dict && row.dict.translation">
{{ getDataIndex(row, record) }} <template v-if="isArray(get(record, row.dataIndex))">
</a-tag> <a-tag v-for="item in get(record, row.dataIndex)" class="ml-1">{{
<a-tag v-else-if="row.dict.tagColor" :color="row.dict.tagColor">{{ getDataIndex(row, item)
getDataIndex(row, record) }}</a-tag>
}}</a-tag> </template>
<span v-else>{{ getDataIndex(row, record) }}</span> <a-tag v-else-if="row.dict.tagColors" :color="getTagColor(row, record)">
</template> {{ getDataIndex(row, record) }}
<template v-else-if="row.dataIndex && row.dataIndex.indexOf('.') !== -1"> </a-tag>
{{ get(record, row.dataIndex) }} <a-tag v-else-if="row.dict.tagColor" :color="row.dict.tagColor">{{
</template> getDataIndex(row, record)
<template v-else-if="row.formType === 'upload'"> }}</a-tag>
<a-link @click="imageSee(row, record, row.dataIndex)"><icon-image /> 查看图片</a-link> <span v-else>{{ getDataIndex(row, record) }}</span>
</template> </template>
<template v-else>{{ record[row.dataIndex] }}</template> <template v-else-if="row.dataIndex && row.dataIndex.indexOf('.') !== -1">
{{ get(record, row.dataIndex) }}
</template>
<template v-else-if="row.formType === 'upload'">
<a-link @click="imageSee(row, record, row.dataIndex)"><icon-image /> 查看图片</a-link>
</template>
<template v-else>{{ record[row.dataIndex] }}</template>
<a-link
class="absolute top-1 right-0"
v-if="row?.quickEdit && allowQuickEdit(row.formType) && !row.isEdit"
@click="quickEdit(row, record)"
>
<icon-edit />
</a-link>
</span>
</slot> </slot>
</template> </template>
</a-table-column> </a-table-column>
@@ -161,7 +236,7 @@
</template> </template>
<script setup> <script setup>
import { inject } from "vue" import { inject, ref, provide } from "vue"
import config from "@/config/crud" import config from "@/config/crud"
import uploadConfig from "@/config/upload" import uploadConfig from "@/config/upload"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
@@ -170,15 +245,17 @@ import CustomRender from "../js/custom-render"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import commonApi from "@/api/common" import commonApi from "@/api/common"
import formInput from "@cps/ma-form/formItem/form-input.vue"
const emit = defineEmits(["refresh", "showImage"]) const emit = defineEmits(["refresh", "showImage"])
const props = defineProps({ const props = defineProps({
columns: Array, columns: Array,
isRecovery: Boolean, isRecovery: Boolean,
crudFormRef: Object crudFormRef: Object
}) })
const options = inject("options") const options = inject("options")
const requestParams = inject("requestParams") const requestParams = inject("requestParams")
const dicts = inject("dicts")
const dictTrans = inject("dictTrans") const dictTrans = inject("dictTrans")
const dictColors = inject("dictColors") const dictColors = inject("dictColors")
@@ -189,29 +266,15 @@ const imageSee = async (row, record, dataIndex) => {
return return
} }
if (!["id", "hash"].includes(row.returnType)) { if (row.returnType === "hash") {
Message.info("该图片无法查看") emit("showImage", tool.showFile(record[dataIndex]))
return
}
Message.info("获取图片中,请稍等...")
const res =
row.returnType === "id"
? await commonApi.getFileInfoById({ id: record.id })
: await commonApi.getFileInfoByHash({ hash: record.hash })
const result = res?.success ?? false
if (!result) {
Message.info("图片信息无法获取")
return return
} }
const isImage = res.data.mime_type.indexOf("image") > -1 if (row.returnType === "id") {
result && Message.info("该图片无法查看")
emit( return
"showImage", }
isImage
? tool.attachUrl(res.data.url, uploadConfig.storageMode[res.data.storage_mode])
: "not-image.png"
)
} else { } else {
if (!record[row.dataIndex]) { if (!record[row.dataIndex]) {
Message.info("无图片") Message.info("无图片")
@@ -248,8 +311,21 @@ const getIndex = (rowIndex) => {
} }
} }
const seeAction = (record) => {
if (isFunction(options.beforeOpenSee) && !options.beforeOpenSee(record)) {
return false
}
if (options.see.action && isFunction(options.see.action)) {
options.see.action(record)
} else {
props.crudFormRef.see(record)
}
}
const editAction = (record) => { const editAction = (record) => {
isFunction(options.beforeOpenEdit) && options.beforeOpenEdit(record) if (isFunction(options.beforeOpenEdit) && !options.beforeOpenEdit(record)) {
return false
}
if (options.edit.action && isFunction(options.edit.action)) { if (options.edit.action && isFunction(options.edit.action)) {
options.edit.action(record) options.edit.action(record)
} else { } else {
@@ -257,6 +333,25 @@ const editAction = (record) => {
} }
} }
const allowQuickEdit = (formType) => ["select", "input", "input-number", "radio"].includes(formType ?? "input")
const quickEdit = (row, record) => {
if (row.formType === "select" || row.formType === "radio") {
record[row.dataIndex] = record[row.dataIndex].toString()
}
row.isEdit = true
}
const updateQuickEditData = async (row, record) => {
if (isFunction(options.beforeEdit) && !(await options.beforeEdit(record))) {
return false
}
const response = await options.edit.api(record[options.pk], record)
row.isEdit = false
isFunction(options.afterEdit) && (await options.afterEdit(response, record))
Message.success(response.message || `修改成功!`)
}
const recoveryAction = async (record) => { const recoveryAction = async (record) => {
const response = await options.recovery.api({ ids: [record[options.pk]] }) const response = await options.recovery.api({ ids: [record[options.pk]] })
response.success && Message.success(response.message || `恢复成功!`) response.success && Message.success(response.message || `恢复成功!`)

View File

@@ -46,7 +46,7 @@
<script setup> <script setup>
import { ref, inject, watch, nextTick } from "vue" import { ref, inject, watch, nextTick } from "vue"
import checkAuth from "@/directives/auth/auth" import checkAuth from "@/directives/auth/auth"
import { isArray } from "lodash" import { isArray } from "lodash-es"
const left = ref(0) const left = ref(0)
const top = ref(0) const top = ref(0)
@@ -100,7 +100,11 @@ const openContextMenu = async (ev, record) => {
currentRow.value = record currentRow.value = record
await nextTick(() => { await nextTick(() => {
const domHeight = document.querySelector(".ma-crud-contextmenu").offsetHeight const domHeight = document.querySelector(".ma-crud-contextmenu").offsetHeight
top.value = ev.clientY - domHeight if (document.body.offsetHeight - ev.pageY < domHeight) {
top.value = ev.clientY - domHeight
} else {
top.value = ev.clientY
}
left.value = ev.clientX left.value = ev.clientX
}) })
} }

View File

@@ -1,5 +1,6 @@
<template> <template>
<!-- addon-before-cancel --> <!-- 修改源码添加mask-closable属性 -->
<!-- 修改源码添加on-before-cancel属性 -->
<component <component
:is="componentName" :is="componentName"
v-model:visible="dataVisible" v-model:visible="dataVisible"
@@ -16,7 +17,7 @@
> >
<template #title>{{ actionTitle }}</template> <template #title>{{ actionTitle }}</template>
<a-spin :loading="dataLoading" tip="加载中..." class="w-full"> <a-spin :loading="dataLoading" tip="加载中..." class="w-full">
<ma-form v-model="form" :columns="formColumns" :options="{ showButtons: false }" ref="maFormRef"> <ma-form v-model="form" :columns="formColumns" :options="formOptions" ref="maFormRef">
<template v-for="slot in Object.keys($slots)" #[slot]="component"> <template v-for="slot in Object.keys($slots)" #[slot]="component">
<slot :name="slot" v-bind="component" /> <slot :name="slot" v-bind="component" />
</template> </template>
@@ -26,43 +27,53 @@
</template> </template>
<script setup> <script setup>
import { ref, toRaw, getCurrentInstance, inject, provide } from "vue" import { ref, toRaw, inject, provide, nextTick } from "vue" // 这里多了nextTick
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import { containerItems } from "@cps/ma-form/js/utils" import { containerItems } from "@cps/ma-form/js/utils"
import { isArray, isFunction, get, cloneDeep, isUndefined } from "lodash" import { isArray, isFunction, get, cloneDeep, isUndefined } from "lodash-es"
import { useRouter } from "vue-router" import { useRouter } from "vue-router"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { useFormStore } from "@/store/index" import { useFormStore } from "@/store/index"
const formStore = useFormStore() const formStore = useFormStore()
const router = useRouter() const router = useRouter()
const app = getCurrentInstance().appContext.app const formOptions = ref({ showButtons: false })
const maFormRef = ref() const maFormRef = ref()
const componentName = ref("a-modal") const componentName = ref("a-modal")
const columns = inject("columns")
const layoutColumns = ref(new Map()) const layoutColumns = ref(new Map())
const options = inject("options")
const formColumns = ref([]) const formColumns = ref([])
const currentAction = ref("") const currentAction = ref("")
const dataVisible = ref(false) const dataVisible = ref(false)
const form = ref({}) const form = ref({})
const actionTitle = ref("") const actionTitle = ref("")
const dataLoading = ref(true) const dataLoading = ref(true)
const columns = inject("columns")
const options = inject("options")
// 修改源码新增事件beforeCancel和处理函数beforeCancel
const emit = defineEmits(["success", "error", "beforeCancel"]) const emit = defineEmits(["success", "error", "beforeCancel"])
const beforeCancel = () => {
emit("beforeCancel")
return true
}
provide("form", toRaw(form)) provide("form", toRaw(form))
if (window.screen.width < 768) {
options.formOption.width = window.screen.width
options.formOption.isFull = true
}
const submit = async () => { const submit = async () => {
const formData = maFormRef.value.getFormData() const formData = maFormRef.value.getFormData()
if (await maFormRef.value.validateForm()) { if (await maFormRef.value.validateForm()) {
return false return false
} }
let response let response
// 在这里添加我们自定义的parameters注意判断options中是否有parameters-key
if (currentAction.value === "add") { if (currentAction.value === "add") {
isFunction(options.beforeAdd) && (await options.beforeAdd(formData)) if (isFunction(options.beforeAdd) && !(await options.beforeAdd(formData))) {
// 首先判断是否options.parameters存在 return false
}
// 修改源码添加parameters参数
if (!options.parameters) { if (!options.parameters) {
response = await options.add.api(formData) response = await options.add.api(formData)
} else { } else {
@@ -70,8 +81,9 @@ const submit = async () => {
} }
isFunction(options.afterAdd) && (await options.afterAdd(response, formData)) isFunction(options.afterAdd) && (await options.afterAdd(response, formData))
} else { } else {
isFunction(options.beforeEdit) && (await options.beforeEdit(formData)) if (isFunction(options.beforeEdit) && !(await options.beforeEdit(formData))) {
// 编辑也需要更新 return false
}
if (!options.parameters) { if (!options.parameters) {
response = await options.edit.api(formData[options.pk], formData) response = await options.edit.api(formData[options.pk], formData)
} else { } else {
@@ -84,6 +96,7 @@ const submit = async () => {
emit("success", response) emit("success", response)
return true return true
} else if (response.success === false && (typeof response.code === "undefined" || response.code !== 200)) { } else if (response.success === false && (typeof response.code === "undefined" || response.code !== 200)) {
Message.clear()
Message.error(response.message || `${actionTitle.value}失败!`) Message.error(response.message || `${actionTitle.value}失败!`)
return false return false
} }
@@ -92,7 +105,7 @@ const open = () => {
formColumns.value = [] formColumns.value = []
layoutColumns.value = new Map() layoutColumns.value = new Map()
init() init()
if (options.formOption.viewType === "tag") { if (options.formOption.viewType === "tag" && currentAction.value !== "see") {
if (!options.formOption.tagId) { if (!options.formOption.tagId) {
Message.info("未配置 tagId") Message.info("未配置 tagId")
return return
@@ -103,6 +116,7 @@ const open = () => {
} }
const config = { const config = {
options, options,
sourceColumns: columns.value,
formColumns: formColumns.value formColumns: formColumns.value
} }
@@ -128,7 +142,6 @@ const open = () => {
} else { } else {
formStore.formList[options.formOption.tagId].editData[queryParams.key] = cloneDeep(form.value) formStore.formList[options.formOption.tagId].editData[queryParams.key] = cloneDeep(form.value)
} }
form.value = {} form.value = {}
router.push(`/openForm/${options.formOption.tagId}` + tool.httpBuild(queryParams, true)) router.push(`/openForm/${options.formOption.tagId}` + tool.httpBuild(queryParams, true))
} else { } else {
@@ -136,17 +149,13 @@ const open = () => {
dataVisible.value = true dataVisible.value = true
} }
} }
// ~~~~addMethod~~~~
const beforeCancel = () => {
emit("beforeCancel")
return true
}
const close = () => { const close = () => {
dataVisible.value = false dataVisible.value = false
formColumns.value = [] formColumns.value = []
form.value = {} form.value = {}
} }
const add = () => { const add = async () => {
// 修改源码:开始
const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"] const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"]
if (!actionTitle.value) { if (!actionTitle.value) {
actionTitle.value = "新增" actionTitle.value = "新增"
@@ -155,11 +164,15 @@ const add = () => {
} else { } else {
actionTitle.value += "新增" actionTitle.value += "新增"
} }
// 修改源码:结束
currentAction.value = "add" currentAction.value = "add"
formOptions.value["disabled"] = false
form.value = {} form.value = {}
open() open()
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
} }
const edit = (data) => { const edit = async (data) => {
// 修改源码:开始
const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"] const strArr = ["新增编辑", "编辑新增", "新增新增", "编辑编辑", "编辑", "新增"]
if (!actionTitle.value) { if (!actionTitle.value) {
actionTitle.value = "编辑" actionTitle.value = "编辑"
@@ -168,32 +181,66 @@ const edit = (data) => {
} else { } else {
actionTitle.value += "编辑" actionTitle.value += "编辑"
} }
// 修改源码:结束
currentAction.value = "edit" currentAction.value = "edit"
formOptions.value["disabled"] = false
form.value = {} form.value = {}
for (let i in data) form.value[i] = data[i] if (options.edit.dataSource && options.edit.dataSource === "api") {
open(data) const response = await options.edit.dataSourceApi(data[options.pk])
if (response.success) {
form.value = response.data
open(response.data)
}
} else {
for (let i in data) form.value[i] = data[i]
open(data)
}
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
}
const see = async (data) => {
actionTitle.value = options.see.title ?? "查看"
currentAction.value = "see"
formOptions.value["disabled"] = true
form.value = {}
if (options.see.dataSource && options.see.dataSource === "api") {
const response = await options.see.dataSourceApi(data[options.pk])
if (response.success) {
form.value = response.data
open(response.data)
}
} else {
for (let i in data) form.value[i] = data[i]
open(data)
}
await nextTick(() => maFormRef.value && maFormRef.value.updateOptions())
} }
const init = () => { const init = async () => {
dataLoading.value = true dataLoading.value = true
const layout = JSON.parse(JSON.stringify(options?.formOption?.layout ?? [])) const layout = JSON.parse(JSON.stringify(options?.formOption?.layout ?? []))
// const layout = options?.formOption?.layout ?? []
columns.map(async (item) => { await Promise.all(
await columnItemHandle(item) columns.value.map(async (item) => {
}) if (item.children && item.children.length > 0) {
await item.children.map(async (childItem) => {
await columnItemHandle(childItem)
})
} else {
await columnItemHandle(item)
}
})
)
// 设置表单布局 // 设置表单布局
settingFormLayout(layout) settingFormLayout(layout)
if (isArray(layout) && layout.length > 0) { if (isArray(layout) && layout.length > 0) {
formColumns.value = layout formColumns.value = layout
const excludeColumns = ["__index", "__operation"] const excludeColumns = ["__index", "__operation"]
columns.map((item) => { columns.value.map((item) => {
if (options.formExcludePk) excludeColumns.push(options.pk) if (options.formExcludePk) excludeColumns.push(options.pk)
if (excludeColumns.includes(item.dataIndex)) return if (excludeColumns.includes(item.dataIndex)) return
!item.__formLayoutSetting && formColumns.value.push(item) !item.__formLayoutSetting && formColumns.value.push(item)
}) })
} }
dataLoading.value = false dataLoading.value = false
} }
@@ -207,28 +254,33 @@ const columnItemHandle = async (item) => {
layoutColumns.value.set(item.dataIndex, item) layoutColumns.value.set(item.dataIndex, item)
formColumns.value.push(item) formColumns.value.push(item)
// 针对带点的数据处理 if (options.formOption.viewType !== "tag") {
if (item.dataIndex && item.dataIndex.indexOf(".") > -1) { // 针对带点的数据处理
form.value[item.dataIndex] = get(form.value, item.dataIndex) if (item.dataIndex && item.dataIndex.indexOf(".") > -1) {
} form.value[item.dataIndex] = get(form.value, item.dataIndex)
// add 默认值处理
if (currentAction.value === "add") {
if (item.addDefaultValue && isFunction(item.addDefaultValue)) {
form.value[item.dataIndex] = await item.addDefaultValue(form.value)
} else if (typeof item.addDefaultValue != "undefined") {
form.value[item.dataIndex] = item.addDefaultValue
} }
}
// edit 默认值处理 // add 默认值处理
if (currentAction.value === "edit") { if (currentAction.value === "add") {
if (item.editDefaultValue && isFunction(item.editDefaultValue)) { if (item.addDefaultValue && isFunction(item.addDefaultValue)) {
form.value[item.dataIndex] = await item.editDefaultValue(form.value) form.value[item.dataIndex] = await item.addDefaultValue(form.value)
} else if (typeof item.editDefaultValue != "undefined") { } else if (typeof item.addDefaultValue != "undefined") {
form.value[item.dataIndex] = item.editDefaultValue form.value[item.dataIndex] = item.addDefaultValue
}
}
// edit 和 see 默认值处理
if (currentAction.value === "edit" || currentAction.value === "see") {
if (item.editDefaultValue && isFunction(item.editDefaultValue)) {
form.value[item.dataIndex] = await item.editDefaultValue(form.value)
} else if (typeof item.editDefaultValue != "undefined") {
form.value[item.dataIndex] = item.editDefaultValue
}
} }
} }
item.disabled = undefined
item.readonly = undefined
await nextTick()
// 其他处理 // 其他处理
item.display = formItemShow(item) item.display = formItemShow(item)
item.disabled = formItemDisabled(item) item.disabled = formItemDisabled(item)
@@ -236,6 +288,7 @@ const columnItemHandle = async (item) => {
item.labelWidth = formItemLabelWidth(item) item.labelWidth = formItemLabelWidth(item)
item.rules = getRules(item) item.rules = getRules(item)
} }
const settingFormLayout = (layout) => { const settingFormLayout = (layout) => {
if (!isArray(layout)) { if (!isArray(layout)) {
return return
@@ -306,19 +359,22 @@ const settingFormLayout = (layout) => {
const formItemShow = (item) => { const formItemShow = (item) => {
if (currentAction.value === "add") { if (currentAction.value === "add") {
return item.addDisplay !== false return isFunction(item.addDisplay) ? item.addDisplay() !== false : item.addDisplay !== false
} }
if (currentAction.value === "edit") { if (currentAction.value === "edit" || currentAction.value === "see") {
return item.editDisplay !== false return isFunction(item.editDisplay) ? item.editDisplay(form.value) !== false : item.editDisplay !== false
} }
return item.display !== false return item.display !== false
} }
const formItemDisabled = (item) => { const formItemDisabled = (item) => {
if (currentAction.value === "add" && !isUndefined(item.addDisabled)) { if (currentAction.value === "add" && !isUndefined(item.addDisabled)) {
return item.addDisabled return isFunction(item.addDisabled) ? item.addDisabled() : item.addDisabled
} }
if (currentAction.value === "edit" && !isUndefined(item.editDisabled)) { if (currentAction.value === "edit" && !isUndefined(item.editDisabled)) {
return item.editDisabled return isFunction(item.editDisabled) ? item.editDisabled(form.value) : item.editDisabled
}
if (currentAction.value === "see") {
return true
} }
if (!isUndefined(item.disabled)) { if (!isUndefined(item.disabled)) {
return item.disabled return item.disabled
@@ -327,10 +383,13 @@ const formItemDisabled = (item) => {
} }
const formItemReadonly = (item) => { const formItemReadonly = (item) => {
if (currentAction.value === "add" && !isUndefined(item.addReadonly)) { if (currentAction.value === "add" && !isUndefined(item.addReadonly)) {
return item.addReadonly return isFunction(item.addReadonly) ? item.addReadonly() : item.addReadonly
} }
if (currentAction.value === "edit" && !isUndefined(item.editReadonly)) { if (currentAction.value === "edit" && !isUndefined(item.editReadonly)) {
return item.editReadonly return isFunction(item.editReadonly) ? item.editReadonly(form.value) : item.editReadonly
}
if (currentAction.value === "see") {
return true
} }
if (!isUndefined(item.readonly)) { if (!isUndefined(item.readonly)) {
return item.readonly return item.readonly
@@ -371,5 +430,6 @@ const getFormColumns = async (type = "add") => {
await init() await init()
return formColumns.value return formColumns.value
} }
defineExpose({ add, edit, currentAction, form, getFormColumns, actionTitle }) // 修改源码,暴露actionTitle
defineExpose({ add, edit, see, currentAction, form, getFormColumns, maFormRef, actionTitle })
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-modal v-model:visible="visible" :footer="false" @cancel="close" draggable> <a-modal v-model:visible="visible" :footer="false" @cancel="close" draggable>
@@ -37,6 +32,7 @@ import { Message } from "@arco-design/web-vue"
const visible = ref(false) const visible = ref(false)
const options = inject("options") const options = inject("options")
const emit = defineEmits(["success"])
const open = () => (visible.value = true) const open = () => (visible.value = true)
const close = () => (visible.value = false) const close = () => (visible.value = false)
@@ -46,8 +42,9 @@ const upload = (fileOption) => {
const dataForm = new FormData() const dataForm = new FormData()
dataForm.append("file", fileOption.fileItem.file) dataForm.append("file", fileOption.fileItem.file)
commonApi.importExcel(options.import.url, dataForm).then((res) => { commonApi.importExcel(options.import.url, dataForm).then(async (res) => {
res.success && Message.success(res.message || "导入成功") res.success && Message.success(res.message || "导入成功")
emit("success")
close() close()
}) })
} }

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-spin :loading="searchLoading" :tip="options.searchLoadingText" v-if="showSearch"> <a-spin :loading="searchLoading" :tip="options.searchLoadingText" v-if="showSearch">
@@ -25,7 +20,10 @@
:label-col-style="{ width: component.searchLabelWidth ?? options.searchLabelWidth }" :label-col-style="{ width: component.searchLabelWidth ?? options.searchLabelWidth }"
> >
<slot :name="`${component.dataIndex}`" v-bind="{ searchForm, component }"> <slot :name="`${component.dataIndex}`" v-bind="{ searchForm, component }">
<component :is="getComponentName(component.formType)" :component="component" /> <component
:is="getComponentName(component.searchFormType ?? component.formType)"
:component="component"
/>
</slot> </slot>
</a-form-item> </a-form-item>
</template> </template>
@@ -57,7 +55,7 @@ import MaFormPicker from "./searchFormItem/form-picker.vue"
import MaFormSelect from "./searchFormItem/form-select.vue" import MaFormSelect from "./searchFormItem/form-select.vue"
import MaFormCascader from "./searchFormItem/form-cascader.vue" import MaFormCascader from "./searchFormItem/form-cascader.vue"
import MaFormTreeSelect from "./searchFormItem/form-tree-select.vue" import MaFormTreeSelect from "./searchFormItem/form-tree-select.vue"
import { cloneDeep, isFunction } from "lodash" import { cloneDeep, isFunction } from "lodash-es"
const options = inject("options") const options = inject("options")
const columns = inject("columns") const columns = inject("columns")
@@ -73,14 +71,30 @@ const searchColumns = ref([])
const searchForm = ref({}) const searchForm = ref({})
provide("searchForm", searchForm) provide("searchForm", searchForm)
provide("columns", columns)
const emit = defineEmits(["search"]) const emit = defineEmits(["search"])
if (columns.length > 0) { const getSearchAllColumns = (cls = []) => {
searchColumns.value = cloneDeep( let sls = []
columns.filter((item) => item.search === true && options.tabs?.dataIndex != item.dataIndex) cls.map((item) => {
) if (item.children && item.children.length > 0) {
let tmp = getSearchAllColumns(item.children)
sls.push(...tmp)
} else if (item.dataIndex && item.search && item.search === true) {
sls.push(item)
}
})
return sls
}
const initSearchColumns = () => {
if (columns.value.length > 0) {
searchColumns.value = cloneDeep(
getSearchAllColumns(columns.value).filter(
(item) => item.search === true && options.tabs?.dataIndex != item.dataIndex
)
)
}
} }
const handlerSearch = () => { const handlerSearch = () => {
@@ -89,6 +103,13 @@ const handlerSearch = () => {
const resetSearch = async () => { const resetSearch = async () => {
searchRef.value.resetFields() searchRef.value.resetFields()
Object.keys(searchForm.value).map((item) => {
let temp = item.match(/^(.+)Min$/)
if (temp) {
searchForm.value[temp[1] + "Min"] = undefined
searchForm.value[temp[1] + "Max"] = undefined
}
})
if (options.resetSearch && isFunction(options.resetSearch)) { if (options.resetSearch && isFunction(options.resetSearch)) {
await options.resetSearch(searchForm.value) await options.resetSearch(searchForm.value)
} }
@@ -117,6 +138,8 @@ const getComponentName = (formType) => {
} }
} }
initSearchColumns()
const setSearchHidden = () => (showSearch.value = false) const setSearchHidden = () => (showSearch.value = false)
const setSearchDisplay = () => (showSearch.value = true) const setSearchDisplay = () => (showSearch.value = true)
const setSearchLoading = () => (searchLoading.value = true) const setSearchLoading = () => (searchLoading.value = true)
@@ -125,9 +148,11 @@ const getSearchFormRef = () => searchRef.value
const getSearchColumns = () => searchColumns.value const getSearchColumns = () => searchColumns.value
defineExpose({ defineExpose({
initSearchColumns,
getSearchFormRef, getSearchFormRef,
getSearchColumns, getSearchColumns,
showSearch, showSearch,
resetSearch,
setSearchHidden, setSearchHidden,
setSearchDisplay, setSearchDisplay,
setSearchLoading, setSearchLoading,

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-cascader <a-cascader
@@ -21,7 +16,7 @@
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })

View File

@@ -1,5 +1,30 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<a-input-group v-if="props.component.formType === 'input-number' && (props.component?.rangeSearch ?? false)">
<a-input-number
v-model="minData"
allow-clear
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}最小值`"
/>
<span class="p-1">~</span>
<a-input-number
v-model="maxData"
allow-clear
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}最大值`"
/>
</a-input-group>
<a-input-number
v-else-if="props.component.formType === 'input-number'"
v-model="value"
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}`"
allow-clear
/>
<a-input <a-input
v-else
v-model="value" v-model="value"
:placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}`" :placeholder="props.component.searchPlaceholder ?? `请输入${props.component.title}`"
allow-clear allow-clear
@@ -8,14 +33,36 @@
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import { get, set } from "lodash" import { get, set, isArray } from "lodash-es"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
const searchForm = inject("searchForm") const searchForm = inject("searchForm")
const minData = ref(isArray(props.component?.searchDefaultValue) ? props.component?.searchDefaultValue[0] : undefined)
const maxData = ref(isArray(props.component?.searchDefaultValue) ? props.component?.searchDefaultValue[1] : undefined)
const value = ref(
get(
searchForm.value,
props.component.dataIndex,
(props.component.searchDefaultValue ?? props.component.formType === "input-number") ? 0 : ""
)
)
const value = ref(get(searchForm.value, props.component.dataIndex, props.component.searchDefaultValue ?? "")) if (props.component.formType === "input-number" && (props.component?.rangeSearch ?? false)) {
set(searchForm.value, props.component.dataIndex, value.value) set(searchForm.value, `${props.component.dataIndex}Min`, minData.value)
set(searchForm.value, `${props.component.dataIndex}Max`, maxData.value)
watch(
() => get(searchForm.value, props.component.dataIndex + "Min"),
(vl) => (minData.value = vl)
)
watch(
() => get(searchForm.value, props.component.dataIndex + "Max"),
(vl) => (maxData.value = vl)
)
} else {
set(searchForm.value, props.component.dataIndex, value.value)
}
watch( watch(
() => get(searchForm.value, props.component.dataIndex), () => get(searchForm.value, props.component.dataIndex),
@@ -25,4 +72,12 @@ watch(
() => value.value, () => value.value,
(v) => set(searchForm.value, props.component.dataIndex, v) (v) => set(searchForm.value, props.component.dataIndex, v)
) )
watch(
() => minData.value,
(v) => set(searchForm.value, `${props.component.dataIndex}Min`, v)
)
watch(
() => maxData.value,
(v) => set(searchForm.value, `${props.component.dataIndex}Max`, v)
)
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<component <component
@@ -18,6 +13,7 @@
" "
:time-picker-props="props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}" :time-picker-props="props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}"
:show-time="props.component.showTime" :show-time="props.component.showTime"
:type="props.component.range ? (props.component.formType === 'time' ? 'time-range' : 'range') : ''"
:format="props.component.format || ''" :format="props.component.format || ''"
:mode="props.component.mode" :mode="props.component.mode"
allow-clear allow-clear
@@ -26,12 +22,13 @@
</template> </template>
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { inject, computed } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
const searchForm = inject("searchForm") const searchForm = inject("searchForm")
const emit = defineEmits(["update:modelValue"])
const getComponentName = () => { const getComponentName = () => {
if (["date", "month", "year", "week", "quarter", "range", "time"].includes(props.component.formType)) { if (["date", "month", "year", "week", "quarter", "range", "time"].includes(props.component.formType)) {
@@ -39,23 +36,30 @@ const getComponentName = () => {
} }
} }
let defaultValue const value = computed({
get() {
let val = get(searchForm.value, props.component.dataIndex)
if (val === undefined) {
if (props.component.formType === "range") {
val = props.component.searchDefaultValue ?? []
} else {
val = props.component.searchDefaultValue ?? ""
}
}
if (props.component.formType === "range") { return val
defaultValue = props.component.searchDefaultValue ?? [] },
} else { set(newVal) {
defaultValue = props.component.searchDefaultValue ?? "" if (newVal === undefined) {
} if (props.component.formType === "range") {
newVal = []
} else {
newVal = ""
}
}
const value = ref(get(searchForm.value, props.component.dataIndex, defaultValue)) emit("update:modelValue", newVal)
set(searchForm.value, props.component.dataIndex, value.value) set(searchForm.value, props.component.dataIndex, newVal)
}
watch( })
() => get(searchForm.value, props.component.dataIndex),
(vl) => (value.value = vl)
)
watch(
() => value.value,
(v) => set(searchForm.value, props.component.dataIndex, v)
)
</script> </script>

View File

@@ -1,12 +1,3 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
-->
<template> <template>
<a-select <a-select
v-model="value" v-model="value"
@@ -26,7 +17,7 @@
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import { handlerCascader } from "@cps/ma-form/js/networkRequest" import { handlerCascader } from "@cps/ma-form/js/networkRequest"
import { get, set } from "lodash" import { get, set } from "lodash-es"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-tree-select <a-tree-select
@@ -23,7 +18,7 @@
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })

View File

@@ -1,14 +1,9 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-drawer :visible="visible" unmountOnClose :footer="false" :width="900" @cancel="onCancel"> <a-drawer :visible="visible" unmountOnClose :footer="false" :width="950" @cancel="onCancel">
<template #title>设置</template> <template #title>设置</template>
<a-space class="mt-3"> <a-space class="mt-3">
@@ -59,7 +54,12 @@
<span v-else> / </span> <span v-else> / </span>
</template> </template>
</a-table-column> </a-table-column>
<a-table-column title="隐藏" data-index="hide" align="center"> <a-table-column title="搜索隐藏" data-index="hide" align="center">
<template #cell="{ record }"
><a-checkbox v-model="record.search" @change="changeColumn($event, 'search', record.dataIndex)"
/></template>
</a-table-column>
<a-table-column title="表格隐藏" data-index="hide" align="center">
<template #cell="{ record }" <template #cell="{ record }"
><a-checkbox v-model="record.hide" @change="changeColumn($event, 'hide', record.dataIndex)" ><a-checkbox v-model="record.hide" @change="changeColumn($event, 'hide', record.dataIndex)"
/></template> /></template>
@@ -123,16 +123,22 @@
import { ref, inject } from "vue" import { ref, inject } from "vue"
const options = inject("options") const options = inject("options")
let columns = inject("columns") const columns = inject("columns")
const allowShowColumns = ref([])
const allowShowColumns = columns.filter((item) => { const emit = defineEmits(["onChangeSearchHide", "onChangeColumnHide"])
return !(item?.settingHide ?? false)
}) const setShowColumns = () => {
allowShowColumns.value = columns.value.filter((item) => {
return !(item?.settingHide ?? false)
})
}
const visible = ref(false) const visible = ref(false)
const bordered = ref("column") const bordered = ref("column")
const open = () => { const open = () => {
setShowColumns()
visible.value = true visible.value = true
} }
@@ -141,7 +147,7 @@ const onCancel = () => {
} }
const changeColumn = (ev, type, name) => { const changeColumn = (ev, type, name) => {
const column = columns.find((item) => item.dataIndex === name) const column = columns.value.find((item) => item.dataIndex === name)
switch (type) { switch (type) {
case "order": case "order":
if (ev === "page") { if (ev === "page") {
@@ -152,6 +158,12 @@ const changeColumn = (ev, type, name) => {
column.sortable = undefined column.sortable = undefined
} }
break break
case "hide":
emit("onChangeColumnHide")
break
case "search":
emit("onChangeSearchHide")
break
} }
} }
@@ -171,7 +183,8 @@ const changeBordered = (v) => {
} }
const onTableChange = (_data) => { const onTableChange = (_data) => {
columns = _data columns.value = _data
setShowColumns()
} }
defineExpose({ open }) defineExpose({ open })

View File

@@ -8,14 +8,22 @@
:type="options.tabs.type" :type="options.tabs.type"
:hide-content="true" :hide-content="true"
@change="tabChange" @change="tabChange"
@tab-click="maEvent.customeEvent(options.tabs, $event, 'onClick')" @tab-click="runEvent(options.tabs, 'onClick', undefined, $event)"
class="ma-tabs mb-5" class="ma-tabs mb-5"
> >
<template #extra><slot name="tabExtra"></slot></template> <template #extra><slot name="tabExtra"></slot></template>
<a-tab-pane :key="item.value" :title="item.label" v-for="item in options.tabs.data"></a-tab-pane> <a-tab-pane :key="item.value" :title="item.label" v-for="item in options.tabs.data">
<template #title
><slot :name="'tabTitle-' + item.label">{{ item.label }}</slot></template
>
</a-tab-pane>
</a-tabs> </a-tabs>
<ma-search @search="searchSubmitHandler" class="__search-panel" ref="crudSearchRef"> <ma-search @search="searchSubmitHandler" class="__search-panel" ref="crudSearchRef">
<template v-for="(slot, slotIndex) in searchSlots" :key="slotIndex" #[slot]="{ searchForm, component }"> <template
v-for="(slot, slotIndex) in getSearchSlot()"
:key="slotIndex"
#[slot]="{ searchForm, component }"
>
<slot :name="`search-${slot}`" v-bind="{ searchForm, component }" /> <slot :name="`search-${slot}`" v-bind="{ searchForm, component }" />
</template> </template>
<template #searchBeforeButtons> <template #searchBeforeButtons>
@@ -28,6 +36,7 @@
<slot name="searchAfterButtons"></slot> <slot name="searchAfterButtons"></slot>
</template> </template>
</ma-search> </ma-search>
<div v-if="$slots.middleContent" class="mb-2"><slot name="middleContent"></slot></div>
</div> </div>
<div class="_crud-content"> <div class="_crud-content">
<div class="operation-tools lg:flex justify-between mb-3" ref="crudOperationRef"> <div class="operation-tools lg:flex justify-between mb-3" ref="crudOperationRef">
@@ -36,6 +45,8 @@
<slot name="tableButtons"> <slot name="tableButtons">
<a-button <a-button
v-if="options.add.show" v-if="options.add.show"
v-auth="options.add.auth || []"
v-role="options.add.role || []"
@click="addAction" @click="addAction"
type="primary" type="primary"
class="w-full lg:w-auto mt-2 lg:mt-0" class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -43,9 +54,15 @@
<template #icon><icon-plus /></template>{{ options.add.text || "新增" }} <template #icon><icon-plus /></template>{{ options.add.text || "新增" }}
</a-button> </a-button>
<a-popconfirm content="确定要删除数据吗?" position="bottom" @ok="deletesMultipleAction"> <a-popconfirm
content="确定要删除数据吗?"
position="bottom"
@ok="deletesMultipleAction"
v-if="options.delete.show && isBatch(options.delete) && options.rowSelection"
>
<a-button <a-button
v-if="options.delete.show" v-auth="options.delete.auth || []"
v-role="options.delete.role || []"
type="primary" type="primary"
status="danger" status="danger"
class="w-full lg:w-auto mt-2 lg:mt-0" class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -55,9 +72,15 @@
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
<a-popconfirm content="确定要恢复数据吗?" position="bottom" @ok="recoverysMultipleAction"> <a-popconfirm
content="确定要恢复数据吗?"
position="bottom"
@ok="recoverysMultipleAction"
v-if="options.recovery.show && isRecovery && isBatch(options.delete)"
>
<a-button <a-button
v-if="options.recovery.show && isRecovery" v-auth="options.recovery.auth || []"
v-role="options.recovery.role || []"
type="primary" type="primary"
status="success" status="success"
class="w-full lg:w-auto mt-2 lg:mt-0" class="w-full lg:w-auto mt-2 lg:mt-0"
@@ -66,11 +89,21 @@
> >
</a-popconfirm> </a-popconfirm>
<a-button v-if="options.import.show" @click="importAction" class="w-full lg:w-auto mt-2 lg:mt-0" <a-button
v-if="options.import.show"
v-auth="options.import.auth || []"
v-role="options.import.role || []"
@click="importAction"
class="w-full lg:w-auto mt-2 lg:mt-0"
><template #icon><icon-upload /></template>{{ options.import.text || "导入" }}</a-button ><template #icon><icon-upload /></template>{{ options.import.text || "导入" }}</a-button
> >
<a-button v-if="options.export.show" @click="exportAction" class="w-full lg:w-auto mt-2 lg:mt-0" <a-button
v-if="options.export.show"
v-auth="options.export.auth || []"
v-role="options.export.role || []"
@click="exportAction"
class="w-full lg:w-auto mt-2 lg:mt-0"
><template #icon><icon-download /></template>{{ options.export.text || "导出" }}</a-button ><template #icon><icon-download /></template>{{ options.export.text || "导出" }}</a-button
> >
@@ -112,8 +145,9 @@
</a-space> </a-space>
</div> </div>
<div ref="crudContentRef"> <div ref="crudContentRef">
<slot name="content" v-bind="tableData"> <slot name="crudContent" v-bind="tableData">
<a-table <a-table
v-if="tableIsShow"
v-bind="$attrs" v-bind="$attrs"
ref="tableRef" ref="tableRef"
:key="options.pk" :key="options.pk"
@@ -123,7 +157,7 @@
:pagination="options.tablePagination" :pagination="options.tablePagination"
:stripe="options.stripe" :stripe="options.stripe"
:bordered="options.bordered" :bordered="options.bordered"
:rowSelection="options.rowSelection || undefined" :rowSelection="options.rowSelection ?? undefined"
:row-key="options?.rowSelection?.key ?? options.pk" :row-key="options?.rowSelection?.key ?? options.pk"
:scroll="options.scroll" :scroll="options.scroll"
:column-resizable="options.resizable" :column-resizable="options.resizable"
@@ -131,8 +165,8 @@
:row-class="options.rowClass" :row-class="options.rowClass"
:hide-expand-button-on-empty="options.hideExpandButtonOnEmpty" :hide-expand-button-on-empty="options.hideExpandButtonOnEmpty"
:default-expand-all-rows="options.expandAllRows" :default-expand-all-rows="options.expandAllRows"
:summary="options.customerSummary || __summary || options.showSummary" :summary="(options.customerSummary || options.showSummary) && __summary"
@selection-change="setSelecteds" v-model:selectedKeys="selecteds"
@sorter-change="handlerSort" @sorter-change="handlerSort"
> >
<template #tr="{ record }"> <template #tr="{ record }">
@@ -155,7 +189,7 @@
<ma-column <ma-column
ref="crudColumnRef" ref="crudColumnRef"
v-if="reloadColumn" v-if="reloadColumn"
:columns="props.columns" :columns="columns"
:isRecovery="isRecovery" :isRecovery="isRecovery"
:crudFormRef="crudFormRef" :crudFormRef="crudFormRef"
@refresh="() => refresh()" @refresh="() => refresh()"
@@ -174,7 +208,15 @@
</template> </template>
<template <template
v-for="(slot, slotIndex) in slots" v-for="(slot, index) in getTitleSlot(columns)"
#[slot]="{ column }"
:key="index"
>
<slot :name="`${slot}`" v-bind="{ column }" />
</template>
<template
v-for="(slot, slotIndex) in getSlot(columns)"
:key="slotIndex" :key="slotIndex"
#[slot]="{ record, column, rowIndex }" #[slot]="{ record, column, rowIndex }"
> >
@@ -213,19 +255,24 @@
/> />
</div> </div>
<ma-setting ref="crudSettingRef" /> <ma-setting ref="crudSettingRef" @onChangeSearchHide="initSearchColumns()" @onChangeColumnHide="changeColumn" />
<!-- 修改源码透传ma-crud属性给ma-form -->
<ma-form ref="crudFormRef" @success="requestSuccess" v-bind="$attrs"> <ma-form ref="crudFormRef" @success="requestSuccess" v-bind="$attrs">
<template v-for="slot in Object.keys($slots)" #[slot]="component"> <template v-for="(slot, index) in Object.keys($slots)" #[slot]="component" :key="index">
<slot :name="slot" v-bind="component" /> <slot :name="slot" v-bind="component" />
</template> </template>
</ma-form> </ma-form>
<ma-import ref="crudImportRef" /> <ma-import ref="crudImportRef" @success="refresh" />
<ma-context-menu ref="crudContextMenuRef" @execCommand="execContextMenuCommand" /> <ma-context-menu ref="crudContextMenuRef" @execCommand="execContextMenuCommand" />
<a-image-preview :src="imgUrl" v-model:visible="imgVisible" /> <a-image-preview-group
:srcList="imgUrl"
v-model:visible="imgVisible"
v-if="typeof imgUrl === 'object' && imgUrl !== null"
/>
<a-image-preview :src="imgUrl" v-model:visible="imgVisible" v-else />
</a-layout-content> </a-layout-content>
</template> </template>
@@ -234,7 +281,7 @@ import config from "@/config/crud"
import { ref, watch, provide, nextTick, onMounted, onUnmounted } from "vue" import { ref, watch, provide, nextTick, onMounted, onUnmounted } from "vue"
import defaultOptions from "./js/defaultOptions" import defaultOptions from "./js/defaultOptions"
import { loadDict } from "@cps/ma-form/js/networkRequest.js" import { loadDict } from "@cps/ma-form/js/networkRequest.js"
import ColumnService from "./js/columnService" import ColumnService from "@cps/ma-form/js/columnService"
import MaSearch from "./components/search.vue" import MaSearch from "./components/search.vue"
import MaForm from "./components/form.vue" import MaForm from "./components/form.vue"
@@ -242,12 +289,14 @@ import MaSetting from "./components/setting.vue"
import MaImport from "./components/import.vue" import MaImport from "./components/import.vue"
import MaColumn from "./components/column.vue" import MaColumn from "./components/column.vue"
import MaContextMenu from "./components/contextMenu.vue" import MaContextMenu from "./components/contextMenu.vue"
import checkAuth from "@/directives/auth/auth"
import checkRole from "@/directives/role/role"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import { request } from "@/utils/request" import { request } from "@/utils/request"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import Print from "@/utils/print" import Print from "@/utils/print"
import { isArray, isFunction, isObject, isUndefined } from "lodash" import { isArray, isFunction, isObject, isUndefined } from "lodash-es"
import { maEvent } from "@cps/ma-form/js/formItemMixin.js" import { runEvent } from "@cps/ma-form/js/event.js"
import globalColumn from "@/config/column.js" import globalColumn from "@/config/column.js"
import { useFormStore } from "@/store/index" import { useFormStore } from "@/store/index"
@@ -267,6 +316,7 @@ const dicts = ref({})
const cascaders = ref([]) const cascaders = ref([])
const reloadColumn = ref(true) const reloadColumn = ref(true)
const tableIsShow = ref(true)
const openPagination = ref(false) const openPagination = ref(false)
const imgVisible = ref(false) const imgVisible = ref(false)
const imgUrl = ref(import.meta.env.VITE_APP_BASE + "not-image.png") const imgUrl = ref(import.meta.env.VITE_APP_BASE + "not-image.png")
@@ -305,19 +355,21 @@ const init = async () => {
} }
// 收集数据 // 收集数据
props.columns.map((item) => { columns.value.map((item) => {
if (item.cascaderItem && item.cascaderItem.length > 0) { if (item.cascaderItem && item.cascaderItem.length > 0) {
cascaders.value.push(...item.cascaderItem) cascaders.value.push(...item.cascaderItem)
} }
}) })
await props.columns.map(async (item) => { await columns.value.map(async (item) => {
// 字典 // 字典
if (!cascaders.value.includes(item.dataIndex) && item.dict) { if (!cascaders.value.includes(item.dataIndex) && item.dict) {
await loadDict(dicts.value, item) await loadDict(dicts.value, item)
} }
}) })
await tabsHandler() setTimeout(async () => {
await tabsHandler()
}, 500)
} }
const dictTrans = (dataIndex, value) => { const dictTrans = (dataIndex, value) => {
@@ -343,14 +395,14 @@ columns.value.map((item, index) => {
item = columns.value[index] item = columns.value[index]
} }
!item.width && (item.width = options.value.columnWidth) !item.width && (item.width = options.value.columnWidth)
!item.minWidth && (item.minWidth = options.value.columnMinWidth)
}) })
provide("options", options.value) provide("options", options.value)
provide("columns", props.columns)
provide("layout", props.layout)
provide("dicts", dicts.value)
provide("dictColors", dictColors.value)
provide("requestParams", requestParams.value) provide("requestParams", requestParams.value)
provide("columns", columns)
provide("dicts", dicts)
provide("layout", props.layout)
provide("dictTrans", dictTrans) provide("dictTrans", dictTrans)
provide("dictColors", dictColors) provide("dictColors", dictColors)
provide("isRecovery", isRecovery) provide("isRecovery", isRecovery)
@@ -365,10 +417,7 @@ watch(
(vl) => (options.value.api = vl) (vl) => (options.value.api = vl)
) )
watch( watch([() => openPagination.value, () => total.value], () => options.value.pageLayout === "fixed" && settingFixedPage())
() => openPagination.value,
() => options.value.pageLayout === "fixed" && settingFixedPage()
)
watch( watch(
() => formStore.crudList[options.value.id], () => formStore.crudList[options.value.id],
@@ -378,6 +427,11 @@ watch(
} }
) )
const showImage = (url) => {
imgUrl.value = url
imgVisible.value = true
}
const getSlot = (cls = []) => { const getSlot = (cls = []) => {
let sls = [] let sls = []
cls.map((item) => { cls.map((item) => {
@@ -391,14 +445,22 @@ const getSlot = (cls = []) => {
return sls return sls
} }
const showImage = (url) => { const getTitleSlot = (cls = []) => {
imgUrl.value = url let sls = []
imgVisible.value = true cls.map((item) => {
if (item.children && item.children.length > 0) {
let tmp = getTitleSlot(item.children)
sls.push(...tmp)
} else if (item.dataIndex) {
sls.push(`tableTitle-${item.dataIndex}`)
}
})
return sls
} }
const getSearchSlot = () => { const getSearchSlot = () => {
let sls = [] let sls = []
props.columns.map((item) => { columns.value.map((item) => {
if (item.search && item.search === true) { if (item.search && item.search === true) {
sls.push(item.dataIndex) sls.push(item.dataIndex)
} }
@@ -406,9 +468,6 @@ const getSearchSlot = () => {
return sls return sls
} }
slots.value = getSlot(props.columns)
searchSlots.value = getSearchSlot(props.columns)
const requestData = async () => { const requestData = async () => {
await init() await init()
if (options.value.showIndex && columns.value.length > 0 && columns.value[0].dataIndex !== "__index") { if (options.value.showIndex && columns.value.length > 0 && columns.value[0].dataIndex !== "__index") {
@@ -456,6 +515,7 @@ const requestHandle = async () => {
loading.value = true loading.value = true
isFunction(options.value.beforeRequest) && options.value.beforeRequest(requestParams.value) isFunction(options.value.beforeRequest) && options.value.beforeRequest(requestParams.value)
if (isFunction(currentApi.value)) { if (isFunction(currentApi.value)) {
// 修改源码添加options.prameters参数
if (options.value.parameters) { if (options.value.parameters) {
requestParams.value = { ...requestParams.value, ...options.value.parameters } requestParams.value = { ...requestParams.value, ...options.value.parameters }
} }
@@ -463,7 +523,6 @@ const requestHandle = async () => {
if (response.rows) { if (response.rows) {
tableData.value = response.rows tableData.value = response.rows
if (response.pageInfo) { if (response.pageInfo) {
// 这里去找total字段
total.value = response.pageInfo.total total.value = response.pageInfo.total
openPagination.value = true openPagination.value = true
} else { } else {
@@ -473,9 +532,9 @@ const requestHandle = async () => {
tableData.value = response tableData.value = response
} }
} else { } else {
console.error(`ma-crud errorcrud.api 不是一个 Function.`) console.error(`ma-crud error您传递的api属性不是一个函数.`)
} }
isFunction(options.value.afterRequest) && options.value.afterRequest(tableData.value) isFunction(options.value.afterRequest) && (tableData.value = options.value.afterRequest(tableData.value))
loading.value = false loading.value = false
} }
@@ -497,6 +556,8 @@ const refresh = async () => {
: options.value.api : options.value.api
await requestHandle() await requestHandle()
} }
selecteds.value = []
tableRef.value.selectAll(false)
} }
const searchSubmitHandler = async (formData) => { const searchSubmitHandler = async (formData) => {
@@ -544,7 +605,11 @@ const toggleSearch = async () => {
const settingFixedPage = () => { const settingFixedPage = () => {
const workAreaHeight = document.querySelector(".work-area").offsetHeight const workAreaHeight = document.querySelector(".work-area").offsetHeight
const tableHeight = workAreaHeight - headerHeight.value - (openPagination.value ? 152 : 108) const tableHeight =
workAreaHeight -
headerHeight.value -
(openPagination.value ? 160 : 116) +
(total.value === 0 && !loading.value ? 44 : 0)
crudContentRef.value.style.height = tableHeight + "px" crudContentRef.value.style.height = tableHeight + "px"
} }
@@ -592,9 +657,17 @@ const dbClickOpenEdit = (record) => {
Message.error("回收站数据不可编辑") Message.error("回收站数据不可编辑")
return return
} }
if (isArray(options.value.edit.auth) || options.value.edit.auth === undefined) {
for (let index in options.value.edit.auth) {
if (!checkAuth(options.value.edit.auth[index])) {
Message.error("没有编辑数据的权限")
return
}
}
if (options.value.edit.api && isFunction(options.value.edit.api)) { if (options.value.edit.api && options.value.edit.show && isFunction(options.value.edit.api)) {
editAction(record) editAction(record)
}
} }
} }
} }
@@ -630,6 +703,7 @@ const deletesMultipleAction = async () => {
options.value.afterDelete(response) options.value.afterDelete(response)
} }
response.success && Message.success(response.message || `删除成功!`) response.success && Message.success(response.message || `删除成功!`)
selecteds.value = []
await refresh() await refresh()
} else { } else {
Message.error("至少选择一条数据") Message.error("至少选择一条数据")
@@ -640,6 +714,7 @@ const recoverysMultipleAction = async () => {
if (selecteds.value && selecteds.value.length > 0) { if (selecteds.value && selecteds.value.length > 0) {
const response = await options.value.recovery.api({ ids: selecteds.value }) const response = await options.value.recovery.api({ ids: selecteds.value })
response.success && Message.success(response.message || `恢复成功!`) response.success && Message.success(response.message || `恢复成功!`)
selecteds.value = []
await refresh() await refresh()
} else { } else {
Message.error("至少选择一条数据") Message.error("至少选择一条数据")
@@ -690,24 +765,30 @@ const __summary = ({ data }) => {
let summarySuffixText = {} let summarySuffixText = {}
let length = data.length || 0 let length = data.length || 0
summary.map((item) => { summary.map((item) => {
summaryData[item.dataIndex] = 0 if (item.action && item.action === "text") {
summaryPrefixText[item.dataIndex] = item?.prefixText ?? "" summaryData[item.dataIndex] = item.content
summarySuffixText[item.dataIndex] = item?.suffixText ?? "" } else {
data.map((record) => { summaryData[item.dataIndex] = 0
if (record[item.dataIndex]) { summaryPrefixText[item.dataIndex] = item?.prefixText ?? ""
if (item.action && item.action === "sum") { summarySuffixText[item.dataIndex] = item?.suffixText ?? ""
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex]) data.map((record) => {
if (record[item.dataIndex]) {
if (item.action && item.action === "sum") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex])
}
if (item.action && item.action === "avg") {
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex]) / length
}
} }
if (item.action && item.action === "avg") { })
summaryData[item.dataIndex] += parseFloat(record[item.dataIndex]) / length }
}
}
})
}) })
for (let i in summaryData) { for (let i in summaryData) {
summaryData[i] = if (/^\d+(\.\d+)?$/.test(summaryData[i])) {
summaryPrefixText[i] + tool.groupSeparator(summaryData[i].toFixed(2)) + summarySuffixText[i] summaryData[i] =
summaryPrefixText[i] + tool.groupSeparator(summaryData[i].toFixed(2)) + summarySuffixText[i]
}
} }
return [summaryData] return [summaryData]
@@ -724,7 +805,7 @@ const tabChange = async (value) => {
const params = {} const params = {}
params[searchKey] = value params[searchKey] = value
requestParams.value = Object.assign(requestParams.value, params) requestParams.value = Object.assign(requestParams.value, params)
await maEvent.customeEvent(options.value.tabs, value, "onChange") await runEvent(options.value.tabs, "onChange", undefined, value)
await refresh() await refresh()
} }
@@ -756,7 +837,7 @@ const execContextMenuCommand = async (args) => {
crudColumnRef.value.deleteAction(record) crudColumnRef.value.deleteAction(record)
break break
default: default:
await maEvent.customeEvent(item, args, "onCommand") await runEvent(item, "onCommand", undefined, args)
break break
} }
} }
@@ -767,26 +848,33 @@ const tabsHandler = async () => {
if (isFunction(tabs.data) || isArray(tabs.data)) { if (isFunction(tabs.data) || isArray(tabs.data)) {
tabs.data = isFunction(tabs.data) ? await tabs.data() : tabs.data tabs.data = isFunction(tabs.data) ? await tabs.data() : tabs.data
} else if (!isUndefined(tabs.dataIndex)) { } else if (!isUndefined(tabs.dataIndex)) {
const col = props.columns.find((item) => item.dataIndex === tabs.dataIndex) const col = columns.value.find((item) => item.dataIndex === tabs.dataIndex)
if (col.search === true && isObject(col.dict)) { if (col.search === true && isObject(col.dict)) {
tabs.data = dicts.value[tabs.dataIndex] tabs.data = dicts.value[tabs.dataIndex]
} }
} }
} }
onMounted(async () => { const isBatch = (obj) => (isUndefined(obj) ? true : (obj?.batch ?? true))
if (typeof options.value.autoRequest == "undefined" || options.value.autoRequest) {
await requestData()
}
const changeColumn = async () => {
tableIsShow.value = false
await nextTick(() => (tableIsShow.value = true))
}
onMounted(async () => {
if (!options.value.expandSearch && crudSearchRef.value) { if (!options.value.expandSearch && crudSearchRef.value) {
crudSearchRef.value.setSearchHidden() crudSearchRef.value.setSearchHidden()
} }
if (options.value.pageLayout === "fixed") { if (options.value.pageLayout === "fixed") {
window.addEventListener("resize", resizeHandler, false) await nextTick(() => {
headerHeight.value = crudHeaderRef.value.offsetHeight window.addEventListener("resize", resizeHandler, false)
settingFixedPage() headerHeight.value = crudHeaderRef.value.offsetHeight
settingFixedPage()
})
}
if (typeof options.value.autoRequest == "undefined" || options.value.autoRequest) {
await requestData()
} }
}) })
@@ -803,6 +891,11 @@ const getFormColumns = async (type = "add") => {
return await crudFormRef.value.getFormColumns(type) return await crudFormRef.value.getFormColumns(type)
} }
const getCurrentPage = () => requestParams.value[config.request.page]
const getPageSize = () => requestParams.value[config.request.pageSize]
const getTotal = () => total.value
const initSearchColumns = () => crudSearchRef.value.initSearchColumns()
/** /**
* 获取column属性服务类 * 获取column属性服务类
* @returns ColumnService * @returns ColumnService
@@ -810,7 +903,7 @@ const getFormColumns = async (type = "add") => {
const getColumnService = (strictMode = true) => { const getColumnService = (strictMode = true) => {
return new ColumnService({ columns: columns.value, cascaders: cascaders.value, dicts: dicts.value }, strictMode) return new ColumnService({ columns: columns.value, cascaders: cascaders.value, dicts: dicts.value }, strictMode)
} }
const setTableData = (data = []) => (tableData.value = data)
defineExpose({ defineExpose({
refresh, refresh,
requestData, requestData,
@@ -822,13 +915,18 @@ defineExpose({
getFormData, getFormData,
getFormColumns, getFormColumns,
getColumnService, getColumnService,
getCurrentPage,
getPageSize,
getTotal,
requestParams, requestParams,
isRecovery, isRecovery,
tableRef, tableRef,
initSearchColumns,
crudFormRef, crudFormRef,
crudSearchRef, crudSearchRef,
crudImportRef, crudImportRef,
crudSettingRef crudSettingRef,
setTableData
}) })
</script> </script>
@@ -840,6 +938,5 @@ defineExpose({
} }
._crud-footer { ._crud-footer {
z-index: 10; z-index: 10;
height: 80px;
} }
</style> </style>

View File

@@ -2,8 +2,6 @@ import { loadDict } from "@cps/ma-form/js/networkRequest"
/** /**
* columnService 列服务处理类 * columnService 列服务处理类
* 首先感谢 @NEKGod 提交的PR此功能原本写在了 Ma-Crud 组件,我特意摘出来,封装成类通过引用来调用
* @author NEKGod, X.Mo <root@imoi.cn>
*/ */
const objectService = function (item) { const objectService = function (item) {

View File

@@ -46,7 +46,7 @@ export default {
// 是否显示总结行 // 是否显示总结行
showSummary: false, showSummary: false,
// 自定义总结行,要传入函数 // 自定义总结行,要传入函数
customerSummary: false, customerSummary: undefined,
// 是否显示工具栏 // 是否显示工具栏
showTools: true, showTools: true,
// 表头是否吸顶 // 表头是否吸顶
@@ -54,7 +54,8 @@ export default {
// 页面布局方式,支持 normal标准和 fixed固定两种 // 页面布局方式,支持 normal标准和 fixed固定两种
pageLayout: "normal", pageLayout: "normal",
// 默认统一设置列宽度 // 默认统一设置列宽度
columnWidth: 100, columnWidth: 0, // 列宽更新为最小列宽(此处设置为 0 时候,默认最小列宽生效)
columnMinWidth: 100,
// 搜索标签对齐方式 // 搜索标签对齐方式
searchLabelAlign: "right", searchLabelAlign: "right",
// 全局搜索标签宽度 // 全局搜索标签宽度
@@ -159,7 +160,13 @@ export default {
// 按钮文案 // 按钮文案
text: "编辑", text: "编辑",
// 是否显示 // 是否显示
show: false show: false,
// 数据来源table(表格行数据) | api(通过接口获取数据)
dataSource: "table",
// 数据源API接口
dataSourceApi: undefined,
// 是否禁用,仅表格行内按钮有效
disabled: false
}, },
delete: { delete: {
// 删除api // 删除api
@@ -170,6 +177,8 @@ export default {
role: [], role: [],
// 按钮文案 // 按钮文案
text: "删除", text: "删除",
// 是否禁用,仅表格行内按钮有效
disabled: false,
// 真实删除api // 真实删除api
realApi: undefined, realApi: undefined,
@@ -179,9 +188,13 @@ export default {
realRole: [], realRole: [],
// 真实按钮文案 // 真实按钮文案
realText: "删除", realText: "删除",
// 真实删除是否禁用,仅表格行内按钮有效
realDisabled: false,
// 是否显示 // 是否显示
show: false show: false,
// 是否显示批量处理按钮
batch: true
}, },
recovery: { recovery: {
// 恢复api // 恢复api
@@ -193,18 +206,26 @@ export default {
// 按钮文案 // 按钮文案
text: "恢复", text: "恢复",
// 是否显示 // 是否显示
show: false show: false,
// 是否显示批量处理按钮
batch: true
},
see: {
// 显示查看按钮的权限
auth: [],
// 显示查看按钮的角色
role: [],
// 按钮文案
text: "查看",
// 是否显示
show: false,
// 数据来源table(表格行数据) | api(通过接口获取数据)
dataSource: "table",
// 数据源API接口
dataSourceApi: undefined,
// 是否禁用,仅表格行内按钮有效
disabled: false
}, },
// see: {
// // 显示查看按钮的权限
// auth: [],
// // 显示查看按钮的角色
// role: [],
// // 按钮文案
// text: '查看',
// // 是否显示
// show: false,
// },
import: { import: {
// 导入url // 导入url
url: undefined, url: undefined,

View File

@@ -1,116 +1,133 @@
import { VNodeChild } from "vue" import { VNodeChild,Ref } from "vue";
/** /**
* 表单组件类型 * 表单组件类型
*/ */
import { FieldRule } from "@arco-design/web-vue" import { FieldRule } from "@arco-design/web-vue";
export type FormDataType =
| "radio"
| "checkbox"
| "select"
| "transfer"
| "tree-select"
| "cascader"
| "date"
| "month"
| "year"
| "week"
| "quarter"
| "range"
| "time"
| "input"
| "password"
| "textarea"
| "upload"
| "select-user"
| "editor"
| "icon"
| "user-info"
| "city-linkage"
| "form-group"
| "select-resource"
| 'component';
export type FormDateType =
| "radio"
| "checkbox"
| "select"
| "transfer"
| "tree-select"
| "cascader"
| "date"
| "month"
| "year"
| "week"
| "quarter"
| "range"
| "time"
| "input"
| "password"
| "textarea"
| "upload"
| "select-user"
| "editor"
| "code-editor"
| "icon"
| "user-info"
| "city-linkage"
| "form-group"
| "select-resource"
/** /**
* 列字典 * 列字典
*/ */
export interface ColumnDict { export interface ColumnDict {
// 字典名称,快捷查询字典接口查询 // 字典名称,快捷查询字典接口查询
name?: string name?: string;
// 自定义url查询 // 自定义url查询
url?: string url?: string;
// url查询方法,填写url之后生效 // url查询方法,填写url之后生效
method?: "GET" | "POST" | "PUT" | "DELETE" method?: "GET" | "POST" | "PUT" | "DELETE";
// url查询params数据,填写url之后生效 // url查询params数据,填写url之后生效
params?: object params?: object;
// url查询body数据,填写url之后生效 // url查询body数据,填写url之后生效
body?: object body?: object;
// 直接设置字典值 // 直接设置字典值
data?: object | Function data?: object | Function;
// 表格列的值是否翻译为字典对应标签 // 表格列的值是否翻译为字典对应标签
translation?: boolean translation?: boolean;
// 表格key 和 value的props设置 // 表格key 和 value的props设置
props?: { props?: {
label?: string label?: string;
value?: string value?: string;
} };
} }
export interface BasicColumn { export interface BasicColumn {
// 标题 // 标题
title: string title: string;
// 字段名称 // 字段名称
dataIndex: string dataIndex: string;
// 组件类型 // 组件类型
formType?: FormDateType formType?: FormDataType;
// 表格列对齐方式 // 表格列对齐方式
align?: "center" | "right" | "left" align?: "center" | "right" | "left";
// 字段是否加入搜索 // 字段是否加入搜索
search?: boolean search?: boolean;
// 列宽 // 列宽
width?: number | "auto" width?: number | "auto";
// 表格列是否隐藏 // 表格列是否隐藏
hide?: boolean hide?: boolean;
// 编辑|创建 通用是否显示字段 // 编辑|创建 通用是否显示字段
display?: boolean display?: boolean;
// 添加弹窗是否显示字段 // 添加弹窗是否显示字段
addDisplay?: boolean addDisplay?: boolean | (() => boolean);
// 编辑弹窗是否显示字段 // 编辑弹窗是否显示字段
editDisplay?: boolean editDisplay?: boolean | ((record) => boolean);
// 编辑|创建 通用是否禁用字段 // 编辑|创建 通用是否禁用字段
disabled?: boolean disabled?: boolean;
// 添加弹窗是否禁用字段 // 添加弹窗是否禁用字段
addDisabled?: boolean addDisabled?: boolean | (() => boolean);
// 编辑弹窗是否禁用字段 // 编辑弹窗是否禁用字段
editDisabled?: boolean editDisabled?: boolean | ((record) => boolean);
// 编辑|创建 通用是否只读字段 // 编辑|创建 通用是否只读字段
readonly?: boolean readonly?: boolean;
// 添加弹窗是否只读字段 // 添加弹窗是否只读字段
addReadonly?: boolean addReadonly?: boolean | (() => boolean);
// 编辑弹窗是否只读字段 // 编辑弹窗是否只读字段
editReadonly?: boolean editReadonly?: boolean | ((record) => boolean);
// 自定义渲染 // 自定义渲染
customRender?: (({ record, column, rowIndex }) => VNodeChild | JSX.Element) | VNodeChild | JSX.Element customRender?:
// 字段新增时默认值 | (({ record, column, rowIndex }) => VNodeChild | JSX.Element)
addDefaultValue?: number | string | boolean | undefined | ((record) => number | string | boolean | undefined) | VNodeChild
// 字段编辑时默认值 | JSX.Element;
editDefaultValue?: number | string | boolean | undefined | ((record) => number | string | boolean | undefined) // 字段新增时默认值
// select,radio,treeSelect,下拉字典配置 addDefaultValue?:
dict?: ColumnDict | number
// 继承公用配置 | string
common?: boolean | boolean
// select 和 tree-select 组件是否开启虚拟列表 | undefined
virtualList?: boolean | ((record) => number | string | boolean | undefined);
// 搜索默认值 // 字段编辑时默认值
searchDefaultValue?: number | string | undefined editDefaultValue?:
// 搜索描述 | number
searchPlaceholder?: string | string
//编辑|创建 通用规则 | boolean
commonRules?: FieldRule | FieldRule[] | undefined
// 创建时规则 | ((record) => number | string | boolean | undefined);
addRules?: FieldRule | FieldRule[] // select,radio,treeSelect,下拉字典配置
// 编辑时规则 dict?: ColumnDict;
editRules?: FieldRule | FieldRule[] // 继承公用配置
// 子表单数据 common?: boolean;
children?: BasicColumn[] // select 和 tree-select 组件是否开启虚拟列表
virtualList?: boolean;
// 搜索默认值
searchDefaultValue?: number | string | undefined;
// 搜索描述
searchPlaceholder?: string;
// 表格是否快捷编辑,只支持 input date select
quickEdit?: boolean;
component?:Ref;
//编辑|创建 通用规则
commonRules?: FieldRule | FieldRule[];
// 创建时规则
addRules?: FieldRule | FieldRule[];
// 编辑时规则
editRules?: FieldRule | FieldRule[];
// 子表单数据
children?: BasicColumn[];
} }

View File

@@ -1,189 +1,189 @@
export interface BasicCrud { export interface BasicCrud {
// 表格接口 // 表格接口
api?: undefined | any api?: undefined | any;
// 主键名称 // 主键名称
pk?: string pk?: string;
// 设置选择列 // 设置选择列
rowSelection?: rowSelection?:
| undefined | undefined
| { | {
// 选择值的标识默认id // 选择值的标识默认id
key?: string key?: string;
// 选择列是否显示全选 // 选择列是否显示全选
showCheckedAll?: boolean showCheckedAll?: boolean;
// 行选择器类型 // 行选择器类型
type?: "checkbox" | "radio" type?: 'checkbox' | 'radio';
// 选择器列标题 // 选择器列标题
title?: string | "#" title?: string | '#';
// 列宽度 // 列宽度
width?: number | 60 width?: number | 60;
// 是否固定 // 是否固定
fixed?: boolean | false fixed?: boolean | false;
// 是否仅展示当前页的keys // 是否仅展示当前页的keys
onlyCurrent?: boolean | true onlyCurrent?: boolean | true;
} };
// 搜索label宽度 // 搜索label宽度
searchLabelWidth?: string | "auto" searchLabelWidth?: string | "auto";
// 搜索label对齐方式 // 搜索label对齐方式
searchLabelAlign?: "center" | "right" | "left" searchLabelAlign?: "center" | "right" | "left";
// 一行多少列 // 一行多少列
searchLabelCols?: number searchLabelCols?: number;
// 是否显示边框 // 是否显示边框
bordered?: { wrapper?: boolean; cell?: boolean } bordered?: { wrapper?: boolean; cell?: boolean };
// 是否开启拖拽排序 // 是否开启拖拽排序
dragSort?: boolean dragSort?: boolean;
// 子节点为空隐藏节点按钮 // 子节点为空隐藏节点按钮
hideExpandButtonOnEmpty?: boolean hideExpandButtonOnEmpty?: boolean;
// 默认展开所有行 // 默认展开所有行
expandAllRows?: boolean expandAllRows?: boolean;
// 斑马线 // 斑马线
stripe?: boolean stripe?: boolean;
// 新增、编辑、删除完成后是否刷新表格 // 新增、编辑、删除完成后是否刷新表格
dataCompleteRefresh?: boolean dataCompleteRefresh?: boolean;
// 表格大小 // 表格大小
size?: "mini" | "small" | "medium" | "large" size?: "mini" | "small" | "medium" | "large";
// 是否开启双击编辑数据 // 是否开启双击编辑数据
isDbClickEdit?: boolean isDbClickEdit?: boolean;
// 是否显示展开/折叠按钮 // 是否显示展开/折叠按钮
isExpand?: boolean isExpand?: boolean;
// 是否显示自定义 // 是否显示自定义
showExpandRow?: boolean showExpandRow?: boolean;
// 是否显示总结行 // 是否显示总结行
showSummary?: boolean showSummary?: boolean;
// 自定义总结行,要传入函数 // 自定义总结行,要传入函数
customerSummary?: boolean customerSummary?: boolean;
// 是否显示工具栏 // 是否显示工具栏
showTools?: boolean showTools?: boolean;
// 新增和编辑显示设置 // 新增和编辑显示设置
formOption?: { formOption?: {
// 显示方式支持模态框和抽屉?: modal drawer // 显示方式支持模态框和抽屉?: modal drawer
viewType?: "modal" | "drawer" viewType?: "modal" | "drawer";
// 显示宽度 // 显示宽度
width?: number width?: number;
// 是否全屏只有modal有效 // 是否全屏只有modal有效
isFull?: boolean isFull?: boolean;
} };
//新增确定之前修改form值 //新增确定之前修改form值
beforeAdd?: (form) => void beforeAdd?: (form) => void;
//新增确定之后调用,返回接口response和form值 //新增确定之后调用,返回接口response和form值
afterAdd?: (response, form) => void afterAdd?: (response, form) => void;
//编辑确定之前修改form值 //编辑确定之前修改form值
beforeEdit?: (form) => void beforeEdit?: (form) => void;
//编辑确定之后调用,返回接口response和form值 //编辑确定之后调用,返回接口response和form值
afterEdit?: (response, form) => void afterEdit?: (response, form) => void;
add?: { add?: {
// 新增api // 新增api
api?: undefined | any api?: undefined | any;
// 显示新增按钮的权限 // 显示新增按钮的权限
auth?: string[] auth?: string[];
// 显示新增按钮的角色 // 显示新增按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
} };
edit?: { edit?: {
// 编辑api // 编辑api
api?: undefined | any api?: undefined | any;
// 显示编辑按钮的权限 // 显示编辑按钮的权限
auth?: string[] auth?: string[];
// 显示编辑按钮的角色 // 显示编辑按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
} };
delete?: { delete?: {
// 删除api // 删除api
api?: undefined | any api?: undefined | any;
// 显示删除按钮的权限 // 显示删除按钮的权限
auth?: string[] auth?: string[];
// 显示删除按钮的角色 // 显示删除按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 真实删除api // 真实删除api
realApi?: undefined | any realApi?: undefined | any;
// 显示真实删除按钮的权限 // 显示真实删除按钮的权限
realAuth?: string[] realAuth?: string[];
// 显示真实删除按钮的角色 // 显示真实删除按钮的角色
realRole?: string[] realRole?: string[];
// 真实按钮文案 // 真实按钮文案
realText?: string realText?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
} };
// Todo // Todo
recycleApi?: any recycleApi?: any;
recovery?: { recovery?: {
// 显示恢复按钮的权限 // 显示恢复按钮的权限
auth?: string[] auth?: string[];
// 显示恢复按钮的角色 // 显示恢复按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
// 恢复列表查询api // 恢复列表查询api
api?: undefined | any api?: undefined | any;
} };
// see?: { // see?: {
// // 显示查看按钮的权限 // // 显示查看按钮的权限
// auth?: string[] // auth?: string[]
// // 显示查看按钮的角色 // // 显示查看按钮的角色
// role?: string[] // role?: string[]
// // 按钮文案 // // 按钮文案
// text?: string // text?: string
// // 是否显示 // // 是否显示
// show?: boolean // show?: boolean
// } // }
import?: { import?: {
// 导入url // 导入url
url?: undefined | any url?: undefined | any;
// 下载模板地址 // 下载模板地址
templateUrl?: undefined | any templateUrl?: undefined | any;
// 显示导入按钮的权限 // 显示导入按钮的权限
auth?: string[] auth?: string[];
// 显示导入按钮的角色 // 显示导入按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
} };
export?: { export?: {
// 导出url // 导出url
url?: undefined | any url?: undefined | any;
// 显示导出按钮的权限 // 显示导出按钮的权限
auth?: string[] auth?: string[];
// 显示导出按钮的角色 // 显示导出按钮的角色
role?: string[] role?: string[];
// 按钮文案 // 按钮文案
text?: string text?: string;
// 是否显示 // 是否显示
show?: boolean show?: boolean;
} };
// 是否显示索引列 // 是否显示索引列
showIndex?: boolean showIndex?: boolean;
// 索引列名称 // 索引列名称
indexLabel?: string indexLabel?: string;
// 设置请求数据label // 设置请求数据label
requestParamsLabel?: undefined requestParamsLabel?: undefined;
// 表格滚动默认宽高 // 表格滚动默认宽高
scroll?: { scroll?: {
x?: string x?: string;
y?: string y?: string;
} };
// 调整列宽 // 调整列宽
resizable?: boolean resizable?: boolean;
// 是否显示操作列 // 是否显示操作列
operationColumn?: boolean operationColumn?: boolean;
// 操作列宽度 // 操作列宽度
operationWidth?: number operationWidth?: number;
// 操作列名称 // 操作列名称
operationColumnText?: string operationColumnText?: string;
} }

View File

@@ -1,20 +1,23 @@
<template> <template>
<a-modal <a-modal
:width="prop.width"
:fullscreen="isFull"
v-model:visible="modal.visible" v-model:visible="modal.visible"
:on-before-ok="modal.submit" :on-before-ok="modal.submit"
unmount-on-close unmount-on-close
@cancel="modal.cancel" @cancel="modal.cancel"
:width="width"
draggable
:on-before-cancel="modal.customCancel"
v-bind="$attrs"
> >
<template #title> <template #title>
{{ prop.title }} {{ prop.title }}
</template> </template>
<slot name="body"></slot> <slot name="body"></slot>
<ma-form ref="maFormRef" :columns="prop.column" v-model="form" :options="{ ...options, showButtons: false }"> <a-space v-if="prop.infoColumns.length">
<template #[`inputPrepend-version`]>V </template> <ma-info :columns="prop.infoColumns" :data="prop.infoData" />
</a-space>
<ma-form ref="maFormRef" :columns="formColumns" v-model="form" :options="{ ...options, showButtons: false }">
<template v-for="(value, key) in $slots" #[key]="slotProps" :key="key">
<slot :name="key" v-bind="slotProps"></slot>
</template>
</ma-form> </ma-form>
</a-modal> </a-modal>
</template> </template>
@@ -28,29 +31,50 @@
import { reactive, ref, watch } from "vue" import { reactive, ref, watch } from "vue"
import MaForm from "@/components/ma-form/index.vue" import MaForm from "@/components/ma-form/index.vue"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import MaInfo from "@/components/ma-info/index.vue"
import { setModalSizeEvent } from "@/utils/common"
const emit = defineEmits(["visible", "validateError", "open", "cancel", "close"]) const emit = defineEmits(["visible", "validateError", "open", "cancel", "close"])
const form = ref({}) const form = ref({})
const formColumns = ref([])
const prop = defineProps({ const prop = defineProps({
width: { type: Number, default: 1200 }, // modal框大小
isFull: { type: Boolean, default: false }, // 是否全屏
title: { type: String, default: "" }, // 弹出框标题 title: { type: String, default: "" }, // 弹出框标题
column: { type: Array, default: [] }, // ma-form字段 column: { type: Array, default: [] }, // ma-form字段
columns: { type: Array, default: [] }, // ma-form字段 别名
infoColumns: { type: Array, default: [] },
infoData: { type: Object, default: {} },
default_visible: { type: Boolean, default: false }, // 默认隐藏 default_visible: { type: Boolean, default: false }, // 默认隐藏
options: { type: Object, default: {} }, // ma-form 属性 options: { type: Object, default: {} }, // ma-form 属性
submit: { type: Function, default: () => {} }, submit: { type: Function, default: () => {} }
width: { type: String, default: "1000" + "px" },
// 自定义异步取消参数
customCancel: { type: Function, default: null }
}) })
const isFull = ref(prop.isFull)
setModalSizeEvent((config) => {
isFull.value = config.isFull
})
formColumns.value = [...prop.column, ...prop.columns]
let submitEvent = prop.submit
const maFormRef = ref() const maFormRef = ref()
const modal = reactive({ const modal = reactive({
visible: prop.default_visible, visible: prop.default_visible,
open(data) { open(formData, infoData = {}) {
modal.visible = true modal.visible = true
for (let [key, value] of Object.entries(data)) { for (let [key, value] of Object.entries(formData)) {
form.value[key] = value form.value[key] = value
} }
emit("open", data) for (let [key, value] of Object.entries(infoData)) {
prop.infoData[key] = value
}
emit("open", formData, infoData)
},
onSubmit(func) {
submitEvent = func
return this
}, },
close() { close() {
modal.visible = false modal.visible = false
@@ -61,13 +85,7 @@ const modal = reactive({
emit("validateError", validate) emit("validateError", validate)
return false return false
} }
return prop.submit(form._rawValue) return submitEvent(form._rawValue)
},
customCancel() {
if (prop.customCancel) {
return prop.customCancel()
}
return true
}, },
cancel() { cancel() {
emit("cancel") emit("cancel")
@@ -82,11 +100,17 @@ watch(
{ immediate: true } { immediate: true }
) )
const getAttr = (key) => {
return form.value[key]
}
defineExpose({ defineExpose({
open: modal.open, open: modal.open,
close: modal.close, close: modal.close,
form: form, form: form,
formRef: maFormRef getAttr,
formRef: maFormRef,
onSubmit: modal.onSubmit
}) })
</script> </script>

View File

@@ -1,15 +1,13 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-card <a-card
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="
(typeof props.component?.display == 'undefined' || props.component?.display === true) &&
(hasDisplayTrue(props.component?.formList ?? []) || props.component?.forceShow)
"
:class="[props.component?.customClass]" :class="[props.component?.customClass]"
:extra="props.component?.extra" :extra="props.component?.extra"
:bordered="props.component?.bordered" :bordered="props.component?.bordered"
@@ -42,13 +40,22 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { getComponentName } from "../js/utils.js" import { getComponentName } from "../js/utils.js"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
})
const hasDisplayTrue = (list) => {
return list.some((item) => item.display ?? true)
}
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<a-form-item <a-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -55,7 +59,6 @@
</template> </template>
<template v-for="(component, componentIndex) in viewFormList[itemIndex]" :key="componentIndex"> <template v-for="(component, componentIndex) in viewFormList[itemIndex]" :key="componentIndex">
<component <component
style="line-height:32px;"
v-if="!containerItems.includes(component.formType)" v-if="!containerItems.includes(component.formType)"
:is="getComponentName(component?.formType ?? 'input')" :is="getComponentName(component?.formType ?? 'input')"
:component="component" :component="component"
@@ -69,69 +72,88 @@
</a-collapse-item> </a-collapse-item>
</a-collapse> </a-collapse>
<a-table v-else class="w-full" :data="formModel[props.component.dataIndex]" :pagination="false" bordered stripe> <div v-else class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
<template #columns id="children-columns"> <div class="arco-table-container">
<!-- 新增删除列 --> <table class="arco-table-element" cellpadding="0" cellspacing="0">
<a-table-column :width="60" fixed="left"> <thead>
<template #title> <tr class="arco-table-tr">
<a-button type="primary" @click="addItem()" size="small" shape="round"> <th class="arco-table-th" width="60">
<template #icon> <span class="arco-table-cell arco-table-cell-align-center">
<icon-plus /> <a-button type="primary" @click="addItem()" size="small" shape="round">
<template #icon>
<icon-plus />
</template>
</a-button>
</span>
</th>
<th class="arco-table-th" :width="60">
<span class="arco-table-cell arco-table-cell-align-center">
<span class="arco-table-th-title">序号</span>
</span>
</th>
<template v-for="component in viewFormList[0]">
<th class="arco-table-th" :width="component.width">
<span class="arco-table-cell arco-table-cell-align-center">
<span class="arco-table-th-title">{{ component.title }}</span>
</span>
</th>
</template> </template>
</a-button> </tr>
</template> </thead>
<template #cell="{ rowIndex }"> <tbody>
<a-button <template v-for="(item, index) in formModel[props.component.dataIndex]">
type="primary" <tr class="arco-table-tr">
status="danger" <td class="arco-table-td">
size="small" <span class="arco-table-cell">
shape="round" <a-button
:disabled="formModel[props.component.dataIndex].length === 1" type="primary"
@click="deleteItem(rowIndex)" status="danger"
> size="small"
<template #icon><icon-minus /></template> shape="round"
</a-button> :disabled="formModel[props.component.dataIndex].length === 1"
</template> @click="deleteItem(index)"
</a-table-column> >
<template #icon><icon-minus /></template>
<a-table-column :width="60" fixed="left"> </a-button>
<template #title>序号</template> </span>
<template #cell="{ rowIndex }"> {{ rowIndex + 1 }} </template> </td>
</a-table-column> <td class="arco-table-td">
<span class="arco-table-cell">
<template v-for="(component, itemIndex) in viewFormList[0]" :key="itemIndex"> <span class="arco-table-td-content">{{ index + 1 }}</span>
<a-table-column </span>
:width="component.width" </td>
:title="component.title ?? '未命名'" <template v-for="component in viewFormList[index]">
:align="component.align || 'left'" <td class="arco-table-td">
:fixed="component.fixed" {{ (component.hideLabel = true ? "" : "") }}
> <span class="arco-table-cell">
<template #cell="{ rowIndex }"> <component
<component v-if="!containerItems.includes(component.formType)"
v-if="!containerItems.includes(component.formType)" :is="getComponentName(component.formType ?? 'input')"
:is="getComponentName(component.formType ?? 'input')" :component="component"
:component="component" :customField="getChildrenDataIndex(index, component.dataIndex)"
:customField="getChildrenDataIndex(rowIndex, component.dataIndex)" >
> <template v-for="slot in Object.keys($slots)" #[slot]="component">
<template v-for="slot in Object.keys($slots)" #[slot]="component"> <slot :name="slot" v-bind="component" />
<slot :name="slot" v-bind="component" /> </template>
</component>
</span>
</td>
</template> </template>
</component> </tr>
</template> </template>
</a-table-column> </tbody>
</template> </table>
</template> </div>
</a-table> </div>
</a-form-item> </a-form-item>
</template> </template>
<script setup> <script setup>
import { ref, inject, provide, onMounted, watch, nextTick, shallowRef, isRef } from "vue" import { ref, inject, onMounted, watch, nextTick } from "vue"
import { cloneDeep, get, isArray, isUndefined, set } from "lodash" import { cloneDeep, isArray, isUndefined } from "lodash-es"
import { getComponentName, containerItems } from "../js/utils.js" import { getComponentName, containerItems } from "../js/utils.js"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
import { loadDict, handlerCascader } from "../js/networkRequest.js" import { loadDict } from "../js/networkRequest.js"
import arrayComponentDefault from "../js/defaultArrayComponent.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
const formList = props.component.formList const formList = props.component.formList
@@ -139,6 +161,11 @@ const viewFormList = ref([])
const options = inject("options") const options = inject("options")
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const dictList = inject("dictList")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const defaultOpenKeys = [0] const defaultOpenKeys = [0]
if (!formModel.value[props.component.dataIndex]) { if (!formModel.value[props.component.dataIndex]) {
@@ -160,7 +187,7 @@ watch(
value[index] = Object.fromEntries(data) value[index] = Object.fromEntries(data)
} }
viewFormList.value[index] = cloneDeep(formList) viewFormList.value[index] = cloneDeep(formList)
maEvent.customeEvent(props.component, { formList: viewFormList.value[index], data, index }, "onAdd") rv("onAdd", { formList: viewFormList.value[index], data, index })
}) })
} }
}, },
@@ -173,23 +200,21 @@ if (props.component.type == "table") {
formList.map((item) => { formList.map((item) => {
item["hideLabel"] = true item["hideLabel"] = true
}) })
} else {
formModel.value[props.component.dataIndex].map((item, index) => {
if (index > 0) defaultOpenKeys.push(index)
})
} }
// 默认不展开所有的collapse
// else {
// formModel.value[props.component.dataIndex].map((item, index) => {
// if (index > 0) defaultOpenKeys.push(index)
// })
// }
const addItem = async (data = {}) => { const addItem = async (data = {}) => {
let index = formModel.value[props.component.dataIndex].length let index = formModel.value[props.component.dataIndex].length
viewFormList.value[index] = cloneDeep(formList) viewFormList.value[index] = cloneDeep(formList)
maEvent.customeEvent(props.component, { formList: viewFormList.value[index], data, index: index }, "onAdd") rv("onAdd", { formList: viewFormList.value[index], data, index })
formModel.value[props.component.dataIndex].push(data) formModel.value[props.component.dataIndex].push(data)
} }
const deleteItem = async (index) => { const deleteItem = async (index) => {
let res = await maEvent.customeEvent(props.component, { index }, "onDelete") let res = await rv("onDelete", { index })
if (isUndefined(res) || res === true) { if (isUndefined(res) || res === true) {
viewFormList.value.splice(index, 1) viewFormList.value.splice(index, 1)
await nextTick() await nextTick()
@@ -201,14 +226,14 @@ const getChildrenDataIndex = (index, dataIndex) => {
return [props.component.dataIndex, index, dataIndex].join(".") return [props.component.dataIndex, index, dataIndex].join(".")
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(async () => { onMounted(async () => {
if (formModel.value[props.component.dataIndex].length === 0) { if (formModel.value[props.component.dataIndex].length === 0) {
for (let i = 0; i < (props.component.emptyRow ?? 1); i++) { for (let i = 0; i < (props.component.emptyRow ?? 1); i++) {
await addItem() await addItem()
} }
} }
maEvent.handleCommonEvent(props.component, "onMounted") rv("onMounted")
}) })
</script> </script>

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-col <a-col
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="[props.component?.customClass]" :class="[props.component?.customClass]"
:span="props.component?.span ?? 12" :span="props.component?.span ?? 12"
:offset="props.component?.offset" :offset="props.component?.offset"

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<div <div
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="['grid-responsive-padding', props.component?.customClass]" :class="['grid-responsive-padding', props.component?.customClass]"
:style="props.component?.style" :style="props.component?.style"
:span="props.component?.span ?? 12" :span="props.component?.span ?? 12"

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<div <div
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="[gridClass, props.component?.customClass]" :class="[gridClass, props.component?.customClass]"
:style="props.component?.style" :style="props.component?.style"
> >
@@ -24,16 +19,20 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue" import { ref, onMounted, inject } from "vue"
import MaGridTailwindCol from "./grid-tailwind-col.vue" import MaGridTailwindCol from "./grid-tailwind-col.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
const gridClass = ref(["ma-grid", "grid", "lg:grid-cols-" + props.component?.colNumber ?? 1]) const gridClass = ref(["ma-grid", "grid", "lg:grid-cols-" + props.component?.colNumber ?? 1])
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-row <a-row
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="[props.component?.customClass]" :class="[props.component?.customClass]"
:gutter="props.component?.gutter" :gutter="props.component?.gutter"
:justify="props.component?.justify" :justify="props.component?.justify"
@@ -28,13 +23,17 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import MaGridCol from "./grid-col.vue" import MaGridCol from "./grid-col.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,6 +1,10 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<td <td
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="['table-cell', props.component?.customClass]" :class="['table-cell', props.component?.customClass]"
:style="props.component?.style" :style="props.component?.style"
:colspan="props.component.colSpan" :colspan="props.component.colSpan"

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<table <table
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="['table-container', props.component?.customClass]" :class="['table-container', props.component?.customClass]"
:style="props.component?.style" :style="props.component?.style"
> >
@@ -33,15 +28,19 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import MaTableCell from "./table-cell.vue" import MaTableCell from "./table-cell.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>
<style lang="less"> <style lang="less">

View File

@@ -1,15 +1,10 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-tabs <a-tabs
v-show="typeof props.component?.display == 'undefined' || props.component?.display === true" v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
:class="[props.component?.customClass]" :class="[props.component?.customClass]"
:trigger="props.component?.trigger" :trigger="props.component?.trigger"
:position="props.component?.position" :position="props.component?.position"
@@ -23,10 +18,10 @@
:hide-content="props.component?.hideContent" :hide-content="props.component?.hideContent"
:lazy-load="props.component?.lazyLoad" :lazy-load="props.component?.lazyLoad"
:destroy-on-hide="props.component?.destroyOnHide" :destroy-on-hide="props.component?.destroyOnHide"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@tab-click="maEvent.handleTabClickEvent(props.component, $event)" @tab-click="rv('onTabClick', $event)"
@add="maEvent.handleTabAddEvent(props.component)" @add="tabAddEvent(props.component, { formModel, getColumnService, columns })"
@delete="maEvent.handleTabDeleteEvent(props.component, $event)" @delete="tabDeleteEvent(props.component, $event, { formModel, getColumnService, columns })"
> >
<template #extra> <template #extra>
<slot :name="`tabExtra-${props.component?.dataIndex ?? ''}`"></slot> <slot :name="`tabExtra-${props.component?.dataIndex ?? ''}`"></slot>
@@ -54,13 +49,17 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { getComponentName } from "../js/utils.js" import { getComponentName } from "../js/utils.js"
import { maEvent } from "../js/formItemMixin.js" import { runEvent, tabAddEvent, tabDeleteEvent } from "../js/event.js"
const props = defineProps({ component: Object }) const props = defineProps({ component: Object })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -24,10 +19,12 @@
:strict="props.component.strict" :strict="props.component.strict"
:filter-option="props.component.filterOption" :filter-option="props.component.filterOption"
:allow-clear="props.component.allowClear ?? true" :allow-clear="props.component.allowClear ?? true"
@change="maEvent.customeEvent(props.component, $event, 'onChange')" @change="rv('onChange', $event)"
@search="maEvent.customeEvent(props.component, $event, 'onSearch')" @search="rv('onSearch', $event)"
@select="maEvent.customeEvent(props.component, $event, 'onSelect')" @select="rv('onSelect', $event)"
@clear="maEvent.customeEvent(props.component, $event, 'onClear')" @clear="rv('onClear')"
@dropdown-scroll="rv('onDropdownScroll')"
@dropdown-reach-bottom="rv('onDropdownReachBottom')"
> >
<slot :name="`autoCompleteFooter-${props.component.dataIndex}`"></slot> <slot :name="`autoCompleteFooter-${props.component.dataIndex}`"></slot>
</a-auto-complete> </a-auto-complete>
@@ -37,15 +34,19 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index, "")) const value = ref(get(formModel.value, index, ""))
@@ -61,8 +62,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,43 +1,44 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <div>
<a-button <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
:type="props.component.type" <a-button
:status="props.component.status" :type="props.component.type"
:size="props.component.size" :status="props.component.status"
:shape="props.component.shape" :size="props.component.size"
:disabled="props.component.disabled" :shape="props.component.shape"
:long="props.component.long" :disabled="props.component.disabled"
:loading="props.component.loading" :long="props.component.long"
:html-type="props.component.htmlType" :loading="props.component.loading"
:href="props.component.href" :html-type="props.component.htmlType"
@click="maEvent.handleCommonEvent(props.component, 'onClick')" :href="props.component.href"
> @click="rv('onClick')"
<template #icon v-if="props.component.icon"> >
<component :is="props.component.icon" /> <template #icon v-if="props.component.icon">
</template> <component :is="props.component.icon" />
{{ props.component.title ?? "button" }} </template>
</a-button> {{ props.component.title ?? "button" }}
</slot> </a-button>
</slot>
</div>
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -45,13 +40,13 @@
:value-key="props.component.valueKey" :value-key="props.component.valueKey"
:fallback="props.component.fallback" :fallback="props.component.fallback"
:expand-child="props.component.expandChild" :expand-child="props.component.expandChild"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@input-value-change="maEvent.handleInputEvent(props.component, $event)" @search="rv('onSearch', $event)"
@popup-visible-change="maEvent.customeEvent(props.component, $event, 'onPopupVisibleChange')" @input-value-change="rv('onInputValueChange', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @popup-visible-change="rv('onPopupVisibleChange', $event)"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @clear="rv('onClear')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @focus="rv('onFocus')"
@search="maEvent.customeEvent(props.component, $event, 'onSearch')" @blur="rv('onBlur')"
> >
</a-cascader> </a-cascader>
</slot> </slot>
@@ -60,9 +55,9 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -71,6 +66,10 @@ const props = defineProps({
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const dictList = inject("dictList")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
@@ -99,8 +98,6 @@ if (
value.value = value.value + "" value.value = value.value + ""
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -20,7 +15,7 @@
:max="props.component.max" :max="props.component.max"
:direction="props.component.direction" :direction="props.component.direction"
:disabled="props.component.disabled" :disabled="props.component.disabled"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
> >
<template v-for="(item, index) in dictList[dictIndex] ?? []"> <template v-for="(item, index) in dictList[dictIndex] ?? []">
<a-checkbox :value="item.value" :disabled="item.disabled" :indeterminate="item.indeterminate">{{ <a-checkbox :value="item.value" :disabled="item.disabled" :indeterminate="item.indeterminate">{{
@@ -34,9 +29,9 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -45,6 +40,10 @@ const props = defineProps({
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const dictList = inject("dictList")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
@@ -63,8 +62,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -22,10 +17,10 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaCityLinkage from "@/components/ma-cityLinkage/index.vue" import MaCityLinkage from "@/components/ma-cityLinkage/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -33,6 +28,10 @@ const props = defineProps({
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -48,8 +47,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -21,10 +16,10 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaColorPicker from "@/components/ma-colorPicker/index.vue" import MaColorPicker from "@/components/ma-colorPicker/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -32,6 +27,9 @@ const props = defineProps({
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList")
const columns = inject("columns")
const rv = async (ev, value = undefined) => await runEvent(props.component, ev, value, { formModel, dictList, columns })
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -47,8 +45,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -20,16 +15,22 @@
</template> </template>
<script setup> <script setup>
import { onMounted, getCurrentInstance, watch } from "vue" import { onMounted, getCurrentInstance, watch, inject } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const app = getCurrentInstance().appContext.app const app = getCurrentInstance().appContext.app
if ( if (
@@ -40,8 +41,8 @@ if (
app.component(props.component.dataIndex, props.component.component) app.component(props.component.dataIndex, props.component.component)
} }
maEvent.handleCommonEvent(props.component, "onCreated") runEvent("onCreated", "handleCommonEvent")
onMounted(() => { onMounted(() => {
maEvent.handleCommonEvent(props.component, "onMounted") runEvent("onMounted", "handleCommonEvent")
}) })
</script> </script>

View File

@@ -0,0 +1,85 @@
<!--
- @Author XXX
- @Link XXX
-->
<template>
<maDialog v-model:visible="visible" :footer="false" />
</template>
<script setup>
import { onMounted, inject, ref, h, computed } from "vue"
import { runEvent } from "../js/event.js"
import { Modal, Drawer } from "@arco-design/web-vue"
import MaForm from "@/components/ma-form/index.vue"
import { isFunction } from "lodash-es"
const props = defineProps({
component: Object
})
const visible = ref(false)
const openDialog = () => (visible.value = true)
const getDataIndex = () => props.component?.dataIndex
const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const form = computed({
get() {
return formModel.value[getDataIndex()]
},
set(newVal) {
formModel.value[getDataIndex()] = newVal
}
})
const maDialog = (p, ctx) => {
const componentProps = { formList: {}, options: {} }
const evs = {}
Object.keys(props.component).map((key) => {
if (!/^on[A-Za-z]+/g.test(key)) {
componentProps[key] = props.component[key]
} else {
if (isFunction(props.component[key])) {
evs[key] = function () {
const argsList = Array.prototype.slice.call(arguments)
props.component[key](...argsList, { formModel, getColumnService, columns })
}
} else {
evs[key] = function () {
const argsList = Array.prototype.slice.call(arguments)
rv(key, { ...argsList })
}
}
}
})
return h(
componentProps?.type === "drawer" ? Drawer : Modal,
{
...Object.assign(componentProps, p),
...evs
},
{
default: () =>
h(
MaForm,
{
columns: componentProps.formList,
options: Object.assign(componentProps.options),
modelValue: form.value,
onSubmit: async (data, done) => await rv("onSubmit", { data, done })
},
componentProps?.formSlot
),
...componentProps?.dialogSlot
}
)
}
rv("onCreated")
onMounted(() => rv("onMounted"))
defineExpose({ openDialog, getDataIndex })
</script>

View File

@@ -1,37 +1,38 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <div>
<a-divider <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
v-if="typeof props.component.display == 'undefined' || props.component.display === true" <a-divider
:class="[props.component.customClass]" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
:margin="props.component.margin" :class="[props.component.customClass]"
:direction="props.component.direction" :margin="props.component.margin"
:orientation="props.component.orientation" :direction="props.component.direction"
:type="props.component.type" :orientation="props.component.orientation"
:size="props.component.size" :type="props.component.type"
> :size="props.component.size"
{{ props.component?.title ?? "" }} >
</a-divider> {{ props.component?.title ?? "" }}
</slot> </a-divider>
</slot>
</div>
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -10,7 +14,7 @@
style="width: 100%" style="width: 100%"
:height="props.component.height" :height="props.component.height"
:id="props.component.id" :id="props.component.id"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
> >
</ma-editor> </ma-editor>
</slot> </slot>
@@ -19,16 +23,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaEditor from "@/components/ma-editor/index.vue" import MaEditor from "@/components/ma-editor/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -44,8 +52,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -21,17 +16,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaIconPicker from "@/components/ma-icon/index.vue" import MaIconPicker from "@/components/ma-icon/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -47,8 +45,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -6,7 +6,7 @@
> >
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
<a-input-number <a-input-number
v-model="value" v-model.trim="value"
:size="props.component.size" :size="props.component.size"
:allow-clear="props.component.allowClear ?? true" :allow-clear="props.component.allowClear ?? true"
:disabled="props.component.disabled" :disabled="props.component.disabled"
@@ -21,12 +21,18 @@
:formatter="props.component.formatter" :formatter="props.component.formatter"
:parser="props.component.parser" :parser="props.component.parser"
:model-event="props.component.modelEvent" :model-event="props.component.modelEvent"
@input="maEvent.handleInputEvent(props.component, $event)" @input="rv('onInput', $event)"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
> >
<template #prepend v-if="props.component.openPrepend">
<slot :name="`inputPrepend-${props.component.dataIndex}`" />
</template>
<template #append v-if="props.component.openAppend">
<slot :name="`inputAppend-${props.component.dataIndex}`" />
</template>
<template #suffix v-if="props.component.openSuffix"> <template #suffix v-if="props.component.openSuffix">
<slot :name="`inputSuffix-${props.component.dataIndex}`" /> <slot :name="`inputSuffix-${props.component.dataIndex}`" />
</template> </template>
@@ -40,18 +46,25 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set, toNumber, isNaN } from "lodash" import { get, set, toNumber, isNaN } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(toNumber(get(formModel.value, index))) const value = ref(toNumber(get(formModel.value, index)))
const rv = async (ev, value = undefined) => {
if (ev === "onChange") set(formModel.value, index, value)
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
}
watch( watch(
() => get(formModel.value, index), () => get(formModel.value, index),
(vl) => (value.value = toNumber(vl)) (vl) => (value.value = toNumber(vl))
@@ -65,8 +78,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -19,13 +23,13 @@
:format-tag="props.component.formatTag" :format-tag="props.component.formatTag"
:unique-value="props.component.uniqueValue" :unique-value="props.component.uniqueValue"
:field-names="props.component.fieldNames" :field-names="props.component.fieldNames"
@input-value-change="maEvent.customeEvent(props.component, $event, 'onInputValueChange')" @input-value-change="rv('onInputValueChange', $event)"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@remove="maEvent.customeEvent(props.component, $event, 'onRemove')" @remove="rv('onRemove', $event)"
@press-enter="maEvent.customeEvent(props.component, $event, 'onPressEnter')" @press-enter="rv('onPressEnter', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
> >
<template #suffix v-if="props.component.openSuffix"> <template #suffix v-if="props.component.openSuffix">
<slot :name="`inputSuffix-${props.component.dataIndex}`" /> <slot :name="`inputSuffix-${props.component.dataIndex}`" />
@@ -40,15 +44,19 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -64,8 +72,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -5,7 +9,6 @@
:custom-field="props.customField" :custom-field="props.customField"
> >
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
<!-- chen.xiugai-warning -->
<component <component
:is="getComponentName()" :is="getComponentName()"
v-model.trim="value" v-model.trim="value"
@@ -24,13 +27,13 @@
:search-button="props.component.searchButton" :search-button="props.component.searchButton"
:loading="props.component.invisibleButton" :loading="props.component.invisibleButton"
:button-text="props.component.buttonText" :button-text="props.component.buttonText"
@input="maEvent.handleInputEvent(props.component, $event)" @input="rv('onInput', $event)"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@press-enter="maEvent.handleCommonEvent(props.component, 'onPressEnter')" @press-enter="rv('onPressEnter')"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
@search="maEvent.handleInputSearchEvent(props.component, $event)" @search="rv('onSearch', $event)"
> >
<template #prepend v-if="props.component.openPrepend"> <template #prepend v-if="props.component.openPrepend">
<slot :name="`inputPrepend-${props.component.dataIndex}`" /> <slot :name="`inputPrepend-${props.component.dataIndex}`" />
@@ -51,17 +54,26 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) //后端传入数字类型导致报错 Invalid prop: type check failed for prop "modelValue". Expected String with value "0", got Number with value 0
const toVal = ref(`${get(formModel.value, index)}`)
const value = ref()
if (toVal.value != "undefined") {
value.value = toVal.value
}
watch( watch(
() => get(formModel.value, index), () => get(formModel.value, index),
@@ -87,8 +99,6 @@ const getComponentName = () => {
} }
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-form-item <a-form-item

View File

@@ -0,0 +1,109 @@
<!--
- @Author XXX
- @Link XXX
-->
<template>
<ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true"
:component="props.component"
:custom-field="props.customField"
>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
<a-table
class="w-full"
row-key="keys"
:pagination="false"
@change="handleChange"
:data="value"
:draggable="{ type: 'handle', width: 40 }"
>
<template #columns>
<a-table-column title="Key" align="center">
<template #cell="{ record }">
<a-input v-model="record.key" />
</template>
</a-table-column>
<a-table-column title="Value" align="center">
<template #cell="{ record }">
<a-input v-model="record.value" />
</template>
</a-table-column>
<a-table-column title="操作" align="center">
<template #cell="{ rowIndex }">
<a-space>
<a-button size="small" type="primary" @click="plus(rowIndex)"
><template #icon><icon-plus /></template
></a-button>
<a-button size="small" type="primary" @click="minus(rowIndex)"
><template #icon><icon-minus /></template
></a-button>
</a-space>
</template>
</a-table-column>
</template>
</a-table>
</slot>
</ma-form-item>
</template>
<script setup>
import { ref, inject, onMounted, watch } from "vue"
import { get, set, cloneDeep, isArray } from "lodash-es"
import MaFormItem from "./form-item.vue"
import { runEvent } from "../js/event.js"
import { Message } from "@arco-design/web-vue"
const props = defineProps({
component: Object,
customField: { type: String, default: undefined }
})
const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const dictList = inject("dictList")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
: props.component.dataIndex
const value = ref(get(formModel.value, index))
watch(
() => get(formModel.value, index),
(vl) => (value.value = vl)
)
watch(
() => value.value,
(v) => {
set(formModel.value, index, v)
index.indexOf(".") > -1 && delete formModel.value[index]
}
)
if (!isArray(value.value) || value.value.length === 0) {
value.value = [{ key: "", value: "" }]
}
const handleChange = (data) => {
value.value = data
}
const plus = (index) => {
value.value.splice(index + 1, 0, { key: "", value: "" })
}
const minus = (index) => {
if (value.value.length === 1) {
Message.info("最少要保留一个")
return false
}
const data = cloneDeep(value.value)
data.splice(index, 1)
value.value = cloneDeep(data)
}
rv("onCreated")
onMounted(() => rv("onMounted"))
</script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
@@ -15,7 +10,7 @@
:disabled="props.component.disabled" :disabled="props.component.disabled"
:loading="props.component.loading" :loading="props.component.loading"
:href="props.component.href" :href="props.component.href"
@click="maEvent.handleCommonEvent(props.component, 'onClick')" @click="rv('onClick')"
> >
<template #icon v-if="props.component.icon"> <template #icon v-if="props.component.icon">
<component :is="props.component.icon" /> <component :is="props.component.icon" />
@@ -26,14 +21,18 @@
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -26,12 +21,12 @@
:error="props.component.error" :error="props.component.error"
:placeholder="props.component.placeholder ?? `请输入${props.component.title}`" :placeholder="props.component.placeholder ?? `请输入${props.component.title}`"
:type="props.component.type" :type="props.component.type"
@input="maEvent.handleInputEvent(props.component, $event)" @input="rv('onInput', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
@search="maEvent.customeEvent(props.component, $event, 'onSearch')" @search="rv('onSearch', $event)"
@select="maEvent.customeEvent(props.component, $event, 'onSelect')" @select="rv('onSelect', $event)"
> >
</a-mention> </a-mention>
</slot> </slot>
@@ -40,15 +35,19 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -64,8 +63,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -19,8 +14,8 @@
v-model="value" v-model="value"
:placeholder=" :placeholder="
props.component.formType === 'range' props.component.formType === 'range'
? ['请选择开始时间', '请选择结束时间'] ? (props.component.placeholder ?? ['请选择开始时间', '请选择结束时间'])
: `请选择${props.component.title}` : (props.component.placeholder ?? `请选择${props.component.title}`)
" "
:hide-trigger="props.component.hideTrigger" :hide-trigger="props.component.hideTrigger"
:allow-clear="props.component.allowClear ?? true" :allow-clear="props.component.allowClear ?? true"
@@ -43,6 +38,7 @@
:show-time="props.component.showTime" :show-time="props.component.showTime"
:preview-shortcut="props.component.previewShortcut" :preview-shortcut="props.component.previewShortcut"
:show-confirm-btn="props.component.showConfirmBtn" :show-confirm-btn="props.component.showConfirmBtn"
:type="props.component.range ? (props.component.formType === 'time' ? 'time-range' : 'range') : ''"
:time-picker-props=" :time-picker-props="
props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {} props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}
" "
@@ -52,9 +48,9 @@
@change="handlePickerChangeEvent" @change="handlePickerChangeEvent"
@select="handlePickerSelectEvent" @select="handlePickerSelectEvent"
@ok="handlePickerOkEvent" @ok="handlePickerOkEvent"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@popup-visible-change="maEvent.customeEvent(props.component, $event, 'onvVisibleChange')" @popup-visible-change="rv('onvVisibleChange', $event)"
@select-shortcut="maEvent.customeEvent(props.component, $event, 'onSelectShortcut')" @select-shortcut="rv('onSelectShortcut', $event)"
@picker-value-change="handlePickerValueChangeEvent" @picker-value-change="handlePickerValueChangeEvent"
/> />
</slot> </slot>
@@ -63,15 +59,19 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -94,23 +94,21 @@ const getComponentName = () => {
} }
const handlePickerChangeEvent = (value, date, dateString) => { const handlePickerChangeEvent = (value, date, dateString) => {
maEvent.handleChangeEvent(props.component, { value, date, dateString }) rv("onPickerChange", { value, date, dateString })
} }
const handlePickerSelectEvent = (value, date, dateString) => { const handlePickerSelectEvent = (value, date, dateString) => {
maEvent.customeEvent(props.component, { value, date, dateString }, "onSelect") rv("onSelect", { value, date, dateString })
} }
const handlePickerValueChangeEvent = (value, date, dateString) => { const handlePickerValueChangeEvent = (value, date, dateString) => {
maEvent.customeEvent(props.component, { value, date, dateString }, "onPickerValueChange") rv("onPickerValueChange", { value, date, dateString })
} }
const handlePickerOkEvent = (value, date, dateString) => { const handlePickerOkEvent = (value, date, dateString) => {
maEvent.customeEvent(props.component, { value, date, dateString }, "onOk") rv("onOk", { value, date, dateString })
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -33,8 +28,8 @@
<script setup> <script setup>
import { ref, inject, onMounted, nextTick, watch } from "vue" import { ref, inject, onMounted, nextTick, watch } from "vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { get, set, isUndefined } from "lodash" import { get, set, isUndefined } from "lodash-es"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
import { handlerCascader } from "../js/networkRequest.js" import { handlerCascader } from "../js/networkRequest.js"
const props = defineProps({ const props = defineProps({
@@ -45,7 +40,10 @@ const props = defineProps({
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const dictList = inject("dictList")
const formLoading = inject("formLoading") const formLoading = inject("formLoading")
const getColumnService = inject("getColumnService")
const columns = inject("columns") const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
@@ -80,7 +78,7 @@ const handleCascaderChangeEvent = async (value) => {
const component = props.component const component = props.component
// 执行自定义事件 // 执行自定义事件
if (component.onChange) { if (component.onChange) {
maEvent.handleChangeEvent(component, value) rv("onChange", value)
} }
// 处理联动 // 处理联动
@@ -90,8 +88,6 @@ const handleCascaderChangeEvent = async (value) => {
nextTick(() => (formLoading.value = false)) nextTick(() => (formLoading.value = false))
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -24,8 +19,8 @@
:allow-half="props.component.allowHalf" :allow-half="props.component.allowHalf"
:grading="props.component.grading" :grading="props.component.grading"
:color="props.component.color" :color="props.component.color"
@change="maEvent.handleInputEvent(props.component, $event)" @change="rv('onChange', $event)"
@hover-change="maEvent.customeEvent(props.component, $event, 'onHoverChange')" @hover-change="rv('onHoverChange', $event)"
> >
</a-rate> </a-rate>
</slot> </slot>
@@ -34,15 +29,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -58,8 +58,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -34,17 +29,21 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaResource from "@/components/ma-resource/index.vue" import MaResource from "@/components/ma-resource/index.vue"
import MaResourceButton from "@/components/ma-resource/button.vue" import MaResourceButton from "@/components/ma-resource/button.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -64,8 +63,6 @@ if (props.component.multiple && !value.value) {
value.value = [] value.value = []
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -16,6 +11,7 @@
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
<a-select <a-select
v-model:model-value="value" v-model:model-value="value"
:options="props.component.data ?? dictList[dictIndex] ?? []"
:multiple="props.component.multiple" :multiple="props.component.multiple"
:size="props.component.size" :size="props.component.size"
:allow-clear="props.component.allowClear ?? true" :allow-clear="props.component.allowClear ?? true"
@@ -23,10 +19,10 @@
:readonly="props.component.readonly" :readonly="props.component.readonly"
:error="props.component.error" :error="props.component.error"
:placeholder="props.component.placeholder ?? `请选择${props.component.title}`" :placeholder="props.component.placeholder ?? `请选择${props.component.title}`"
:loading="props.component.loading" :loading="props.component.loading ?? loading"
:allow-search="props.component.allowSearch ?? true" :allow-search="props.component.allowSearch ?? true"
:allow-create="props.component.allowCreate" :allow-create="props.component.allowCreate"
:max-tag-count="props.component.maxTagCount" :max-tag-count="props.component.maxTagCount ?? 1"
:bordered="props.component.bordered" :bordered="props.component.bordered"
:unmount-on-close="props.component.unmountOnClose" :unmount-on-close="props.component.unmountOnClose"
:popup-container="props.component.popupContainer" :popup-container="props.component.popupContainer"
@@ -34,39 +30,100 @@
:virtual-list-props="props.component.virtualListProps" :virtual-list-props="props.component.virtualListProps"
:trigger-props="props.component.triggerProps" :trigger-props="props.component.triggerProps"
:format-label="props.component.formatLabel" :format-label="props.component.formatLabel"
:fallback-option="props.component.fallbackOption" :fallback-option="props.component.fallbackOption ?? handlerFallback"
:show-extra-options="props.component.showExtraOptions" :show-extra-options="props.component.showExtraOptions ?? false"
:value-key="props.component.valueKey" :value-key="props.component.valueKey"
:search-delay="props.component.searchDelay" :search-delay="props.component.searchDelay"
:limit="props.component.limit" :limit="props.component.limit"
:field-names="props.component.fieldNames" :field-names="props.component.fieldNames"
:scrollbar="props.component.scrollbar" :scrollbar="props.component.scrollbar"
@input-value-change="maEvent.handleInputEvent(props.component, $event)" @input-value-change="rv('onInputValueChange', $event)"
@change="handleCascaderChangeEvent($event)" @change="handleCascaderChangeEvent($event)"
@remove="maEvent.customeEvent(props.component, $event, 'onRemove')" @remove="rv('onRemove', $event)"
@popup-visible-change="maEvent.customeEvent(props.component, $event, 'onPopupVisibleChange')" @popup-visible-change="rv('onPopupVisibleChange', $event)"
@dropdown-scroll="maEvent.handleCommonEvent(props.component, 'onDropdownScroll')" @dropdown-scroll="rv('onDropdownScroll')"
@dropdown-reach-bottom="maEvent.handleCommonEvent(props.component, 'onDropdownReachBottom')" @dropdown-reach-bottom="rv('onDropdownReachBottom')"
@exceed-limit="maEvent.customeEvent(props.component, $event, 'onExceedLimit')" @exceed-limit="rv('onExceedLimit', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @search="rv('onSearch', $event)"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')"
@search="maEvent.customeEvent(props.component, $event, 'onSearch')"
> >
<template v-for="(item, index) in dictList[dictIndex] ?? []"> <template #header v-if="props.component.multiple && props.component.multipleTools">
<a-option :value="item.value" :disabled="item.disabled">{{ item.label }}</a-option>
</template>
<template #header v-if="props.component.multiple">
<div style="padding: 6px 12px"> <div style="padding: 6px 12px">
<a-space> <a-space fill>
<a-checkbox :value="false" @change="handleSelectAll">全选/清除</a-checkbox> <a-checkbox
<a-button size="mini" type="outline" @click="handleInverse">反选</a-button> v-if="
isBoolean(props.component.multipleTools) || props.component.multipleTools.selectAll
"
:model-value="checkedAll"
:indeterminate="indeterminate"
:disabled="loading"
@change="handleSelectAll"
>全选/清除</a-checkbox
>
<a-button
v-if="isBoolean(props.component.multipleTools) || props.component.multipleTools.inverse"
class="ml-2"
size="mini"
type="outline"
:disabled="loading"
@click="handleInverse"
>反选</a-button
>
<a-popover
:content-style="{ padding: '0px', width: '256px' }"
position="rt"
trigger="click"
v-if="
isBoolean(props.component.multipleTools) ||
props.component.multipleTools.showSelectAll
"
>
<a-button class="ml-2" size="mini">已选 {{ value.length }}</a-button>
<template #title>
<a-space fill style="padding: 12px 12px 8px 12px">
<a-button
:disabled="loading || !value.length"
size="mini"
status="danger"
@click="value = []"
>清空 {{ value.length }}</a-button
>
<a-input-search v-model="keyword" size="mini" allow-clear />
</a-space>
</template>
<template #content>
<a-scrollbar style="height: 200px; overflow: auto">
<a-checkbox-group
v-if="
(value.length && keyword === '') ||
Object.keys(filteredOptions).length > 0
"
direction="vertical"
v-model="value"
>
<div v-for="item in filteredOptions" class="select-all-options">
<a-checkbox :value="item.value">{{ item.label }}</a-checkbox>
</div>
</a-checkbox-group>
<a-empty v-else />
</a-scrollbar>
</template>
</a-popover>
</a-space> </a-space>
</div> </div>
</template> </template>
<template #footer v-if="props.component?.dict.pageOption ?? false"> <template #footer v-if="props.component?.dict?.openPage ?? false">
<div class="flex justify-center"> <div class="flex justify-center">
<a-pagination class="p-2" size="mini" :total="200" simple> <a-pagination
class="p-2"
size="mini"
:total="dataTotal"
:page-size="props.component.dict.pageOption.pageSize"
:disabled="loading"
simple
@change="handlePage"
>
<template #page-item-step="{ type }"> <template #page-item-step="{ type }">
<div>{{ type === "previous" ? "上一页" : "下一页" }}</div> <div>{{ type === "previous" ? "上一页" : "下一页" }}</div>
</template> </template>
@@ -79,27 +136,41 @@
</template> </template>
<script setup> <script setup>
import { ref, inject, onMounted, nextTick, watch } from "vue" import { ref, inject, onMounted, nextTick, watch, computed } from "vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { get, isUndefined, set, xor } from "lodash" import { get, isUndefined, set, xor, isObject, isBoolean } from "lodash-es"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
import { handlerCascader } from "../js/networkRequest.js" import { handlerCascader, loadDict } from "../js/networkRequest.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
if (isObject(props.component.dict)) {
props.component.dict.pageOption = {
page: 1,
pageSize: props.component?.dict?.pageOption?.pageSize ?? props.component?.dict?.pageSize ?? 15
}
}
const formModel = inject("formModel") const formModel = inject("formModel")
const dictList = inject("dictList") const dictList = inject("dictList")
const formLoading = inject("formLoading") const formLoading = inject("formLoading")
const columns = inject("columns") const columns = inject("columns")
const getColumnService = inject("getColumnService")
const rv = async (ev, value = "") =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
: props.component.dataIndex : props.component.dataIndex
const value = ref(get(formModel.value, index, "")) const value = ref(get(formModel.value, index, ""))
const dataTotal = ref(0)
const loading = ref(false)
const optionMap = ref({})
const keyword = ref("")
watch( watch(
() => get(formModel.value, index), () => get(formModel.value, index),
@@ -108,13 +179,57 @@ watch(
watch( watch(
() => value.value, () => value.value,
(v) => { (v) => {
if (props.component.multiple) {
v.forEach((k) => {
if (!optionMap.value[k]) {
optionMap.value[k] = dictList.value[dictIndex].find((i) => i.value === k)
}
})
for (const k in optionMap.value) {
if (!v.includes(k)) delete optionMap.value[k]
}
}
set(formModel.value, index, v) set(formModel.value, index, v)
index.indexOf(".") > -1 && delete formModel.value[index] index.indexOf(".") > -1 && delete formModel.value[index]
} }
) )
watch(
() => dictList.value[index],
async (v) => {
dataTotal.value = v?.pageInfo?.total || 0
}
)
const checkedAll = computed(() => {
const { multiple, multipleTools } = props.component
const currentDicts = dictList.value[dictIndex]
if (multiple && multipleTools && currentDicts) {
return currentDicts.every((item) => value.value.includes(item.value))
}
return false
})
const filteredOptions = computed(() => {
const { multiple, multipleTools } = props.component
if (multiple && multipleTools && keyword.value !== "") {
const lowerCaseKeyword = keyword.value.toLowerCase()
return Object.values(optionMap.value).filter((option) => option.label.toLowerCase().includes(lowerCaseKeyword))
}
return optionMap.value
})
const indeterminate = computed(() => {
if (props.component.multiple && props.component.multipleTools && checkedAll.value == false) {
const currentDicts = dictList.value[dictIndex]
return currentDicts.some((item) => value.value.includes(item.value))
}
return false
})
if (value.value === "") { if (value.value === "") {
value.value = undefined value.value = props.component.multiple === true ? [] : ""
} else if ( } else if (
!isUndefined(value.value) && !isUndefined(value.value) &&
props.component.dict && props.component.dict &&
@@ -124,14 +239,17 @@ if (value.value === "") {
value.value = value.value + "" value.value = value.value + ""
} }
if (isUndefined(props.component.multipleTools)) props.component.multipleTools = true
const handleSelectAll = (status) => { const handleSelectAll = (status) => {
if (isUndefined(value.value)) { if (isUndefined(value.value)) {
value.value = [] value.value = []
} }
if (status) { if (status) {
dictList.value[dictIndex].map((item) => { const currentSet = new Set(value.value)
value.value.push(item.value) const newValues = dictList.value[dictIndex]
}) .filter((item) => !currentSet.has(item.value))
.map((item) => item.value)
value.value = [...value.value, ...newValues]
} else { } else {
value.value = [] value.value = []
} }
@@ -143,15 +261,25 @@ const handleInverse = () => {
} }
const ids = [] const ids = []
dictList.value[dictIndex].map((item) => ids.push(item.value)) dictList.value[dictIndex].map((item) => ids.push(item.value))
value.value = xor(ids, value.value) value.value = xor(value.value, ids)
} }
const handlePage = async (page) => {
loading.value = true
props.component.dict.pageOption.page = page
await loadDict(dictList.value, props.component)
loading.value = false
}
const handlerFallback = (key) => {
return optionMap.value[key] || key
}
const handleCascaderChangeEvent = async (value) => { const handleCascaderChangeEvent = async (value) => {
formLoading.value = true formLoading.value = true
const component = props.component const component = props.component
// 执行自定义事件 // 执行自定义事件
if (component.onChange) { if (component.onChange) {
maEvent.handleChangeEvent(component, value) rv("onChange", value)
} }
// 处理联动 // 处理联动
@@ -161,8 +289,30 @@ const handleCascaderChangeEvent = async (value) => {
nextTick(() => (formLoading.value = false)) nextTick(() => (formLoading.value = false))
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => {})
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>
<style lang="less" scoped>
.arco-checkbox-group {
width: 100%;
.select-all-options {
width: 100%;
height: 36px;
padding: 0 15px 0 7px;
color: inherit;
background-color: transparent;
transition: all 0.1s cubic-bezier(0, 0, 1, 1);
::v-deep(.arco-checkbox .arco-checkbox-label) {
height: 36px;
line-height: 36px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&:hover {
color: var(--color-text-1);
background-color: var(--color-fill-2);
}
}
}
</style>

View File

@@ -0,0 +1,601 @@
<template>
<ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true"
:component="props.component"
:custom-field="props.customField"
>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
<div style="flex: 1 1 auto" class="arco-col arco-form-item-wrapper-col">
<div class="add-spec">
<a-button type="primary" :disabled="specification.length >= 5" @click="addSpec">添加规格</a-button>
</div>
<div style="margin-top: 10px; width: 100%; height: auto">
<div class="specification">
<ul class="spec-list" v-if="specification.length">
<li class="item" v-for="(item, index) in specification" :key="index">
<div class="name">
<a-input placeholder="输入产品规格" allow-clear v-model="item.name" />
<i class="icon el-icon-circle-close" @click="delSpec(index)"></i>
</div>
<div class="values" style="display: flex">
<div style="margin-bottom: 5px">
<span
class="el-tag"
v-for="(tag, num) in item.value"
:key="tag"
style="margin: 15px 5px"
>
<a-tag closable @close="delSpecTag(index, num)">{{ tag }}</a-tag>
</span>
</div>
<div class="add-attr" style="margin: 0 0 0 5px">
<div style="display: flex">
<a-input
style="width: 120px"
allow-clear
size="small"
v-model="addValues[index]"
placeholder="请输入规格值"
/>
<a-button
type="primary"
size="mini"
@click="addSpecTag(index)"
style="margin-left: 5px"
>确定</a-button
>
</div>
</div>
</div>
</li>
</ul>
</div>
<div>
<table class="stock-table">
<thead>
<tr>
<th v-for="(item, index) in specification" :key="index">
{{ item.name }}
</th>
<th style="width: 160px">规格编码</th>
<th>成本价</th>
<th>库存</th>
<th>销售价</th>
<th>是否启用</th>
</tr>
</thead>
<tbody v-if="specification[0] && specification[0].value.length">
<tr :key="index" v-for="(item, index) in countSum(0)">
<template v-for="(n, specIndex) in specification.length">
<td v-if="showTda(specIndex, index)" :key="n" :rowspan="countSum(n)">
{{ getSpecAttr(specIndex, index) }}
</td>
</template>
<td>
<a-input
type="text"
:disabled="!childProductArray[index].isUse"
v-model="childProductArray[index].childProductNo"
@blur="handleNo(index)"
placeholder="输入商品规格编号"
allow-clear
/>
</td>
<td>
<a-input
type="text"
v-model.number="childProductArray[index].childProductCost"
placeholder="输入成本价"
:disabled="!childProductArray[index].isUse"
allow-clear
/>
</td>
<td>
<a-input
type="text"
v-model.number="childProductArray[index].childProductStock"
placeholder="输入库存"
:disabled="!childProductArray[index].isUse"
allow-clear
/>
</td>
<td>
<a-input
type="text"
v-model.number="childProductArray[index].childProductPrice"
placeholder="输入销售价"
:disabled="!childProductArray[index].isUse"
allow-clear
/>
</td>
<td>
<a-switch
v-model="childProductArray[index].isUse"
size="small"
@change="
(val) => {
handleUserChange(index, val)
}
"
/>
</td>
</tr>
<tr>
<td colspan="8" class="wh-foot">
<span class="label">批量设置</span>
<ul class="set-list" v-if="isSetListShow">
<li class="set-item" @click="openBatch('childProductCost')">成本价</li>
<li class="set-item" @click="openBatch('childProductStock')">库存</li>
<li class="set-item" @click="openBatch('childProductPrice')">销售价</li>
</ul>
<div class="set-form" v-else>
<a-input-number
v-model="batchValue"
placeholder="输入要设置的数量"
class="input-demo"
:min="1"
:max="99999"
/>
<a-button
style="margin-right: 5px"
type="primary"
@click="setBatch"
size="mini"
>确定</a-button
>
<a-button type="primary" @click="cancelBatch" size="mini">取消</a-button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- <div class="example">-->
<!-- <h4 class="title">数据格式</h4>-->
<!-- <textarea class="code-area" :value="JSON.stringify(childProductArray)"></textarea>-->
<!-- </div>-->
</slot>
</ma-form-item>
</template>
<script setup>
// 引入相关vue必要的api
import { inject, onMounted, reactive, ref, watch } from "vue"
// 引入处理索引的函数
import { get, set } from "lodash-es"
// 引入 MaFormItem 组件
import MaFormItem from "./form-item.vue"
// 引入处理事件的函数
import { runEvent } from "../js/event.js"
// 组件都需要定义以下的props
const props = defineProps({
component: Object,
customField: { type: String, default: undefined }
})
// 该组件在form数据的索引名称
const index = props.customField ?? props.component.dataIndex
const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const value = ref(get(formModel.value, index))
// 规格
let specification = reactive([])
let addValues = reactive([])
let isSetListShow = ref(true)
let batchValue = ref(1)
let currentType = ref("")
let defaultProductNo = "P_"
let childProductArray = reactive([])
childProductArray = value.value && value.value.sku ? value.value.sku : reactive([])
specification = value.value && value.value.spec ? value.value.spec : reactive([])
onMounted(() => {
return childProductArray.map((item) => item.childProductSpec)
})
// 监听组件数据的改变
watch(
() => addValues,
(vl) => {
value.value = vl
let json = {
sku: childProductArray,
spec: specification
}
set(formModel.value, index, json)
},
{ immediate: true, deep: true }
)
watch(
() => specification,
(v) => {
let json = {
sku: childProductArray,
spec: specification
}
set(formModel.value, index, json)
},
{ immediate: true, deep: true }
)
watch(
() => childProductArray,
(v) => {
let json = {
sku: childProductArray,
spec: specification
}
set(formModel.value, index, json)
// set(formModel.value, index, v)
},
{ immediate: true, deep: true }
)
// 打开设置框
function openBatch(type) {
currentType.value = type
isSetListShow.value = false
}
// 批量设置
function setBatch() {
if (typeof batchValue.value === "string") {
alert("请输入正确的值")
return
}
childProductArray.forEach((item) => {
if (item.isUse) {
item[currentType.value] = batchValue.value
}
})
cancelBatch()
}
// 取消批量设置
function cancelBatch() {
batchValue.value = 1
currentType.value = ""
isSetListShow.value = true
}
// 监听规格启用操作
function handleUserChange(index, value) {
// 启用规格时,生成不重复的商品编号;关闭规格时,清空商品编号
if (value) {
childProductArray[index].childProductNo = makeProductNoNotRepet(index)
} else {
childProductArray[index].childProductNo = ""
}
}
// 监听商品编号的blur事件
function handleNo(index, attr) {
// 1.当用户输入完商品编号时,判断是否重复,如有重复,则提示客户并自动修改为不重复的商品编号
const value = childProductArray[index].childProductNo
let isRepet
childProductArray.forEach((item, i) => {
if (item.childProductNo === value && i !== index) {
isRepet = true
}
})
if (isRepet) {
alert("不允许输入重复的商品编号")
childProductArray[index].childProductNo = makeProductNoNotRepet(index)
// this.$set(this.childProductArray[index], 'childProductNo', this.)
}
}
// 生成不重复的商品编号
function makeProductNoNotRepet(index) {
let No
let i = index
let isRepet = true
while (isRepet) {
No = defaultProductNo + i
isRepet = isProductNoRepet(No)
i++
}
return No
}
// 商品编号判重
function isProductNoRepet(No) {
const result = childProductArray.findIndex((item) => {
return item.childProductNo === No
})
return result > -1
}
// 根据传入的条件来判断是否显示该td
function showTda(specIndex, index) {
// 如果当前项目下没有属性,则不显示
if (!specification[specIndex]) {
return false
// 自己悟一下吧
} else if (index % countSum(specIndex + 1) === 0) {
return true
} else {
return false
}
}
// 添加规格属性
function addSpecTag(index) {
let str = addValues[index] || ""
if (!str.trim()) return // 判空
str = str.trim()
let arr = str.split(/\s+/) // 使用空格分割成数组
let newArr = specification[index].value.concat(arr)
newArr = Array.from(new Set(newArr)) // 去重
specification[index].value = newArr
clearAddValues(index)
handleSpecChange("add")
}
// 清空 addValues
function clearAddValues(index) {
addValues[index] = ""
}
// 删除规格属性
function delSpecTag(index, num) {
specification[index].value.splice(num, 1)
handleSpecChange("del")
}
function addSpec() {
if (specification.length < 5) {
specification.push({
name: "",
value: []
})
}
}
function delSpec(index) {
specification.splice(index, 1)
handleSpecChange("del")
}
/**
* [handleSpecChange 监听标签的变化,当添加标签时求出每一行的hash id再动态变更库存信息当删除标签时先清空已有库存信息然后将原有库存信息暂存到stockCopy中方便后面调用]
* @param {[string]} option ['add'|'del' 操作类型,添加或删除]
* @return {[type]} [description]
*/
function handleSpecChange(option) {
const stockCopy = JSON.parse(JSON.stringify(childProductArray))
if (option === "del") {
childProductArray = []
}
for (let i = 0; i < countSum(0); i++) {
changeStock(option, i, stockCopy)
}
}
// 获取childProductArray的childProductSpec属性
function getChildProductSpec(index) {
let obj = {}
specification.forEach((item, specIndex) => {
obj[item.name] = getSpecAttr(specIndex, index)
})
return obj
}
/*
根据传入的属性值,拿到相应规格的属性
@params
specIndex 规格项目在 advancedSpecification 中的序号
index 所有属性在遍历时的序号
*/
function getSpecAttr(specIndex, index) {
// 获取当前规格项目下的属性值
const currentValues = specification[specIndex].value
let indexCopy
// 判断是否是最后一个规格项目
if (specification[specIndex + 1] && specification[specIndex + 1].value.length) {
indexCopy = index / countSum(specIndex + 1)
} else {
indexCopy = index
}
const i = Math.floor(indexCopy % currentValues.length)
if (i.toString() !== "NaN") {
return currentValues[i]
} else {
return ""
}
}
/**
* [根据规格,动态改变库存相关信息]
* @param {[string]} option ['add'|'del' 操作类型,添加或删除]
* @param {[array]} stockCopy [库存信息的拷贝]
* @return {[type]} [description]
*/
function changeStock(option, index, stockCopy) {
let childProduct = {
childProductId: 0,
childProductSpec: getChildProductSpec(index),
childProductNo: defaultProductNo + index,
childProductStock: 0,
childProductPrice: 0,
childProductCost: 0,
isUse: true
}
const spec = childProduct.childProductSpec
if (option === "add") {
// 如果此id不存在说明为新增属性则向 childProductArray 中添加一条数据
// if (stockSpecArr.value.findIndex((item) => objEquals(spec, item)) === -1) {
childProductArray[index] = childProduct
// }
} else if (option === "del") {
// 因为是删除操作理论上所有数据都能从stockCopy中获取到
let origin = ""
stockCopy.forEach((item) => {
if (objEquals(spec, item.childProductSpec)) {
origin = item
return false
}
})
childProductArray.push(origin || childProduct)
}
}
function objEquals(aObj, bObj) {
if (typeof aObj == "object" && typeof bObj == "object") {
if (aObj.constructor == Array && bObj.constructor == Array) {
return aObj.length == bObj.length && JSON.stringify(aObj) == JSON.stringify(bObj)
} else {
return Object.keys(aObj) == Object.keys(bObj) && Object.values(aObj) == Object.values(bObj)
}
return false
} else {
return aObj === bObj
}
}
/*
计算属性的乘积
@params
specIndex 规格项目在 advancedSpecification 中的序号
*/
function countSum(specIndex) {
let num = 1
specification.forEach((item, index) => {
if (index >= specIndex && item.value.length) {
num *= item.value.length
}
})
return num
}
//
// 绑定组件事件
rv("onCreated")
onMounted(() => rv("onMounted"))
</script>
<script>
export default {
name: "form-sku"
}
</script>
<style scoped>
.specification {
display: inline-block;
vertical-align: top;
width: 100%;
}
.specification .spec-list {
background-color: #fff;
border: 1px solid #d8d8d8;
padding: 10px;
margin-bottom: 20px;
}
.specification .spec-list .item {
margin-top: 5px;
}
.specification .spec-list .item:first-child {
margin-top: 0;
}
.specification .spec-list .item .name {
/*background: #f3f6fb;*/
padding: 2px 8px;
text-align: right;
overflow: hidden;
margin-bottom: 10px;
}
.specification .spec-list .item .name .icon {
display: none;
color: #929292;
cursor: pointer;
}
.specification .spec-list .item .name .icon:hover {
color: #880000;
}
.stock-table td,
.stock-table th {
padding: 4px 10px;
border-bottom: 1px solid #dfe6ec;
border-right: 1px solid #dfe6ec;
border-left: 1px solid #dfe6ec;
}
.stock-table th {
line-height: 30px;
background-color: #eef1f6;
}
.stock-table .wh-foot {
line-height: 30px;
}
.stock-table .wh-foot .label {
display: inline-block;
vertical-align: top;
}
.stock-table .wh-foot .set-list {
display: inline-block;
vertical-align: top;
font-size: 0;
}
.stock-table .wh-foot .set-list .set-item {
display: inline-block;
vertical-align: top;
margin-left: 15px;
font-size: 13px;
cursor: pointer;
color: #0088ee;
}
.stock-table .wh-foot .set-form {
display: inline-block;
margin-left: 10px;
}
</style>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -28,7 +23,7 @@
:marks="props.component.marks" :marks="props.component.marks"
:show-input="props.component.showInput" :show-input="props.component.showInput"
:show-ticks="props.component.showTicks" :show-ticks="props.component.showTicks"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
> >
</a-slider> </a-slider>
</slot> </slot>
@@ -37,15 +32,19 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -61,8 +60,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,33 +1,34 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component"> <div>
<div <slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
v-if="typeof props.component.display == 'undefined' || props.component.display === true" <div
:class="['static-text', props.component.customClass]" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
:style="props.component.style" :class="['static-text', props.component.customClass]"
> :style="props.component.style"
{{ props.component.title ?? "" }} >
</div> {{ props.component.title ?? "" }}
</slot> </div>
</slot>
</div>
</template> </template>
<script setup> <script setup>
import { onMounted } from "vue" import { onMounted, inject } from "vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object component: Object
}) })
maEvent.handleCommonEvent(props.component, "onCreated") const formModel = inject("formModel")
onMounted(() => { const getColumnService = inject("getColumnService")
maEvent.handleCommonEvent(props.component, "onMounted") const columns = inject("columns")
}) const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
rv("onCreated")
onMounted(() => rv("onMounted"))
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -16,9 +20,9 @@
:checked-color="props.component.checkedColor" :checked-color="props.component.checkedColor"
:unchecked-color="props.component.uncheckedColor" :unchecked-color="props.component.uncheckedColor"
:before-change="props.component.beforeChange" :before-change="props.component.beforeChange"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
> >
<template #checked> <template #checked>
<slot :name="`switchChecked-${props.component.dataIndex}`" /> <slot :name="`switchChecked-${props.component.dataIndex}`" />
@@ -39,15 +43,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -63,8 +72,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,12 +1,3 @@
<!--
- MineAdmin is committed to providing solutions for quickly building web applications
- Please view the LICENSE file that was distributed with this source code,
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -27,11 +18,11 @@
:show-word-limit="props.component.showWordLimit" :show-word-limit="props.component.showWordLimit"
:word-length="props.component.wordLength" :word-length="props.component.wordLength"
:word-slice="props.component.wordSlice" :word-slice="props.component.wordSlice"
@input="maEvent.handleInputEvent(props.component, $event)" @input="rv('onInput', $event)"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@focus="maEvent.handleCommonEvent(props.component, 'onFocus')" @focus="rv('onFocus')"
@blur="maEvent.handleCommonEvent(props.component, 'onBlur')" @blur="rv('onBlur')"
> >
</a-textarea> </a-textarea>
</slot> </slot>
@@ -40,15 +31,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -64,8 +60,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -23,8 +18,8 @@
:simple="props.component.simple" :simple="props.component.simple"
:data="props.component.data ?? dictList[dictIndex] ?? []" :data="props.component.data ?? dictList[dictIndex] ?? []"
:fallback="props.component.fallback" :fallback="props.component.fallback"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@select="maEvent.customeEvent(props.component, $event, 'onSelect')" @select="rv('onSelect', $event)"
> >
</a-transfer> </a-transfer>
</slot> </slot>
@@ -33,9 +28,9 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -43,7 +38,11 @@ const props = defineProps({
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const dictList = inject("dictList") const dictList = inject("dictList")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
@@ -66,8 +65,6 @@ if (props.component.dict && (props.component.dict.name || props.component.dict.d
value.value = value.value + "" value.value = value.value + ""
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -48,10 +43,10 @@
:fallback-option="props.component.fallbackOption" :fallback-option="props.component.fallbackOption"
:selectable="props.component.selectable" :selectable="props.component.selectable"
:scrollbar="props.component.scrollbar" :scrollbar="props.component.scrollbar"
@change="maEvent.handleChangeEvent(props.component, $event)" @change="rv('onChange', $event)"
@popup-visible-change="maEvent.customeEvent(props.component, $event, 'onPopupVisibleChange')" @popup-visible-change="rv('onPopupVisibleChange', $event)"
@clear="maEvent.handleCommonEvent(props.component, 'onClear')" @clear="rv('onClear')"
@search="maEvent.customeEvent(props.component, $event, 'onSearch')" @search="rv('onSearch', $event)"
> >
</a-tree-select> </a-tree-select>
</slot> </slot>
@@ -60,9 +55,9 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
import { handlerCascader } from "../js/networkRequest.js" import { handlerCascader } from "../js/networkRequest.js"
const props = defineProps({ const props = defineProps({
@@ -71,12 +66,16 @@ const props = defineProps({
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const dictList = inject("dictList") const dictList = inject("dictList")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) const dictIndex = index.match(/^(\w+\.)\d+\./)
? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex
: props.component.dataIndex : props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index) ?? "")
watch( watch(
() => get(formModel.value, index), () => get(formModel.value, index),
@@ -94,8 +93,6 @@ if (props.component.dict && (props.component.dict.name || props.component.dict.d
value.value = value.value + "" value.value = value.value + ""
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -41,16 +36,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaUpload from "@/components/ma-upload/index.vue" import MaUpload from "@/components/ma-upload/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -66,8 +65,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,11 +1,6 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<ma-form-item <ma-form-item
@@ -27,16 +22,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaUserSelect from "@/components/ma-user/index.vue" import MaUserSelect from "@/components/ma-user/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -56,8 +55,6 @@ if (props.component.multiple && !value.value) {
value.value = [] value.value = []
} }
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -12,16 +16,20 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaUserInfo from "@/components/ma-userInfo/index.vue" import MaUserInfo from "@/components/ma-userInfo/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
customField: { type: String, default: undefined } customField: { type: String, default: undefined }
}) })
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -37,8 +45,6 @@ watch(
} }
) )
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>

View File

@@ -1,3 +1,7 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<ma-form-item <ma-form-item
v-if="typeof props.component.display == 'undefined' || props.component.display === true" v-if="typeof props.component.display == 'undefined' || props.component.display === true"
@@ -23,10 +27,10 @@
<script setup> <script setup>
import { ref, inject, onMounted, watch } from "vue" import { ref, inject, onMounted, watch } from "vue"
import { get, set } from "lodash" import { get, set } from "lodash-es"
import MaVerifyCode from "@/components/ma-verifyCode/index.vue" import MaVerifyCode from "@/components/ma-verifyCode/index.vue"
import MaFormItem from "./form-item.vue" import MaFormItem from "./form-item.vue"
import { maEvent } from "../js/formItemMixin.js" import { runEvent } from "../js/event.js"
const props = defineProps({ const props = defineProps({
component: Object, component: Object,
@@ -35,6 +39,10 @@ const props = defineProps({
const formVerifyCode = ref() const formVerifyCode = ref()
const formModel = inject("formModel") const formModel = inject("formModel")
const getColumnService = inject("getColumnService")
const columns = inject("columns")
const rv = async (ev, value = undefined) =>
await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const index = props.customField ?? props.component.dataIndex const index = props.customField ?? props.component.dataIndex
const value = ref(get(formModel.value, index)) const value = ref(get(formModel.value, index))
@@ -63,10 +71,8 @@ component.rules = [
} }
] ]
maEvent.handleCommonEvent(props.component, "onCreated") rv("onCreated")
onMounted(() => { onMounted(() => rv("onMounted"))
maEvent.handleCommonEvent(props.component, "onMounted")
})
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,6 +1,10 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<div class="w-full"> <div class="w-full">
<a-spin :loading="formLoading" :tip="options.loadingText" class="w-full"> <a-spin :loading="formLoading" :tip="options.loadingText" class="w-full ma-form-spin">
<div <div
v-if="options.showFormTitle" v-if="options.showFormTitle"
:class="['ma-form-title', options.formTitleClass]" :class="['ma-form-title', options.formTitleClass]"
@@ -19,80 +23,76 @@
:rules="options?.rules" :rules="options?.rules"
@submit="formSubmit" @submit="formSubmit"
> >
<template v-for="(component, componentIndex) in columns" :key="componentIndex"> <slot name="formContent">
<component :is="getComponentName(component?.formType ?? 'input')" :component="component"> <template v-for="(component, componentIndex) in columns" :key="componentIndex">
<template v-for="slot in Object.keys($slots)" #[slot]="component"> <component
<slot :name="slot" v-bind="component" /> :is="getComponentName(component?.formType ?? 'input')"
</template> :component="component"
</component> :ref="setDialogRef"
</template> >
<div class="text-center" v-if="options.showButtons"> <template v-for="slot in Object.keys($slots)" #[slot]="component">
<a-space> <slot :name="slot" v-bind="component" />
<slot name="formBeforeButtons" /> </template>
<slot name="formButtons"> </component>
<a-button </template>
:type="options.submitType" <div class="text-center" v-if="options.showButtons">
:status="options.submitStatus" <a-space>
v-if="options.submitShowBtn" <slot name="formBeforeButtons" />
html-type="submit" <slot name="formButtons">
> <a-button
<template #icon v-if="options?.submitIcon"> :type="options.submitType"
<component :is="options.submitIcon" /> :status="options.submitStatus"
</template> v-if="options.submitShowBtn"
{{ options.submitText }} html-type="submit"
</a-button> >
<a-button <template #icon v-if="options?.submitIcon">
:type="options.resetType" <component :is="options.submitIcon" />
:status="options.resetStatus" </template>
v-if="options.resetShowBtn" {{ options.submitText }}
@click="resetForm" </a-button>
> <a-button
<template #icon v-if="options?.resetIcon"> :type="options.resetType"
<component :is="options.resetIcon" /> :status="options.resetStatus"
</template> v-if="options.resetShowBtn"
{{ options.resetText }} @click="resetForm"
</a-button> >
</slot> <template #icon v-if="options?.resetIcon">
<slot name="formAfterButtons" /> <component :is="options.resetIcon" />
</a-space> </template>
</div> {{ options.resetText }}
</a-button>
</slot>
<slot name="formAfterButtons" />
</a-space>
</div>
</slot>
</a-form> </a-form>
</a-spin> </a-spin>
</div> </div>
</template> </template>
<script setup> <script setup>
import { isNil, get } from "lodash"
import { ref, watch, provide, onMounted, nextTick, getCurrentInstance } from "vue" import { ref, watch, provide, onMounted, nextTick, getCurrentInstance } from "vue"
import { isNil, set, get, cloneDeep } from "lodash"
import defaultOptions from "./js/defaultOptions.js" import defaultOptions from "./js/defaultOptions.js"
import { getComponentName, toHump, interactiveControl, handleFlatteningColumns } from "./js/utils.js" import {
getComponentName,
toHump,
interactiveControl,
handleFlatteningColumns,
insertGlobalCssToHead,
insertGlobalFunctionsToHtml
} from "./js/utils.js"
import { loadDict, handlerCascader } from "./js/networkRequest.js" import { loadDict, handlerCascader } from "./js/networkRequest.js"
import arrayComponentDefault from "./js/defaultArrayComponent.js" import arrayComponentDefault from "./js/defaultArrayComponent.js"
import ColumnService from "./js/columnService.js"
import { maEvent } from "./js/formItemMixin.js" import { runEvent } from "./js/event.js"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
const containerList = import.meta.glob("./containerItem/*.vue", { eager: true })
const componentList = import.meta.glob("./formItem/*.vue", { eager: true })
const _this = getCurrentInstance().appContext
for (const path in containerList) {
const name = path.match(/([A-Za-z0-9_-]+)/g)[1]
const containerName = `Ma${toHump(name)}`
if (!_this.components[containerName]) {
_this.app.component(containerName, containerList[path].default)
}
}
for (const path in componentList) {
const name = path.match(/([A-Za-z0-9_-]+)/g)[1]
const componentName = `Ma${toHump(name)}`
if (!_this.components[componentName]) {
_this.app.component(componentName, componentList[path].default)
}
}
const formLoading = ref(false) const formLoading = ref(false)
const maFormRef = ref() const maFormRef = ref()
const dialogRefs = ref({})
const flatteningColumns = ref([]) const flatteningColumns = ref([])
const dictList = ref({}) const dictList = ref({})
const cascaderList = ref([]) const cascaderList = ref([])
@@ -103,7 +103,7 @@ const props = defineProps({
columns: { type: Array }, columns: { type: Array },
options: { type: Object, default: {} } options: { type: Object, default: {} }
}) })
const emit = defineEmits(["onSubmit", "update:modelValue"]) const emit = defineEmits(["submit", "update:modelValue"])
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -114,16 +114,38 @@ watch(
watch( watch(
() => form.value, () => form.value,
(vl) => { (vl) => {
interactiveControl(vl, flatteningColumns.value) interactiveControl(vl, flatteningColumns.value, { getColumnService, dictList })
emit("update:modelValue", vl) emit("update:modelValue", vl)
}, },
{ deep: true } { deep: true }
) )
const options = ref(Object.assign(JSON.parse(JSON.stringify(defaultOptions)), props.options)) const options = ref({})
// 初始化 // 初始化
const init = async () => { const init = async () => {
const containerList = import.meta.glob("./containerItem/*.vue", { eager: true })
const componentList = import.meta.glob("./formItem/*.vue", { eager: true })
const _this = getCurrentInstance()?.appContext ?? undefined
if (_this) {
for (const path in containerList) {
const name = path.match(/([A-Za-z0-9_-]+)/g)[1]
const containerName = `Ma${toHump(name)}`
if (!_this.components[containerName]) {
_this.app.component(containerName, containerList[path].default)
}
}
for (const path in componentList) {
const name = path.match(/([A-Za-z0-9_-]+)/g)[1]
const componentName = `Ma${toHump(name)}`
if (!_this.components[componentName]) {
_this.app.component(componentName, componentList[path].default)
}
}
}
formLoading.value = true formLoading.value = true
handleFlatteningColumns(props.columns, flatteningColumns.value) handleFlatteningColumns(props.columns, flatteningColumns.value)
@@ -137,16 +159,29 @@ const init = async () => {
// 初始化数据 // 初始化数据
flatteningColumns.value.map(async (item) => { flatteningColumns.value.map(async (item) => {
if (isNil(form.value[item.dataIndex]) && !item.isChildrenForm) { if (isNil(form.value[item.dataIndex])) {
form.value[item.dataIndex] = undefined form.value[item.dataIndex] = undefined
if (arrayComponentDefault.includes(item.formType) && !item.isChildrenForm) { if (arrayComponentDefault.includes(item.formType)) {
form.value[item.dataIndex] = [] form.value[item.dataIndex] = []
} }
} }
// 处理带点的字段
if (typeof item.dataIndex === "string") {
if (item.dataIndex.indexOf(".") > -1) {
const value = cloneDeep(form.value[item.dataIndex])
delete form.value[item.dataIndex]
set(form.value, item.dataIndex, value)
}
}
// 字典 // 字典
if (!cascaderList.value.includes(item.dataIndex) && item.dict) { if (!cascaderList.value.includes(item.dataIndex) && item.dict) {
await loadDict(dictList.value, item) await loadDict(dictList.value, item, options.value.sourceList, {
formModel: form.value,
getColumnService,
columns: flatteningColumns.value
})
} }
// 联动 // 联动
@@ -161,22 +196,31 @@ const init = async () => {
}) })
await nextTick(() => { await nextTick(() => {
interactiveControl(form.value, flatteningColumns.value) interactiveControl(form.value, flatteningColumns.value, { getColumnService, dictList })
formLoading.value = false formLoading.value = false
}) })
} }
provide("options", options.value) const setDialogRef = async (ref) => {
provide("columns", flatteningColumns) await nextTick(() => {
provide("dictList", dictList) if (ref?.getDataIndex) {
provide("formModel", form) dialogRefs.value[ref.getDataIndex()] = ref
provide("formLoading", formLoading) if (!form.value[ref.getDataIndex()]) {
maEvent.handleCommonEvent(options.value, "onCreated") form.value[ref.getDataIndex()] = {}
}
}
})
}
onMounted(() => { // maEvent.handleCommonEvent(options.value, 'onCreated')
maEvent.handleCommonEvent(options.value, "onMounted")
options.value.init && init() onMounted(async () => {
maEvent.handleCommonEvent(options.value, "onInit") updateOptions()
insertGlobalCssToHead(options.value.globalCss)
insertGlobalFunctionsToHtml(options.value.globalFunction)
// maEvent.handleCommonEvent(options.value, 'onMounted')
options.value.init && (await init())
// maEvent.handleCommonEvent(options.value, 'onInit')
}) })
const done = (status) => (formLoading.value = status) const done = (status) => (formLoading.value = status)
@@ -192,81 +236,56 @@ const validateForm = async () => {
const resetForm = async () => await maFormRef.value.resetFields() const resetForm = async () => await maFormRef.value.resetFields()
const clearValidate = async () => await maFormRef.value.clearValidate() const clearValidate = async () => await maFormRef.value.clearValidate()
const formSubmit = async () => ((await validateForm()) && !formLoading.value) || emit("onSubmit", form.value, done) const formSubmit = async () => {
if (await validateForm()) {
return false
}
emit("submit", form.value, done)
}
/**
* 获取column属性服务类
* @returns ColumnService
*/
const getColumnService = (strictMode = true) => {
return new ColumnService(
{
columns: flatteningColumns.value,
cascaders: cascaderList.value,
dicts: dictList.value,
refs: dialogRefs.value
},
strictMode
)
}
const getFormRef = () => maFormRef.value const getFormRef = () => maFormRef.value
const getDictList = () => dictList.value const getDictList = () => dictList.value
const getDictService = () => {
const DictService = function (dictList) {
/**
* dict项服务类
* @param dataIndex
* @param dictData
* @constructor
*/
const DictItemService = function (dataIndex, dictData) {
this.dict = dictData
this.dataIndex = dataIndex
/**
* 返回原DictData对象
* @returns {*}
*/
this.getRawDictData = () => {
return this.dict
}
/**
* 追加
* @param label
* @param value
* @param extend
*/
this.append = (label, value, extend = {}) => {
this.getRawDictData().push(
Object.assign(
{
label: label,
value: value
},
extend
)
)
}
/**
* 重新加载dict
* @param dictConfig
* @returns {Promise<void>}
*/
this.loadDict = (dictConfig) => {
return loadDict(dictList, { formType: "select", dict: dictConfig, dataIndex: this.dataIndex })
}
}
this.dictMap = new Map()
for (const [dataIndex, dictData] of Object.entries(dictList)) {
this.dictMap.set(dataIndex, new DictItemService(dataIndex, dictData))
}
this.get = (key) => {
return this.dictMap.get(key)
}
}
return new DictService(getDictList())
}
const getColumns = () => flatteningColumns.value const getColumns = () => flatteningColumns.value
const getCascaderList = () => cascaderList.value const getCascaderList = () => cascaderList.value
const getFormData = () => form.value const getFormData = () => form.value
const updateOptions = () => (options.value = Object.assign(cloneDeep(defaultOptions), props.options))
provide("options", options.value)
provide("columns", flatteningColumns)
provide("dictList", dictList)
provide("formModel", form)
provide("formLoading", formLoading)
provide("getColumnService", getColumnService)
provide("maFormRef", maFormRef)
defineExpose({ defineExpose({
init, init,
getFormRef, getFormRef,
getColumns, getColumns,
getDictList, getDictList,
getDictService, getColumnService,
getCascaderList, getCascaderList,
getFormData, getFormData,
validateForm, validateForm,
resetForm, resetForm,
clearValidate clearValidate,
updateOptions
}) })
</script> </script>

View File

@@ -0,0 +1,175 @@
import { loadDict } from "@cps/ma-form/js/networkRequest"
/**
* columnService 列服务处理类
* 首先感谢 @NEKGod 提交的PR此功能原本写在了 Ma-Crud 组件,我特意摘出来,封装成类通过引用来调用
* @author NEKGod, X.Mo <root@imoi.cn>
*/
const objectService = function (item) {
this.setAttr = (key, value) => {
item[key] = value
}
this.getAttr = (key) => {
return item[key]
}
this.get = () => {
return item
}
this.set = (config = {}) => {
for (let [key, value] of Object.entries(config)) {
item[key] = value
}
}
}
/**
* dict项服务类
* @param dataIndex
* @param dictData
* @constructor
*/
const dictService = function (dataIndex, dictData, dicts, columns) {
this.columns = columns
this.dicts = dicts
this.dictData = dictData
this.dataIndex = dataIndex
/**
* 返回原DictData对象
* @returns {*}
*/
this.getRawDictData = () => {
return this.dictData
}
/**
* 追加
* @param label
* @param value
* @param extend
*/
this.append = (label, value, extend = {}) => {
this.getRawDictData().push(
Object.assign(
{
label: label,
value: value
},
extend
)
)
}
/**
* 重新加载dict
* @param dictConfig
* @returns {Promise<void>}
*/
this.loadDict = async (dictConfig) => {
this.columns.setAttr("dict", dictConfig)
await loadDict(this.dicts, { formType: "select", dict: dictConfig, dataIndex: this.dataIndex })
}
}
class ColumnService {
/**
* @type {Map<string, Object>}
*/
columnMap = new Map()
dictMap = new Map()
columns
cascaders
dicts
refs
strictMode
/**
* @param data
* @param strictMode
*/
constructor(data, strictMode) {
this.columns = data.columns
this.cascaders = data.cascaders
this.dicts = data.dicts
this.refs = data?.refs ?? {}
this.strictMode = strictMode
this.columns.forEach((item) => {
this.columnMap.set(item.dataIndex, new objectService(item))
})
for (const [dataIndex, dictData] of Object.entries(this.dicts)) {
this.dictMap.set(dataIndex, new dictService(dataIndex, dictData, this.dicts, this.columnMap.get(dataIndex)))
}
}
getDialogRefs(refName = undefined) {
return refName ? this.refs[refName] : this.refs
}
getDictService(dataIndex) {
return this.dictMap.get(dataIndex)
}
get(dataIndex) {
return this.columnMap.get(dataIndex)
}
isEmpty(dataIndex) {
return !this.columnMap.has(dataIndex)
}
exist(dataIndex) {
return !this.isEmpty(dataIndex)
}
async append(item, appendStartDataIndex = null) {
if (this.strictMode === true && item.dataIndex && this.exist(item.dataIndex)) {
console.warn(
`严格模式columnService.append(item) 参数中未有item.dataIndex属性或者item.dataIndex已存在column.${item.dataIndex}`
)
return false
}
if (this.cascaders.includes(item.dataIndex) && item.dict) {
await loadDict(this.dicts, item)
}
this.columns.push(item)
this.columnMap.set(item.dataIndex, new objectService(item))
// 获取排序
if (appendStartDataIndex !== null) {
let appendIndex =
this.columns
.map((item) => {
return item.dataIndex
})
?.indexOf(appendStartDataIndex) ?? -1
if (appendIndex === -1) {
return this.append(item, null)
}
let sortIndex = 0
let appendPosIndex = 0
this.columns.forEach((sortItem) => {
if (sortItem.dataIndex === appendStartDataIndex) {
appendPosIndex = sortIndex
} else if (sortItem.dataIndex === item.dataIndex) {
sortIndex = appendPosIndex + 1
} else {
}
sortItem.sortIndex = sortIndex
sortIndex += 2
})
this.columns.sort((a, b) => a.sortIndex - b.sortIndex)
}
return true
}
}
export default ColumnService

View File

@@ -47,5 +47,13 @@ export default {
// 自定义标题样式css // 自定义标题样式css
formTitleStyle: "", formTitleStyle: "",
// 自定义标题样式class // 自定义标题样式class
formTitleClass: [] formTitleClass: [],
// 数据源列表,配合表单设计器使用,单独无法使用
sourceList: [],
// 全局CSS class
globalCss: "",
// 全局function
globalFunction: ""
} }

View File

@@ -0,0 +1,39 @@
import { isString, isFunction } from "lodash-es"
export const haveArgsEvent = async (component, value, evName, maformObj) => {
if (component[evName]) {
if (isFunction(component[evName])) {
return await component[evName](value, maformObj)
}
if (isString(component[evName])) {
const customFn = new Function("value", "maFormObject", component[evName])
return await customFn.call(component, value, maformObj)
}
}
}
export const notHaveArgsEvent = async (component, evName, maformObj) => {
if (component[evName]) {
if (isFunction(component[evName])) {
return await component[evName](maformObj)
}
if (isString(component[evName])) {
const customFn = new Function("maFormObject", component[evName])
return await customFn.call(component[evName], maformObj)
}
}
}
export const tabAddEvent = (component, maformObj) => {
haveArgsEvent(component, component?.tabs, "onTabAdd", maformObj)
}
export const tabDeleteEvent = (component, value, maformObj) => {
haveArgsEvent(component, { tabs: component?.tabs, value }, "onTabDelete", maformObj)
}
export const runEvent = async (component, evName, maformObj, value = undefined) => {
return value
? await haveArgsEvent(component, value, evName, maformObj)
: await notHaveArgsEvent(component, evName, maformObj)
}

View File

@@ -1,4 +1,4 @@
import { isString, isFunction } from "lodash" import { isString, isFunction } from "lodash-es"
export const maEvent = { export const maEvent = {
customeEvent: async (component, value, evName) => { customeEvent: async (component, value, evName) => {
if (component[evName]) { if (component[evName]) {

View File

@@ -1,6 +1,8 @@
import { isArray, isFunction, set } from "lodash" import { toRaw } from "vue"
import { isArray, isFunction, set, isUndefined } from "lodash-es"
import { request } from "@/utils/request" import { request } from "@/utils/request"
import commonApi from "@/api/common" import commonApi from "@/api/common"
import { Message } from "@arco-design/web-vue"
import tool from "@/utils/tool" import tool from "@/utils/tool"
export const allowUseDictComponent = [ export const allowUseDictComponent = [
@@ -14,8 +16,49 @@ export const allowUseDictComponent = [
] ]
export const allowCoverComponent = ["radio", "checkbox", "select", "transfer", "cascader"] export const allowCoverComponent = ["radio", "checkbox", "select", "transfer", "cascader"]
export const requestDict = (url, method, params, data, timeout = 10 * 1000) => export const coverSourceArrayToObj = (arr) => {
request({ url, method, params, data, timeout }) if (arr === null || arr.length === 0) {
return null
}
let obj = {}
const checkDataType = (type, value) => {
if (type === "string") {
return value.toString()
}
if (type === "number") {
return parseInt(value)
}
if (type === "bool") {
return value !== "false" && value !== 0 && (value === "true" || value) ? true : false
}
}
arr.map((item) => (obj[item.name] = checkDataType(item.type, item.value)))
return obj
}
export const requestDict = (url, method, params, data, header = {}, timeout = 10 * 1000) => {
return request({ url, method, params, data, header, timeout })
}
export const requestDataSource = async (config, maFormObject = {}) => {
try {
const response = await requestDict(
config.url,
config.type,
coverSourceArrayToObj(config.params),
coverSourceArrayToObj(config.data),
coverSourceArrayToObj(config.header),
config.timeout * 1000
)
const func = new Function("response", "config", "maFormObject", toRaw(config.response).getValue())
return await func.call(null, response, config, maFormObject)
} catch (err) {
const func = new Function("message", "response", "config", toRaw(config.errorHandle).getValue())
await func.call(null, Message, err, config)
return err
}
}
export const handlerDictProps = (item, tmpArr) => { export const handlerDictProps = (item, tmpArr) => {
let data = [] let data = []
@@ -23,7 +66,7 @@ export const handlerDictProps = (item, tmpArr) => {
let colors = {} let colors = {}
let labelName = "label" let labelName = "label"
let valueName = "value" let valueName = "value"
if (item.dict.name && (!item.dict.url || !item.dict.data)) { if (item?.dict?.name && (!item.dict.url || !item.dict.data)) {
labelName = "title" labelName = "title"
valueName = "key" valueName = "key"
} }
@@ -37,10 +80,10 @@ export const handlerDictProps = (item, tmpArr) => {
typeof dicItem["indeterminate"] == "undefined" typeof dicItem["indeterminate"] == "undefined"
? false ? false
: dicItem["indeterminate"] === true : dicItem["indeterminate"] === true
? true ? true
: false : false
let value let value
if (item.dict.name || item.dict.data) value = tmp.toString() if (item.dict.name || item.dict.data) value = tmp?.toString() ?? tmp
else if (tmp === "true") value = true else if (tmp === "true") value = true
else if (tmp === "false") value = false else if (tmp === "false") value = false
else value = tmp else value = tmp
@@ -56,61 +99,122 @@ export const handlerDictProps = (item, tmpArr) => {
return data return data
} }
export const loadDict = async (dictList, item) => { export const loadDict = async (dictList, item, sourceList = [], maFormObject = {}) => {
if (allowUseDictComponent.includes(item.formType) && item.dict) { if (!allowUseDictComponent.includes(item.formType)) {
if (item.dict.name) { return
const response = await commonApi.getDict(item.dict.name) }
if (response.data) { const dataIndex = item.parentDataIndex ? `${item.parentDataIndex}.${item.dataIndex}` : item.dataIndex
dictList[item.dataIndex] = handlerDictProps(item, response.data) if (item.dict.name) {
} const response = await commonApi.getDict(item.dict.name)
} else if (item.dict.url) { if (response.data) {
const dictData = tool.local.get("dictData") dictList[dataIndex] = handlerDictProps(item, response.data)
if (item.dict.cache && dictData[item.dataIndex]) { }
dictList[item.dataIndex] = dictData[item.dataIndex] } else if (item.dict.remote) {
let requestData = {
openPage: item.dict?.openPage ?? false,
remoteOption: item.dict.remoteOption ?? {}
}
requestData = Object.assign(requestData, item.dict.pageOption)
if (requestData.openPage) {
const { data } = await requestDict(item.dict.remote, "POST", {}, requestData)
dictList[dataIndex] = handlerDictProps(item, data.items)
dictList[dataIndex].pageInfo = data.pageInfo
} else {
const dictData = tool.local.get("dictData") ?? {}
if (item.dict.cache && dictData[dataIndex]) {
dictList[dataIndex] = handlerDictProps(item, dictData[dataIndex])
} else { } else {
const response = await requestDict( const { data } = await requestDict(item.dict.remote, "POST", {}, requestData)
dictList[dataIndex] = handlerDictProps(item, data)
if (item.dict.cache) {
dictData[dataIndex] = data
tool.local.set("dictData", dictData)
}
}
}
} else if (item.dict.url) {
let requestData = {
openPage: item.dict?.openPage ?? false,
remoteOption: item.dict.remoteOption ?? {}
}
requestData = Object.assign(requestData, item.dict.pageOption)
if (requestData.openPage) {
if (item.dict?.method === "GET" || item.dict?.method === "get") {
item.dict.params = Object.assign(item.dict.params ?? {}, requestData)
} else {
item.dict.body = Object.assign(item.dict.body ?? {}, requestData)
}
const { data } = await requestDict(
item.dict.url,
item.dict.method || "GET",
item.dict.params || {},
item.dict.body || {}
)
dictList[dataIndex] = handlerDictProps(item, data.items)
dictList[dataIndex].pageInfo = data.pageInfo
} else {
const dictData = tool.local.get("dictData") ?? {}
if (item.dict.cache && dictData[dataIndex]) {
dictList[dataIndex] = handlerDictProps(item, dictData[dataIndex])
} else {
const { data } = await requestDict(
item.dict.url, item.dict.url,
item.dict.method || "GET", item.dict.method || "GET",
item.dict.params || {}, item.dict.params || {},
item.dict.body || {} item.dict.body || {}
) )
if (response.data) { dictList[dataIndex] = handlerDictProps(item, data)
dictList[item.dataIndex] = handlerDictProps(item, response.data) if (item.dict.cache) {
if (item.dict.cache) { dictData[dataIndex] = data
dictData[item.dataIndex] = dictList[item.dataIndex] tool.local.set("dictData", dictData)
tool.local.set("dictData", dictData)
}
} }
} }
} else if (item.dict.data) { }
if (isArray(item.dict.data)) { } else if ((item?.sourceIndex || item?.sourceIndex === 0) && !isUndefined(sourceList[item?.sourceIndex])) {
dictList[item.dataIndex] = handlerDictProps(item, item.dict.data) const config = sourceList[item.sourceIndex]
} else if (isFunction(item.dict.data)) { const data = await requestDataSource(config, maFormObject)
const response = await item.dict.data() dictList[dataIndex] = handlerDictProps(item, data)
dictList[item.dataIndex] = handlerDictProps(item, response) } else if (item.dict.data) {
} if (isArray(item.dict.data)) {
dictList[dataIndex] = handlerDictProps(item, item.dict.data)
} else if (isFunction(item.dict.data)) {
const response = await item.dict.data()
dictList[dataIndex] = handlerDictProps(item, response)
} }
} }
} }
const requestCascaderData = async (val, dict, dictList, name) => { const requestCascaderData = async (val, dict, dictList, name) => {
if (dict && dict.url) { if (dict && (dict.remote || dict.url)) {
let requestData = { openPage: dict?.openPage ?? false, remoteOption: dict.remoteOption ?? {} }
let response let response
if (dict && dict.url.indexOf("{{key}}") > 0) { const pageOption = Object.assign(requestData, dict.pageOption)
let url = dict.remote ?? dict.url
if (dict && url.indexOf("{{key}}") > 0) {
url = url.replace("{{key}}", val)
// 解析参数
let queryParams = tool.getRequestParams(url)
let urlIndex = url.indexOf("?")
if (urlIndex !== -1) {
url = url.substring(0, urlIndex)
}
response = await requestDict( response = await requestDict(
dict.url.replace("{{key}}", val), url,
dict.method || "GET", dict.method ?? "GET",
dict.params || {}, Object.assign(dict.params || {}, requestData.openPage ? pageOption : {}, queryParams),
dict.data || {} Object.assign(dict.data || {}, requestData.openPage ? pageOption : {})
) )
} else { } else {
let temp = { key: val } let temp = Object.assign({ key: val }, requestData.openPage ? pageOption : {})
const params = Object.assign(dict.params || {}, temp) const params = Object.assign(dict.params || {}, temp)
const data = Object.assign(dict.data || {}, temp) const data = Object.assign(dict.data || {}, temp)
response = await requestDict(dict.url, dict.method || "GET", params || {}, data || {}) response = await requestDict(url, dict.method ?? "GET", params || {}, data || {})
} }
if (response.data && response.code === 200) { if (response.data && response.code === 200) {
dictList[name] = response.data.map((dicItem) => { let dataIems = requestData.openPage ? response.data.items : response.data
dictList[name] = dataIems.map((dicItem) => {
return { return {
label: dicItem[(dict.props && dict.props.label) || "label"], label: dicItem[(dict.props && dict.props.label) || "label"],
value: dicItem[(dict.props && dict.props.value) || "value"], value: dicItem[(dict.props && dict.props.value) || "value"],
@@ -120,10 +224,11 @@ const requestCascaderData = async (val, dict, dictList, name) => {
typeof dicItem["indeterminate"] == "undefined" typeof dicItem["indeterminate"] == "undefined"
? false ? false
: dicItem["indeterminate"] === true : dicItem["indeterminate"] === true
? true ? true
: false : false
} }
}) })
dictList[name].pageInfo = response.data.pageInfo ?? undefined
} else { } else {
console.error(response) console.error(response)
} }
@@ -133,9 +238,12 @@ const requestCascaderData = async (val, dict, dictList, name) => {
export const handlerCascader = async (val, column, columns, dictList, formModel, clearData = true) => { export const handlerCascader = async (val, column, columns, dictList, formModel, clearData = true) => {
if (column.cascaderItem && isArray(column.cascaderItem)) { if (column.cascaderItem && isArray(column.cascaderItem)) {
column.cascaderItem.map(async (name) => { column.cascaderItem.map(async (name) => {
const dict = columns.find((col) => col.dataIndex === name && col.dict).dict const columnItem = columns.find((col) => col.dataIndex === name && col.dict)
clearData && set(formModel, name, undefined) const dataIndex = columnItem.parentDataIndex
requestCascaderData(val, dict, dictList, name) ? `${columnItem.parentDataIndex}.${columnItem.dataIndex}`
: columnItem.dataIndex
clearData && set(formModel, dataIndex, undefined)
await requestCascaderData(val, columnItem.dict, dictList, dataIndex)
}) })
} }
} }

View File

@@ -1,15 +1,30 @@
import { isEmpty, isFunction, get, set } from "lodash" import { isEmpty, isFunction, get, set } from "lodash-es"
export const containerItems = ["tabs", "table", "card", "grid", "grid-tailwind", "children-form"] export const containerItems = ["tabs", "table", "card", "grid", "grid-tailwind", "children-form"]
export const inputType = ["input", "input-password", "input-search"] export const inputType = ["input", "input-password", "input-search"]
export const pickerType = ["date", "month", "year", "week", "quarter", "range", "time"] export const pickerType = ["date", "month", "year", "week", "quarter", "range", "time"]
export const interactiveControl = (form, columns) => { export const interactiveControl = (form, columns, maFormObject) => {
const obj = [] const obj = []
const names = []
const keys = Object.keys(form)
if (keys && keys.length > 0) {
keys.map((item) => {
if (form[item] && typeof form[item] === "object") {
for (let name in form[item]) {
names.push(`${item}.${name}`)
}
}
})
}
for (let name in form) { for (let name in form) {
columns.map((item) => { columns.map((item) => {
if (item.dataIndex === name && item.control && isFunction(item.control)) { if (
obj.push(item.control(get(form, name), form)) (item.dataIndex === name || names.includes(item.dataIndex)) &&
item.onControl &&
isFunction(item.onControl)
) {
obj.push(item.onControl(get(form, item.dataIndex), maFormObject))
} }
}) })
} }
@@ -56,7 +71,7 @@ export const getComponentName = (formType) => {
return `MaForm${toHump(formType)}` return `MaForm${toHump(formType)}`
} }
export const handleFlatteningColumns = (data, columns, isChildrenForm = undefined) => { export const handleFlatteningColumns = (data, columns) => {
for (let key in data) { for (let key in data) {
const item = data[key] const item = data[key]
if (containerItems.includes(item.formType)) { if (containerItems.includes(item.formType)) {
@@ -90,15 +105,42 @@ export const handleFlatteningColumns = (data, columns, isChildrenForm = undefine
}) })
} }
break break
// case 'children-form': case "children-form":
// item.formList && handleFlatteningColumns(item.formList, columns, item.dataIndex, true) item.formList.map((list) => (list.parentDataIndex = item.dataIndex))
// break item.formList && handleFlatteningColumns(item.formList, columns)
break
} }
} else { } else {
// if (isChildrenForm) {
// item['isChildrenForm'] = true
// }
columns.push(item) columns.push(item)
} }
} }
} }
export const insertGlobalCssToHead = (cssCode) => {
const head = document.getElementsByTagName("head")[0]
const oldStyle = document.getElementById("mineadmin-global-css")
oldStyle && head.removeChild(oldStyle)
const newStyle = document.createElement("style")
newStyle.rel = "stylesheet"
newStyle.id = "mineadmin-global-css"
try {
newStyle.appendChild(document.createTextNode(cssCode))
} catch (ex) {
newStyle.styleSheet.cssText = cssCode
}
head.appendChild(newStyle)
}
export const insertGlobalFunctionsToHtml = (functionsCode) => {
const bodyEle = document.getElementsByTagName("body")[0]
const oldScriptEle = document.getElementById("mineadmin-global-functions")
oldScriptEle && bodyEle.removeChild(oldScriptEle)
const newScriptEle = document.createElement("script")
newScriptEle.id = "mineadmin-global-functions"
newScriptEle.type = "text/javascript"
newScriptEle.innerHTML = functionsCode
bodyEle.appendChild(newScriptEle)
}

View File

@@ -1,126 +1,128 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<div class="w-full"> <div class="w-full">
<a-input-group class="w-full"> <a-input-group class="w-full">
<a-input placeholder="请点击右侧按钮选择图标" allow-clear v-model="currentIcon" /> <a-input placeholder="请点击右侧按钮选择图标" v-if="props.preview" allow-clear v-model="currentIcon" />
<div class="icon-container" v-if="props.preview"> <div class="icon-container" v-if="props.preview">
<component :is="currentIcon" v-if="currentIcon" /> <component :is="currentIcon" v-if="currentIcon" />
</div> </div>
<a-button type="primary" @click="() => (visible = true)">选择图标</a-button> <a-button type="primary" @click="() => visible = true">选择图标</a-button>
</a-input-group> </a-input-group>
<a-modal v-model:visible="visible" width="800px" draggable :footer="false"> <a-modal v-model:visible="visible" width="800px" draggable :footer="false">
<template #title>选择图标</template> <template #title>选择图标</template>
<a-tabs class="tabs"> <a-tabs class="tabs">
<a-tab-pane key="arco" title="Arco Design"> <a-tab-pane key="arco" title="Arco Design">
<ul class="arco"> <ul class="arco">
<li v-for="icon in arcodesignIcons" :key="icon" @click="selectIcon(icon, 'arco')"> <li
<component :is="icon" /> v-for="icon in arcodesignIcons"
</li> :key="icon"
</ul> @click="selectIcon(icon, 'arco')"
</a-tab-pane> >
<a-tab-pane key="mine" title="MineAdmin"> <component :is="icon" />
<ul class="mine"> </li>
<li v-for="icon in mineadminIcons" :key="icon" @click="selectIcon(icon, 'mine')"> </ul>
<component :is="icon" /> </a-tab-pane>
</li> <a-tab-pane key="mine" title="MineAdmin">
</ul> <ul class="mine">
</a-tab-pane> <li
</a-tabs> v-for="icon in mineadminIcons"
</a-modal> :key="icon"
</div> @click="selectIcon(icon, 'mine')"
>
<component :is="icon" />
</li>
</ul>
</a-tab-pane>
</a-tabs>
</a-modal>
</div>
</template> </template>
<script setup> <script setup>
import { reactive, ref, onMounted } from "vue" import { reactive, ref, computed } from 'vue'
import * as arcoIcons from "@arco-design/web-vue/es/icon" import * as arcoIcons from '@arco-design/web-vue/es/icon'
const mineadminIcons = reactive([]) const mineadminIcons = reactive([])
const arcodesignIcons = reactive([]) const arcodesignIcons = reactive([])
const visible = ref(false) const visible = ref(false)
const currentIcon = ref()
const props = defineProps({ const props = defineProps({
modelValue: { type: String }, modelValue: { type: String },
preview: { type: Boolean, default: true } preview: { type: Boolean, default: true },
}) })
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(['update:modelValue'])
onMounted(() => (currentIcon.value = props.modelValue)) const currentIcon = computed({
get() {
return props.modelValue;
},
set(value) {
// html标签名不能以数字开头
if ((/^[^\d].*/.test(value) && value) || !value) {
emit('update:modelValue', value);
}
}
});
for (let icon in arcoIcons) { for (let icon in arcoIcons) {
arcodesignIcons.push(icon) arcodesignIcons.push(icon)
} }
arcodesignIcons.pop() arcodesignIcons.pop()
const modules = import.meta.globEager("../../assets/ma-icons/*.vue") const modules = import.meta.glob('../../assets/ma-icons/*.vue')
for (const path in modules) { for (const path in modules) {
const name = path.match(/([A-Za-z0-9_-]+)/g)[2] const name = path.match(/([A-Za-z0-9_-]+)/g)[2]
mineadminIcons.push(`MaIcon${name}`) mineadminIcons.push(`MaIcon${name}`)
} }
const selectIcon = (icon, className) => { const selectIcon = (icon, className) => {
currentIcon.value = icon currentIcon.value = icon
emit("update:modelValue", currentIcon.value)
visible.value = false visible.value = false
} }
const handlerChange = (value) => { const handlerChange = (value) => {
selectIcon(value, "") selectIcon(value, '')
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.icon-container { .icon-container {
width: 50px; width: 50px; height: 32px;
height: 32px; background-color: var(--color-fill-1);
background-color: var(--color-fill-1); display: flex; justify-content: center;
display: flex; align-items: center;
justify-content: center;
align-items: center;
} }
.icon { .icon {
width: 1em; width: 1em; fill: var(--color-text-1);
fill: var(--color-text-1);
} }
.tabs { .tabs {
ul {
display: flex;
flex-wrap: wrap;
padding-left: 7px;
}
li { ul {
border: 2px solid var(--color-fill-4); display: flex; flex-wrap: wrap; padding-left: 7px;
margin-bottom: 10px; }
margin-right: 6px;
padding: 5px;
cursor: pointer;
}
li:hover, li {
li.active { border: 2px solid var(--color-fill-4); margin-bottom: 10px;
border: 2px solid rgb(var(--primary-6)); margin-right: 6px; padding: 5px; cursor: pointer;
} }
& li svg { li:hover, li.active {
width: 2.4em; border: 2px solid rgb(var(--primary-6));
height: 2.4em; }
}
& li svg {
width: 2.4em; height: 2.4em;
}
} }
:deep(.arco-select-option-content) { :deep(.arco-select-option-content) {
width: 100%; width: 100%;
} }
</style> </style>

View File

@@ -1,131 +1,150 @@
<template> <template>
<a-modal <a-modal
:width="prop.width" :width="prop.width"
v-model:visible="modal.visible" :fullscreen="isFull"
:on-before-ok="modal.submit" v-model:visible="modal.visible"
:unmount-on-close="true" :on-before-ok="modal.submit"
@cancel="modal.cancel" :unmount-on-close="true"
@cancel="modal.cancel"
>
<template #title>
{{ prop.title }}
</template>
<slot></slot>
<slot name="body"></slot>
<a-card
class="mt-2"
:hoverable="true"
v-for="info in layout"
> >
<template #title> <ma-info
{{ prop.title }} v-if="isArray(info?.child)"
</template> :title="info.title"
<slot name="body"></slot> :columns="info.child"
<a-card class="mt-2" :hoverable="true" v-for="info in layout"> :data="infoData"
<ma-info ref="maInfoRefs"
v-if="isArray(info?.child)" ></ma-info>
:title="info.title" <a-descriptions
:columns="info.child" :title="info.title"
:data="infoData" v-else
ref="maInfoRefs" >
></ma-info> <component
<a-descriptions :title="info.title" v-else> :data="infoData"
<component :data="infoData" :is="info?.component" /> :is="info?.component"
</a-descriptions>
</a-card>
<ma-form
v-model="form"
ref="maFormRef"
v-if="prop.formColumns.length > 0"
:columns="prop.formColumns"
:options="{ showButtons: false, ...prop.formOptions }"
/> />
</a-modal> </a-descriptions>
</a-card>
<ma-form
v-model="form"
ref="maFormRef"
v-if="prop.formColumns.length > 0"
:columns="prop.formColumns"
:options="{showButtons: false, ...prop.formOptions}"
/>
</a-modal>
</template> </template>
<script setup> <script setup>
import MaInfo from "../ma-info/index.vue" import MaInfo from "../ma-info/index.vue";
import { getCurrentInstance, reactive, ref, watch } from "vue" import {getCurrentInstance, reactive, ref, watch} from "vue";
import { isArray, isFunction, isObject, isString } from "lodash" import {isArray, isFunction, isObject, isString} from "lodash";
import { isComponent } from "@arco-design/web-vue/es/_utils/vue-utils" import {isComponent} from "@arco-design/web-vue/es/_utils/vue-utils";
const emit = defineEmits(["visible", "validateError", "open", "cancel", "close"]) import {setModalSizeEvent} from "@/utils/common";
const emit = defineEmits(["visible", "validateError", "open", "cancel", "close"]);
const app = getCurrentInstance().appContext.app const app = getCurrentInstance().appContext.app
const maFormRef = ref() const maFormRef = ref()
const prop = defineProps({ const prop = defineProps({
width: { type: Number, default: 1200 }, // modal框大小 width: {type: Number, default: 1200}, // modal框大小
title: { type: String, default: "" }, // 弹出框标题 isFull: { type: Boolean, default: false},
column: { type: Array, default: [] }, // ma-form字段 title: { type: String, default: "" }, // 弹出框标题
default_visible: { type: Boolean, default: false }, // 默认隐藏 column: { type: Array, default: [] }, // ma-form字段
options: { type: Object, default: {} }, // ma-form 属性 default_visible: { type: Boolean, default: false}, // 默认隐藏
submit: { type: Function, default: () => {} }, options: { type: Object, default: {} }, // ma-form 属性
layout: { type: Object, default: [] }, submit: { type: Function, default: () => {} },
formOptions: { type: Object, default: {} }, // ma-form-options layout: { type: Object, default:[]},
formColumns: { type: Array, default: [] } formOptions: {type: Object, default: {}}, // ma-form-options
formColumns: { type: Array, default: []}
});
const isFull = ref(prop.isFull)
setModalSizeEvent((config) => {
isFull.value = config.isFull
}) })
const maInfoRefs = ref([]) const maInfoRefs = ref([])
const layout = ref([]) const layout = ref([])
const infoData = ref({}) const infoData = ref({})
const form = ref({}) const form = ref({})
watch( watch(() => prop.layout, val => {
() => prop.layout, layout.value = val
(val) => { },{
layout.value = val immediate: true,
}, })
{
immediate: true
}
)
let columnMap = {} let columnMap = {}
const init = async () => { const init = async () => {
let column = prop.column let column = prop.column
column.forEach((item) => { column.forEach(item => {
columnMap[item.dataIndex] = item columnMap[item.dataIndex] = item
})
if (layout.value.length > 0) {
layout.value.forEach((item) => {
if (item.component) {
app.component(item.component, item.child)
return;
}
item.child.forEach((child, index) => {
if (isString(child)) {
item.child[index] = columnMap[child]
}
})
}) })
if (layout.value.length > 0) { }else{
layout.value.forEach((item) => { layout.value.push({
if (item.component) { title: "",
app.component(item.component, item.child) child: column,
return })
} }
item.child.forEach((child, index) => {
if (isString(child)) {
item.child[index] = columnMap[child]
}
})
})
} else {
layout.value.push({
title: "",
child: column
})
}
} }
const modal = reactive({ const modal = reactive({
visible: prop.default_visible, visible: prop.default_visible,
open(data, formData = {}) { open(data, formData = {}) {
modal.visible = true modal.visible = true;
for (let [key, value] of Object.entries(data)) { for (let [key, value] of Object.entries(data)) {
infoData.value[key] = value infoData.value[key] = value;
}
for (let [key, value] of Object.entries(formData)) {
form.value[key] = value
}
emit("open", infoData)
},
close() {
modal.visible = false
},
async submit() {
let validate = await maFormRef.value.validateForm()
if (validate) {
emit("validateError", validate)
return false
}
return prop.submit(form._rawValue)
},
cancel() {
emit("cancel")
} }
}) for (let [key, value] of Object.entries(formData)) {
form.value[key] = value;
}
emit("open", infoData);
},
close() {
modal.visible = false;
},
async submit() {
let validate = await maFormRef.value.validateForm()
if (validate) {
emit("validateError", validate)
return false
}
return prop.submit(form._rawValue)
},
cancel() {
emit("cancel");
},
});
init() init()
defineExpose({ defineExpose({
open: modal.open, open: modal.open,
close: modal.close, close: modal.close,
data: infoData data: infoData,
}) });
</script> </script>
<style scoped></style> <style scoped>
</style>

View File

@@ -1,129 +1,122 @@
<template> <template>
<a-space direction="vertical" size="large" fill> <a-space direction="vertical" size="large" fill>
<a-descriptions <a-descriptions
:data="descriptions" :data="descriptions"
:column="props.column" :column="props.column"
:title="props.title" :title="props.title"
:layout="props.layout" :layout="props.layout"
:bordered="props.bordered" :bordered="props.bordered"
table-layout="auto" table-layout="fixed"
:size="props.size" :size="props.size"
:label-style="props.labelStyle" :label-style="props.labelStyle"
:value-style="props.valueStyle" :value-style="props.valueStyle"
> >
<a-descriptions-item v-for="item in descriptions" :label="item.label"> <a-descriptions-item v-for="item in descriptions" :label="item.label">
<template v-if="item.formType === 'upload'"> <template v-if="item.formType === 'upload'">
<a-image-preview-group infinite v-if="isArray(item.value)"> <a-image-preview-group infinite v-if="isArray(item.value)">
<a-space> <a-space>
<a-image v-for="src in item.value" :src="tool.viewImage(src)" width="50" /> <a-image v-for="src in item.value" :src="tool.viewImage(src)" width="50" />
</a-space> </a-space>
</a-image-preview-group> </a-image-preview-group>
<a-image v-else :src="tool.viewImage(item.value)" width="50"></a-image> <a-image v-else :src="tool.viewImage(item.value)" width="50"></a-image>
</template> </template>
<template v-else-if="item.infoSlot"> <template v-else-if="item.infoSlot">
<slot :name="item.dataIndex" :row="item" :data="data"></slot> <slot :name="item.dataIndex" :row="item" :data="data"></slot>
</template> </template>
<template <template v-else-if="item.formType === 'radio' || item.formType === 'select'">
v-else-if="(item.multiple === true && item.formType === 'select') || item.formType === 'checkbox'" <a-tag color="blue">{{
> item.dict.data.find((row) => row.value == item.value)?.label
<a-space size="mini" }}</a-tag>
><a-tag color="pinkpurple" v-for="subItem in item.value" :key="subItem"> </template>
{{ item.dict.data.find((row) => row.value == subItem)?.label }} <div v-else>{{ item.value }} {{ item?.textAppend }}</div>
</a-tag> </a-descriptions-item>
</a-space> </a-descriptions>
</template> </a-space>
<template v-else-if="item.formType === 'radio' || item.formType === 'select'">
<a-tag color="blue">{{ item.dict.data.find((row) => row.value == item.value)?.label }}</a-tag>
</template>
<div v-else>{{ item.value }} {{ item?.textAppend }}</div>
</a-descriptions-item>
</a-descriptions>
</a-space>
</template> </template>
<script setup> <script setup>
import { getCurrentInstance, inject, provide, ref, watch } from "vue" import { getCurrentInstance, inject, provide, ref, watch } from "vue";
import tool from "@/utils/tool" import tool from "@/utils/tool";
import { get, isArray, isBoolean, isEmpty, isFunction } from "lodash" import { get, isArray, isBoolean, isEmpty, isFunction } from "lodash";
import { loadDict } from "@cps/ma-form/js/networkRequest.js" import { loadDict } from "@cps/ma-form/js/networkRequest.js";
import globalColumn from "@/config/column.js" import globalColumn from "@/config/column.js";
const dictList = {} const dictList = {};
const app = getCurrentInstance().appContext.app const app = getCurrentInstance().appContext.app;
const columns = ref([]) const columns = ref([]);
const data = ref({}) const data = ref({});
const descriptions = ref([]) const descriptions = ref([]);
const dictData = ref([]) const dictData = ref([]);
const props = defineProps({ const props = defineProps({
columns: { type: Array, default: [] }, columns: { type: Array, default: [] },
data: {}, data: {},
column: { default: 3 }, column: { default: 3 },
title: { title: {
default: "" default: "",
}, },
bordered: { bordered: {
default: true default: true,
}, },
layout: { layout: {
default: "vertical" default: "vertical",
}, },
labelStyle: { labelStyle: {
default: {} default: {},
}, },
valueStyle: { valueStyle: {
default: {} default: {},
}, },
size: { size: {
default: "large" default: "large",
} },
}) });
watch( watch(
() => props.columns, () => props.columns,
(vl) => { (vl) => {
columns.value = vl columns.value = vl;
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
) );
const reset = (vl) => { const reset = async (vl) => {
data.value = vl data.value = vl;
descriptions.value = [] descriptions.value = [];
if (!columns.value) { if (!columns.value) {
return return;
}
for (let item of columns.value) {
let value = null;
if (isEmpty(item) || item.dataIndex === "__operation" || item.infoShow === false) {
return;
} }
columns.value.forEach(async (item) => { if (isBoolean(item.common) && item.common && globalColumn[item.dataIndex]) {
let value = null item = globalColumn[item.dataIndex];
if (isEmpty(item) || item.dataIndex === "__operation" || item.infoShow === false) { }
return if (item.dict) {
} await loadDict(dictList, item);
if (isBoolean(item.common) && item.common && globalColumn[item.dataIndex]) { item.dict.data = dictList[item.dataIndex] ?? [];
item = globalColumn[item.dataIndex] }
} if (isFunction(item.customRender)) {
if (item.dict) { value = item.customRender({ record: data.value });
await loadDict(dictList, item) }
item.dict.data = dictList[item.dataIndex] ?? [] descriptions.value.push({
} ...item,
if (isFunction(item.customRender)) { label: item.title,
value = item.customRender({ record: data.value }) value: value ?? get(data.value, item.dataIndex),
} });
descriptions.value.push({ };
...item, };
label: item.title,
value: value ?? get(data.value, item.dataIndex)
})
})
}
watch( watch(
() => props.data, () => props.data,
(vl) => { (vl) => {
reset(vl) reset(vl);
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
) );
defineExpose({ reset }) defineExpose({ reset });
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -1,88 +1,81 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<div class="inline-block"> <div class="inline-block">
<a-input-group class="w-full"> <a-input-group class="w-full">
<a-trigger position="bottom" auto-fit-position :unmount-on-close="false"> <a-trigger position="bottom" auto-fit-position :unmount-on-close="false">
<a-input v-model="inputValue" placeholder="请点击左侧按钮选择资源" readonly v-if="!props.multiple" /> <a-input v-model="inputValue" placeholder="请点击左侧按钮选择资源" readonly v-if="! props.multiple" />
<a-button v-else>预览已选</a-button> <a-button v-else>预览已选</a-button>
<template #content> <template #content>
<div class="trigger-content"> <div class="trigger-content">
<a-empty v-if="list && list.length == 0" /> <a-empty v-if="list && list.length == 0" />
<a-image :src="inputValue" v-else-if="list && !isArray(list)" /> <a-image :src="inputValue" v-else-if="list && ! isArray(list)" />
<div v-else> <div v-else>
<a-image-preview-group infinite> <a-image-preview-group infinite>
<a-space> <a-space>
<a-image :src="item" v-for="(item, index) in list" width="100%" :key="index" /> <a-image :src="item" v-for="(item, index) in list" width="100%" :key="index" />
</a-space> </a-space>
</a-image-preview-group> </a-image-preview-group>
</div> </div>
</div> </div>
</template> </template>
</a-trigger> </a-trigger>
<a-button type="primary" @click="visible = true"><icon-experiment /> 资源选择器</a-button> <a-button type="primary" @click="visible = true"><icon-experiment /> 资源选择器</a-button>
</a-input-group> </a-input-group>
<a-modal v-model:visible="visible" :width="props.width" :footer="false" draggable> <a-modal v-model:visible="visible" :width="props.width" :footer="false" draggable>
<template #title>资源选择器</template> <template #title>资源选择器</template>
<ma-resource v-model="list" :multiple="props.multiple" :only-data="props.onlyData" /> <ma-resource v-model="list" :multiple="props.multiple" :only-data="props.onlyData" />
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, watch } from "vue" import { onMounted, ref, watch } from 'vue'
import MaResource from "@cps/ma-resource/index.vue" import MaResource from '@cps/ma-resource/index.vue'
import { isArray } from "lodash" import { isArray } from 'lodash'
const list = ref() const list = ref()
const visible = ref(false) const visible = ref(false)
const inputValue = ref("") const inputValue = ref('')
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(['update:modelValue'])
const props = defineProps({ const props = defineProps({
modelValue: { type: [String, Array] }, modelValue: { type: [ String, Array ] },
multiple: { type: Boolean, default: true }, multiple: { type: Boolean, default: true },
onlyData: { type: Boolean, default: true }, onlyData: { type: Boolean, default: true },
width: { type: Number, default: 1080 } width: { type: Number, default: 1080 },
}) })
list.value = props.modelValue list.value = props.modelValue
// onMounted(() => ) // onMounted(() => )
watch( watch(
() => list.value, () => list.value,
(vl) => { vl => {
emit("update:modelValue", list.value) emit('update:modelValue', list.value)
if (props.multiple) { if ( props.multiple ) {
console.log(vl) inputValue.value = isArray(list) ? list.value.join(',') : []
inputValue.value = isArray(list) ? list.value.join(",") : [] } else {
} else { inputValue.value = list.value
inputValue.value = list.value }
} visible.value = false
visible.value = false
}, },
{ deep: true } { deep: true }
) )
</script> </script>
<style scoped> <style scoped>
.trigger-content { .trigger-content {
margin-top: 1px; margin-top: 1px;
background: var(--color-fill-1); background: var(--color-fill-1);
border: 1px solid var(--color-fill-3); border: 1px solid var(--color-fill-3);
width: 340px; width: 340px;
border-radius: var(--border-radius-medium); border-radius: var(--border-radius-medium);
} }
:deep(.arco-space) { :deep(.arco-space) {
display: block; display: block; margin-bottom: 5px;
margin-bottom: 5px;
} }
</style> </style>

View File

@@ -1,18 +1,22 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<div class="w-full p-2 resource-container h-full lg:flex lg:justify-between rounded-sm"> <div class="w-full p-2 resource-container h-full lg:flex lg:justify-between rounded-sm">
<a-modal v-model:visible="openNetworkModal" ok-text="保存" :on-before-ok="saveNetworkImg" draggable> <a-modal v-model:visible="openNetworkModal" :ok-text="$t('sys.save')" :on-before-ok="saveNetworkImg" draggable>
<template #title>保存网络图片</template> <template #title>{{ $t("maResource.saveNetworkImage") }}</template>
<a-input v-model="networkImg" class="mb-3" placeholder="请粘贴网络图片地址" allow-clear /> <a-input v-model="networkImg" class="mb-3" :placeholder="$t('maResource.networkImageNotice')" allow-clear />
<a-image :src="networkImg" width="100%" style="min-height: 150px" /> <a-image :src="networkImg ?? ''" width="100%" style="min-height: 150px" />
</a-modal> </a-modal>
<div class="lg:w-1/5 w-full p-2 shadow"> <div class="lg:w-1/5 w-full p-2 shadow">
<ma-tree-slider <ma-tree-slider
v-model="sliderData" :data="sliderData"
search-placeholder="搜索资源类型" :search-placeholder="$t('maResource.searchResource')"
:field-names="{ title: 'title', key: 'key' }" :field-names="{ title: 'title', key: 'key' }"
@click="handlerClick" @click="handlerClick"
icon="icon-folder" icon="icon-folder"
:selected-keys="['all']" v-model="defaultKey"
/> />
</div> </div>
<div class="w-full lg:ml-3 mt-3 lg:mt-2 flex flex-col"> <div class="w-full lg:ml-3 mt-3 lg:mt-2 flex flex-col">
@@ -20,17 +24,17 @@
<div class="flex"> <div class="flex">
<ma-upload v-model="uploadFile" multiple :show-list="false" type="chunk" :resource="false" /> <ma-upload v-model="uploadFile" multiple :show-list="false" type="chunk" :resource="false" />
<a-button class="ml-3" @click="openNetworkModal = true"> <a-button class="ml-3" @click="openNetworkModal = true">
<icon-image /> 保存网络图片 <icon-image /> {{ $t("maResource.saveNetworkImage") }}
</a-button> </a-button>
</div> </div>
<a-input <a-input
v-model="filename" v-model="filename"
class="input-search lg:mt-0 mt-2" class="input-search lg:mt-0 mt-2"
placeholder="文件名搜索" :placeholder="$t('maResource.searchFileNotice')"
@press-enter="searchFile" @press-enter="searchFile"
/> />
</div> </div>
<a-spin :loading="resourceLoading" tip="数据加载中..." class="h-full"> <a-spin :loading="resourceLoading" :tip="$t('maResource.loadingText')" class="h-full">
<div class="resource-list mt-2" ref="rl" v-if="attachmentList && attachmentList.length > 0"> <div class="resource-list mt-2" ref="rl" v-if="attachmentList && attachmentList.length > 0">
<div <div
class="item rounded-sm" class="item rounded-sm"
@@ -72,7 +76,9 @@
v-model:page-size="pageSize" v-model:page-size="pageSize"
@change="changePage" @change="changePage"
/> />
<a-button type="primary" @click="selectComplete" class="mt-3 lg:mt-0">选择完成</a-button> <a-button type="primary" @click="selectComplete" class="mt-3 lg:mt-0">{{
$t("maResource.ok")
}}</a-button>
</div> </div>
</div> </div>
</div> </div>
@@ -84,10 +90,13 @@ import MaUpload from "@cps/ma-upload/index.vue"
import uploadConfig from "@/config/upload" import uploadConfig from "@/config/upload"
import MaTreeSlider from "@cps/ma-treeSlider/index.vue" import MaTreeSlider from "@cps/ma-treeSlider/index.vue"
import commonApi from "@/api/common" import commonApi from "@/api/common"
import { xor } from "lodash"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
const { t } = useI18n()
const sliderData = ref([]) const sliderData = ref([])
const defaultKey = ref(["all"])
const uploadFile = ref() const uploadFile = ref()
const attachmentList = ref([]) const attachmentList = ref([])
const openNetworkModal = ref(false) const openNetworkModal = ref(false)
@@ -108,7 +117,7 @@ const props = defineProps({
modelValue: { type: [String, Array] }, modelValue: { type: [String, Array] },
multiple: { type: Boolean, default: true }, multiple: { type: Boolean, default: true },
onlyData: { type: Boolean, default: true }, onlyData: { type: Boolean, default: true },
returnType: { type: String, default: "url" } returnType: { type: String, default: "hash" }
}) })
onMounted(async () => { onMounted(async () => {
@@ -140,6 +149,7 @@ const handlerItemClick = (e) => {
} }
const handlerClick = async (item) => { const handlerClick = async (item) => {
defaultKey.value = item
const type = item[0] === "all" ? undefined : item[0] const type = item[0] === "all" ? undefined : item[0]
await getAttachmentList({ mime_type: type }) await getAttachmentList({ mime_type: type })
} }
@@ -149,13 +159,9 @@ const searchFile = async () => {
} }
const selectFile = (item, index) => { const selectFile = (item, index) => {
if (!props.multiple && selecteds.value) {
if (props.onlyData && item.url != selecteds.value) return
if (!props.onlyData && item.id != selecteds.value.id) return
}
const children = rl.value.children const children = rl.value.children
const className = "item rounded-sm" const className = "item rounded-sm"
if (!/^(http|https)/g.test(item.url)) { if (!/^(http|https)/g.test(item.url)) {
item.url = tool.attachUrl(item.url, getStoreMode(item.storage_mode)) item.url = tool.attachUrl(item.url, getStoreMode(item.storage_mode))
} }
@@ -163,18 +169,20 @@ const selectFile = (item, index) => {
children[index].className = className children[index].className = className
if (props.multiple) { if (props.multiple) {
selecteds.value.map((file, idx) => { selecteds.value.map((file, idx) => {
if (props.onlyData && file == item.url) selecteds.value.splice(idx, 1) selecteds.value.splice(idx, 1)
if (!props.onlyData && file.id == item.id) selecteds.value.splice(idx, 1)
}) })
} else { } else {
selecteds.value = "" selecteds.value = ""
} }
} else { } else {
children[index].className = className + " active"
if (props.multiple) { if (props.multiple) {
children[index].className = className + " active"
selecteds.value.push(props.onlyData ? item[props.returnType] : item) selecteds.value.push(props.onlyData ? item[props.returnType] : item)
} else { } else {
selecteds.value = props.onlyData ? item[props.returnType] : item if (document.querySelectorAll(".item.active").length < 1) {
children[index].className = className + " active"
selecteds.value = props.onlyData ? item[props.returnType] : item
}
} }
} }
} }
@@ -204,7 +212,7 @@ const changePage = async (page) => {
const saveNetworkImg = async (done) => { const saveNetworkImg = async (done) => {
if (!networkImg.value) { if (!networkImg.value) {
Message.error("请粘贴网络图片地址") Message.error(t("maResource.networkImageNotice"))
done(false) done(false)
return return
} }

View File

@@ -1,112 +1,114 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<div class="flex flex-col w-full h-full"> <div class="flex flex-col w-full h-full">
<a-input-group class="mb-2 w-full flex items-center" size="mini"> <a-input-group class="mb-2 w-full" size="mini">
<a-input <a-input
:placeholder="props?.searchPlaceholder" :placeholder="props?.searchPlaceholder"
allow-clear allow-clear
@input="changeKeyword" @input="changeKeyword"
@clear="resetData" @clear="resetData"
style="height: 100%" />
/> <a-button
<a-button @click="() => {
@click=" isExpand ? maTree.expandAll(false) : maTree.expandAll(true)
() => { isExpand = ! isExpand
isExpand ? maTree.expandAll(false) : maTree.expandAll(true) }"
isExpand = !isExpand >{{ isExpand ? '折叠' : '展开' }}</a-button>
} </a-input-group>
" <a-tree
>{{ isExpand ? "折叠" : "展开" }}</a-button blockNode
> ref="maTree"
</a-input-group> :data="treeData"
<a-tree class="h-full w-full"
blockNode @select="handlerSelect"
:data="treeData" :field-names="props.fieldNames"
class="h-full w-full" v-model:selected-keys="modelValue"
@select="handlerSelect" v-bind="$attrs"
:field-names="props.fieldNames" >
v-model:selected-keys="selectedKeys" <template #icon v-if="props.icon"><component :is="props.icon" /></template>
ref="maTree" </a-tree>
v-bind="$attrs" </div>
>
<template #icon v-if="props.icon"><component :is="props.icon" /></template>
</a-tree>
</div>
</template> </template>
<script setup> <script setup>
import { ref, watch, onMounted } from "vue" import { ref, watch, computed } from 'vue'
const treeData = ref([]) const treeData = ref([])
const selectedKeys = ref([]) const maTree = ref()
const maTree = ref() const isExpand = ref(false)
const isExpand = ref(false)
const emit = defineEmits(["click"]) const emit = defineEmits(['update:modelValue', 'click'])
const props = defineProps({ const props = defineProps({
modelValue: { type: Array }, modelValue: { type: Array },
data: { type: Array },
searchPlaceholder: { type: String }, searchPlaceholder: { type: String },
selectedKeys: { type: Array }, fieldNames: { type: Object, default: () => { return { title: 'label', key: 'value' } } },
fieldNames: { icon: { type: String, default: undefined },
type: Object, })
default: () => {
return { title: "label", value: "code" } const modelValue = computed({
} get() {
return props.modelValue
}, },
icon: { type: String, default: undefined } set(newVal) {
}) emit('update:modelValue', newVal)
onMounted(() => (selectedKeys.value = props.selectedKeys))
watch(
() => props.modelValue,
(val) => {
treeData.value = val
} }
) })
const handlerSelect = (item, data) => { watch(
selectedKeys.value = [item] () => props.data,
emit("click", ...[item, data]) val => {
} treeData.value = val
},
{ immediate: true, deep: true }
)
const resetData = () => (treeData.value = props.modelValue) const handlerSelect = (item, data) => {
modelValue.value = [ item ]
emit('click', ...[item, data])
}
const changeKeyword = (keyword) => { const resetData = () => treeData.value = props.data
if (!keyword || keyword === "") {
treeData.value = Object.assign(props.modelValue, []) const changeKeyword = (keyword) => {
return false if (!keyword || keyword === '') {
treeData.value = Object.assign(props.data, [])
return false
} }
treeData.value = searchNode(keyword) treeData.value = searchNode(keyword)
} }
const searchNode = (keyword) => { const searchNode = (keyword) => {
const loop = (data) => { const loop = (data) => {
let tree = [] let tree = []
data.map((item) => { data.map(item => {
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
const temp = loop(item.children) const temp = loop(item.children)
tree.push(...temp) tree.push(...temp)
} else if (item[props.fieldNames["title"]].indexOf(keyword) !== -1) { } else if (item[props.fieldNames['title']].indexOf(keyword) !== -1) {
tree.push(item) tree.push(item)
} }
return tree
})
return tree return tree
} })
return loop(props.modelValue)
}
defineExpose({ maTree }) return tree
}
return loop(treeData.value)
}
defineExpose({ maTree })
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
:deep(.arco-tree-node:hover) { :deep(.arco-tree-node:hover) {
background-color: var(--color-fill-2); background-color: var(--color-fill-2);
border-radius: 2px; border-radius: 3px;
} }
:deep(.arco-tree-node-switcher) { :deep(.arco-tree-node-switcher) {
margin-left: 2px; margin-left: 2px;
} }
</style> </style>

View File

@@ -18,9 +18,9 @@
<div> <div>
<icon-upload class="text-5xl text-gray-400" /> <icon-upload class="text-5xl text-gray-400" />
<div class="text-red-600 font-bold"> <div class="text-red-600 font-bold">
{{ config.title === "buttonText" ? '本地上传' : config.title }} {{ config.title === "buttonText" ? $t("upload.buttonText") : config.title }}
</div> </div>
将文件拖到此处<span style="color: #3370ff">点击上传</span> {{ $t("upload.uploadDesc") }}<span style="color: #3370ff">{{ $t("upload.clickUpload") }}</span>
</div> </div>
</div> </div>
</slot> </slot>
@@ -35,17 +35,26 @@
animation animation
type="circle" type="circle"
size="mini" size="mini"
show-text
class="progress" class="progress"
/> />
<a-button class="delete" @click="removeSignFile()" v-if="currentItem.percent === 100"> <span v-if="currentItem.percent < 100">上传中...</span>
<template #icon><icon-delete /></template>
<a-tooltip content="点击文件名预览/下载" position="tr">
<a
:href="currentItem.url"
v-if="currentItem?.url && currentItem.percent === 100 && currentItem?.status === 'complete'"
class="file-name"
target="_blank"
>{{ currentItem.name }}</a
>
</a-tooltip>
<a-button type="text" size="small" @click="removeSignFile()" v-if="currentItem.percent === 100">
<template #icon>
<icon-delete />
</template>
</a-button> </a-button>
<div
v-if="currentItem?.url && currentItem.percent === 100 && currentItem?.status === 'complete'"
class="file-item"
>
{{ currentItem.url }}
</div>
</div> </div>
<!-- 多文件 --> <!-- 多文件 -->
@@ -58,26 +67,39 @@
size="mini" size="mini"
class="progress" class="progress"
/> />
<a-button class="delete" @click="removeFile(idx)" v-if="file.percent === 100"> <span v-if="file.percent < 100">上传中...</span>
<template #icon><icon-delete /></template>
<a-tooltip content="点击文件名预览/下载" position="tr">
<a
:href="file.url"
v-if="file?.url && file.percent === 100 && file?.status === 'complete'"
class="file-name"
target="_blank"
>{{ file.name }}</a
>
</a-tooltip>
<a-button type="text" size="small" @click="removeFile(idx)" v-if="file.percent === 100">
<template #icon>
<icon-delete />
</template>
</a-button> </a-button>
<div v-if="file?.url && file.percent === 100 && file?.status === 'complete'" class="file-item">
{{ file.url }}
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import commonApi from "@/api/common" import commonApi from "@/api/common"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { isArray } from "lodash" import { isArray, throttle } from "lodash"
import { getFileUrl } from "../js/utils" import { getFileUrl } from "../js/utils"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import file2md5 from "file2md5" import file2md5 from "file2md5"
const props = defineProps({ const props = defineProps({
modelValue: { type: [String, Number, Array], default: () => {} } modelValue: {
type: [String, Number, Array],
default: () => {}
}
}) })
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(["update:modelValue"])
const config = inject("config") const config = inject("config")
@@ -123,6 +145,7 @@ const chunkUpload = async (options) => {
if (res.data && res.data.hash) { if (res.data && res.data.hash) {
res.data.url = tool.attachUrl(res.data.url, storageMode[res.data.storage_mode]) res.data.url = tool.attachUrl(res.data.url, storageMode[res.data.storage_mode])
if (config.multiple) { if (config.multiple) {
showFileList.value[idx].name = res.data.origin_name
showFileList.value[idx].percent = 100 showFileList.value[idx].percent = 100
showFileList.value[idx].status = "complete" showFileList.value[idx].status = "complete"
showFileList.value[idx].url = res.data.url showFileList.value[idx].url = res.data.url
@@ -145,7 +168,7 @@ const chunkUpload = async (options) => {
return return
} }
if (res.data && res.data.code && res.data.code === 201) { if (res.data && res.data.code && res.data.code === 201) {
const percent = parseFloat((1 / chunks).toFixed(2)) const percent = Math.floor((1 / chunks) * 10000) / 10000
if (config.multiple) { if (config.multiple) {
showFileList.value[idx].percent += percent showFileList.value[idx].percent += percent
} else { } else {
@@ -175,32 +198,49 @@ const removeFile = (idx) => {
emit("update:modelValue", files) emit("update:modelValue", files)
} }
const init = async () => { const init = throttle(async () => {
if (config.multiple) { if (config.multiple) {
if (isArray(props.modelValue) && props.modelValue.length > 0) { if (isArray(props.modelValue) && props.modelValue.length > 0) {
const result = await props.modelValue.map(async (item) => { const result = await props.modelValue.map(async (item) => {
return await getFileUrl(config.returnType, item, storageMode) return await getFileUrl(config.returnType, item, storageMode)
}) })
const results = await Promise.all(result) const data = await Promise.all(result)
showFileList.value = results.map((item) => {
return { let fileItemObj = { percent: 100, status: "complete" }
...item, if (config.returnType === "url") {
percent: 100, showFileList.value = data.map((url) => {
status: "complete" return { url, name: url.substring(url.lastIndexOf("/") + 1), ...fileItemObj }
} })
}) } else {
showFileList.value = data.map((item) => {
return {
name: item.origin_name,
[config.returnType]: item[config.returnType],
url: item.url,
...fileItemObj
}
})
}
} else { } else {
showFileList.value = [] showFileList.value = []
} }
} else if (props.modelValue) { } else if (props.modelValue) {
signFile.value = props.modelValue if (config.returnType === "url") {
getFileUrl(config.returnType, props.modelValue, storageMode).then((item) => (currentItem.value.url = item)) signFile.value = props.modelValue
currentItem.value.url = props.modelValue
currentItem.value.name = props.modelValue?.substring((props.modelValue?.lastIndexOf("/") || 0) + 1)
} else {
const result = await getFileUrl(config.returnType, props.modelValue, storageMode)
signFile.value = result.url
currentItem.value.url = result.url
currentItem.value.name = result.origin_name
}
currentItem.value.percent = 100 currentItem.value.percent = 100
currentItem.value.status = "complete" currentItem.value.status = "complete"
} else { } else {
removeSignFile() removeSignFile()
} }
} }, 1000)
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -215,25 +255,21 @@ watch(
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.file-list { .file-list {
position: relative;
background-color: var(--color-primary-light-1); background-color: var(--color-primary-light-1);
border-radius: 2px; border-radius: 4px;
height: 36px; height: 36px;
line-height: 36px; padding: 0 5px;
padding: 0 10px;
width: 100%; width: 100%;
.delete { display: flex;
position: absolute; flex-direction: row;
z-index: 99; justify-content: space-between;
right: 2px; align-items: center;
top: 2px;
}
.progress { .file-name {
position: absolute; max-width: 90%;
left: 30px; margin: 0 5px;
top: 50%; overflow: hidden;
transform: translateX(-50%) translateY(-50%); color: #165dff;
} }
} }
</style> </style>

View File

@@ -19,7 +19,7 @@
<div> <div>
<icon-upload class="text-5xl text-gray-400" /> <icon-upload class="text-5xl text-gray-400" />
<div class="text-red-600 font-bold"> <div class="text-red-600 font-bold">
{{ config.title === "buttonText" ? "本地上传" : config.title }} {{ config.title === "buttonText" ? $t("upload.buttonText") : config.title }}
</div> </div>
将文件拖到此处<span style="color: #3370ff">点击上传</span> 将文件拖到此处<span style="color: #3370ff">点击上传</span>
</div> </div>
@@ -30,33 +30,48 @@
</div> </div>
<!-- 单文件 --> <!-- 单文件 -->
<div class="file-list mt-2" v-if="!config.multiple && currentItem?.url && config.showList"> <div class="file-list mt-2" v-if="!config.multiple && currentItem?.url && config.showList">
<a-button class="delete" @click="removeSignFile()"> <a-tooltip content="点击文件名预览/下载" position="tr">
<template #icon><icon-delete /></template> <a
:href="currentItem.url"
v-if="currentItem?.url && currentItem.percent === 100 && currentItem?.status === 'complete'"
class="file-name"
target="_blank"
>{{ currentItem.name }}</a
>
</a-tooltip>
<a-button type="text" size="small" @click="removeSignFile()" v-if="currentItem.percent === 100">
<template #icon>
<icon-delete />
</template>
</a-button> </a-button>
<div class="file-item">
{{ currentItem.url }}
</div>
</div> </div>
<!-- 多文件 --> <!-- 多文件 -->
<div v-if="config.showList" class="file-list mt-2" v-for="(file, idx) in showFileList" :key="idx"> <div v-if="config.showList" class="file-list mt-2" v-for="(file, idx) in showFileList" :key="idx">
<a-button class="delete" @click="removeFile(idx)"> <a-tooltip content="点击文件名预览/下载" position="tr">
<template #icon><icon-delete /></template> <a :href="file.url" v-if="file?.url" class="file-name" target="_blank">{{ file.name }}</a>
</a-tooltip>
<a-button type="text" size="small" @click="removeFile(idx)">
<template #icon>
<icon-delete />
</template>
</a-button> </a-button>
<div class="file-item">
{{ file.url }}
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { isArray } from "lodash" import { isArray, throttle } from "lodash-es"
import { getFileUrl, uploadRequest } from "../js/utils" import { getFileUrl, uploadRequest } from "../js/utils"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
const props = defineProps({ const props = defineProps({
modelValue: { type: [String, Number, Array], default: () => {} } modelValue: {
type: [String, Number, Array],
default: () => {}
}
}) })
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(["update:modelValue"])
const config = inject("config") const config = inject("config")
@@ -72,7 +87,7 @@ const uploadFileHandler = async (options) => {
} }
const file = options.fileItem.file const file = options.fileItem.file
if (file.size > config.size) { if (file.size > config.size) {
Message.warning(file.name + " " + "文件大小超过了限制") Message.warning(file.name + " " + t("upload.sizeLimit"))
currentItem.value = {} currentItem.value = {}
return return
} }
@@ -111,25 +126,45 @@ const removeFile = (idx) => {
emit("update:modelValue", files) emit("update:modelValue", files)
} }
const init = async () => { const init = throttle(async () => {
if (config.multiple) { if (config.multiple) {
if (isArray(props.modelValue) && props.modelValue.length > 0) { if (isArray(props.modelValue) && props.modelValue.length > 0) {
const result = await props.modelValue.map(async (item) => { const result = await props.modelValue.map(async (item) => {
return await getFileUrl(config.returnType, item, storageMode) return await getFileUrl(config.returnType, item, storageMode)
}) })
showFileList.value = await Promise.all(result) const data = await Promise.all(result)
if (config.returnType === "url") {
showFileList.value = data.map((url) => {
return { url, name: url.substring(url.lastIndexOf("/") + 1) }
})
} else {
showFileList.value = data.map((item) => {
return {
url: item.url,
[config.returnType]: item[config.returnType],
name: item.origin_name
}
})
}
} else { } else {
showFileList.value = [] showFileList.value = []
} }
} else if (props.modelValue) { } else if (props.modelValue) {
signFile.value = props.modelValue if (config.returnType === "url") {
getFileUrl(config.returnType, props.modelValue, storageMode).then((item) => (currentItem.value.url = item)) signFile.value = props.modelValue
currentItem.value.url = props.modelValue
} else {
const result = await getFileUrl(config.returnType, props.modelValue, storageMode)
signFile.value = result.url
currentItem.value.url = result.url
currentItem.value.name = result.origin_name
}
currentItem.value.percent = 100 currentItem.value.percent = 100
currentItem.value.status = "complete" currentItem.value.status = "complete"
} else { } else {
removeSignFile() removeSignFile()
} }
} }, 1000)
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -145,25 +180,21 @@ watch(
<style lang="less" scoped> <style lang="less" scoped>
.file-list { .file-list {
position: relative;
background-color: var(--color-primary-light-1); background-color: var(--color-primary-light-1);
border-radius: 2px; border-radius: 4px;
height: 36px; height: 36px;
line-height: 36px; padding: 0 5px;
padding: 0 10px;
width: 100%; width: 100%;
.delete { display: flex;
position: absolute; flex-direction: row;
z-index: 99; justify-content: space-between;
right: 2px; align-items: center;
top: 2px;
}
.progress { .file-name {
position: absolute; max-width: 90%;
left: 30px; margin: 0 5px;
top: 50%; overflow: hidden;
transform: translateX(-50%) translateY(-50%); color: #165dff;
} }
} }
</style> </style>

View File

@@ -1,63 +1,79 @@
<template> <template>
<div class="upload-image flex"> <div class="upload-image flex">
<!-- 单图 --> <!-- 单图 -->
<div <a-space wrap>
:class="'image-list ' + (config.rounded ? 'rounded-full' : '')"
v-if="!config.multiple && currentItem?.url && config.showList"
>
<a-button class="delete" @click="removeSignImage()">
<template #icon><icon-delete /></template>
</a-button>
<a-image width="130" height="130" :class="config.rounded ? 'rounded-full' : ''" :src="currentItem.url" />
</div>
<!-- 多图显示 -->
<a-space v-else-if="config.multiple && config.showList" :class="showImgList.length > 0 ? 'mr-2' : ''" wrap>
<div <div
:class="'image-list ' + (config.rounded ? 'rounded-full' : '')" :class="'image-list ' + (config.rounded ? 'rounded-full' : '')"
v-for="(image, idx) in showImgList" v-if="!config.multiple && currentItem?.url && config.showList"
:key="idx"
> >
<a-button class="delete" @click="removeImage(idx)"> <a-button class="delete" @click="removeSignImage()">
<template #icon><icon-delete /></template> <template #icon>
<icon-delete />
</template>
</a-button> </a-button>
<a-image width="130" height="130" :class="config.rounded ? 'rounded-full' : ''" :src="image.url" /> <a-image
width="130"
height="130"
:class="config.rounded ? 'rounded-full' : ''"
:src="currentItem.url"
/>
</div> </div>
</a-space> <!-- 多图显示 -->
<template v-else-if="config.multiple && config.showList">
<a-upload <div
:custom-request="uploadImageHandler" :class="'image-list ' + (config.rounded ? 'rounded-full' : '')"
:show-file-list="false" v-for="(image, idx) in showImgList"
:multiple="config.multiple" :key="idx"
:accept="config.accept ?? '.jpg,jpeg,.gif,.png,.svg,.bpm'" >
:disabled="config.disabled" <a-button class="delete" @click="removeImage(idx)">
:tip="config.tip" <template #icon>
:limit="config.limit" <icon-delete />
> </template>
<template #upload-button> </a-button>
<slot name="customer"> <a-image width="130" height="130" :class="config.rounded ? 'rounded-full' : ''" :src="image.url" />
<div </div>
:class="'upload-skin ' + (config.rounded ? 'rounded-full' : 'rounded-sm')"
v-if="!props.modelValue || config.multiple"
>
<div class="icon text-3xl"><component :is="config.icon" /></div>
<div class="title">
{{ config.title === "buttonText" ? "本地上传" : config.title }}
</div>
</div>
</slot>
</template> </template>
</a-upload>
<a-upload
:custom-request="uploadImageHandler"
:show-file-list="false"
:multiple="config.multiple"
:accept="config.accept ?? '.jpg,jpeg,.gif,.png,.svg,.bpm'"
:disabled="config.disabled"
:tip="config.tip"
:limit="config.limit"
>
<template #upload-button>
<slot name="customer">
<div
:class="'upload-skin ' + (config.rounded ? 'rounded-full' : 'rounded-sm')"
v-if="!props.modelValue || config.multiple"
>
<div class="icon text-3xl">
<component :is="config.icon" />
</div>
<div class="title">
{{ config.title === "buttonText" ? $t("upload.buttonText") : config.title }}
</div>
</div>
</slot>
</template>
</a-upload>
</a-space>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, inject, watch } from "vue" import { ref, inject, watch } from "vue"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { isArray } from "lodash" import { isArray, throttle } from "lodash-es"
import { getFileUrl, uploadRequest } from "../js/utils" import { getFileUrl, uploadRequest } from "../js/utils"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
const props = defineProps({ const props = defineProps({
modelValue: { type: [String, Number, Array], default: () => {} } modelValue: {
type: [String, Number, Array],
default: () => {}
}
}) })
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(["update:modelValue"])
const config = inject("config") const config = inject("config")
@@ -73,7 +89,7 @@ const uploadImageHandler = async (options) => {
} }
const file = options.fileItem.file const file = options.fileItem.file
if (file.size > config.size) { if (file.size > config.size) {
Message.warning(file.name + " " + "文件大小超过了限制") Message.warning(file.name + " " + t("upload.sizeLimit"))
currentItem.value = {} currentItem.value = {}
return return
} }
@@ -111,25 +127,40 @@ const removeImage = (idx) => {
emit("update:modelValue", files) emit("update:modelValue", files)
} }
const init = async () => { const init = throttle(async () => {
if (config.multiple) { if (config.multiple) {
if (isArray(props.modelValue) && props.modelValue.length > 0) { if (isArray(props.modelValue) && props.modelValue.length > 0) {
const result = await props.modelValue.map(async (item) => { const result = await props.modelValue.map(async (item) => {
return await getFileUrl(config.returnType, item, storageMode) return await getFileUrl(config.returnType, item, storageMode)
}) })
showImgList.value = await Promise.all(result) const data = await Promise.all(result)
if (config.returnType === "url") {
showImgList.value = data.map((url) => {
return { url }
})
} else {
showImgList.value = data.map((item) => {
return { url: item.url, [config.returnType]: item[config.returnType] }
})
}
} else { } else {
showImgList.value = [] showImgList.value = []
} }
} else if (props.modelValue) { } else if (props.modelValue) {
signImage.value = props.modelValue if (config.returnType === "url") {
getFileUrl(config.returnType, props.modelValue, storageMode).then((item) => (currentItem.value.url = item)) signImage.value = props.modelValue
currentItem.value.url = props.modelValue
} else {
const result = await getFileUrl(config.returnType, props.modelValue, storageMode)
signImage.value = result.url
currentItem.value.url = result.url
}
currentItem.value.percent = 100 currentItem.value.percent = 100
currentItem.value.status = "complete" currentItem.value.status = "complete"
} else { } else {
removeSignImage() removeSignImage()
} }
} }, 1000)
watch( watch(
() => props.modelValue, () => props.modelValue,
@@ -153,6 +184,7 @@ watch(
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.icon, .icon,
.title { .title {
color: var(--color-text-3); color: var(--color-text-3);
@@ -165,6 +197,7 @@ watch(
background-color: var(--color-fill-2); background-color: var(--color-fill-2);
width: 130px; width: 130px;
height: 130px; height: 130px;
.delete { .delete {
position: absolute; position: absolute;
z-index: 99; z-index: 99;
@@ -186,6 +219,7 @@ watch(
display: block; display: block;
} }
} }
.upload-skin:hover { .upload-skin:hover {
border: 1px dashed rgb(var(--primary-6)); border: 1px dashed rgb(var(--primary-6));
} }

View File

@@ -1,63 +1,62 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<div> <div>
<ma-image-upload v-if="props.type === 'image'" v-model="file" /> <ma-image-upload v-if="props.type === 'image'" v-model="file" />
<ma-file-upload v-if="props.type === 'file'" v-model="file" /> <ma-file-upload v-if="props.type === 'file'" v-model="file" />
<ma-chunk-upload v-if="props.type === 'chunk'" v-model="file" /> <ma-chunk-upload v-if="props.type === 'chunk'" v-model="file" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, watch, provide } from "vue" import { ref, watch, provide } from 'vue'
import { Message } from "@arco-design/web-vue" import { Message } from '@arco-design/web-vue'
import uploadConfig from "@/config/upload" import uploadConfig from '@/config/upload'
import MaImageUpload from "./components/image-upload.vue" import MaImageUpload from './components/image-upload.vue'
import MaFileUpload from "./components/file-upload.vue" import MaFileUpload from './components/file-upload.vue'
import MaChunkUpload from "./components/chunk-upload.vue" import MaChunkUpload from './components/chunk-upload.vue'
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(['update:modelValue'])
const file = ref() const file = ref()
const props = defineProps({ const props = defineProps({
modelValue: { type: [String, Number, Array], default: () => {} }, modelValue: { type: [ String, Number, Array ], default: () => {} },
title: { type: String, default: "buttonText" }, title: { type: String, default: 'buttonText', },
icon: { type: String, default: "icon-plus" }, icon: { type: String, default: 'icon-plus'},
rounded: { type: Boolean, default: false }, rounded: { type: Boolean, default: false },
multiple: { type: Boolean, default: false }, multiple: { type: Boolean, default: false },
disabled: { type: Boolean, default: false }, disabled: { type: Boolean, default: false },
draggable: { type: Boolean, default: false }, draggable: { type: Boolean, default: false },
size: { type: Number, default: 4 * 1024 * 1024 }, size: { type: Number, default: 4 * 1024 * 1024 },
chunk: { type: Boolean, default: false }, chunk: { type: Boolean, default: false },
chunkSize: { type: Number, default: 1 * 1024 * 1024 }, chunkSize: { type: Number, default: 1 * 1024 * 1024 },
limit: { type: Number, default: 0 }, limit: { type: Number, default: 0 },
tip: { type: String, default: undefined }, tip: { type: String, default: undefined },
type: { type: String, default: "image" }, type: { type: String, default: 'image' },
accept: { type: String, default: "*" }, accept: { type: String, default: '*' },
returnType: { type: String, default: "url" }, returnType: { type: String, default: 'hash' },
fileType: { type: String, default: "button" }, fileType: { type: String, default: 'button' },
showList: { type: Boolean, default: true }, showList: { type: Boolean, default: true },
requestData: { type: Object, default: {} } requestData: { type: Object, default: {} },
}) })
if (!["id", "url", "hash"].includes(props.returnType)) { if (! ['id', 'url', 'hash'].includes(props.returnType)) {
Message.error("MaUpload组件props的returnType只能为id, url, hash 其中一个") Message.error('MaUpload组件props的returnType只能为id, url, hash 其中一个')
console.error("MaUpload组件props的returnType只能为id, url, hash 其中一个") console.error('MaUpload组件props的returnType只能为id, url, hash 其中一个')
} }
watch( watch(() => props.modelValue, (val) => {
() => props.modelValue, file.value = val
(val) => { }, {
file.value = val deep: true, immediate: true
}, })
{
deep: true,
immediate: true
}
)
provide("storageMode", uploadConfig.storageMode) provide('storageMode', uploadConfig.storageMode)
provide("config", props) provide('config', props)
watch( watch(
() => file.value, () => file.value,
(vl) => emit("update:modelValue", vl) vl => emit('update:modelValue', vl)
) )
</script> </script>

View File

@@ -1,36 +1,36 @@
import commonApi from "@/api/common" import commonApi from '@/api/common'
import tool from "@/utils/tool" import tool from '@/utils/tool'
import file2md5 from "file2md5" import file2md5 from 'file2md5'
export const getFileUrl = async (returnType, value, storageMode) => { export const getFileUrl = async (returnType, value, storageMode) => {
if (returnType === "url") { if (returnType === 'url') {
return value return value
} else if (returnType === "id") { } else if (returnType === 'id') {
const { data } = await commonApi.getFileInfoById(value) const { data } = await commonApi.getFileInfoById(value)
if (data) { if (data) {
data.url = tool.attachUrl(data.url, storageMode[data.storage_mode]) data.url = tool.attachUrl(data.url, storageMode[data.storage_mode])
return data return data
}
return ""
} else if (returnType === "hash") {
const { data } = await commonApi.getFileInfoByHash(value)
if (data) {
data.url = tool.attachUrl(data.url, storageMode[data.storage_mode])
return data
}
return ""
} }
return ''
} else if (returnType === 'hash') {
const { data } = await commonApi.getFileInfoByHash(value)
if (data) {
data.url = tool.attachUrl(data.url, storageMode[data.storage_mode])
return data
}
return ''
}
} }
export const uploadRequest = async (file, type, method, requestData = {}) => { export const uploadRequest = async (file, type, method, requestData = {}) => {
const hash = await file2md5(file) const hash = await file2md5(file)
const dataForm = new FormData() const dataForm = new FormData()
dataForm.append(type, file) dataForm.append(type, file)
dataForm.append("isChunk", false) dataForm.append('isChunk', false)
dataForm.append("hash", hash) dataForm.append('hash', hash)
for (let name in requestData) { for (let name in requestData) {
dataForm.append(name, requestData[name]) dataForm.append(name, requestData[name])
} }
const response = await commonApi[method](dataForm) const response = await commonApi[method](dataForm)
return response.data return response.data
} }

View File

@@ -1,133 +1,128 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<div class="ma-content-block"> <div class="ma-content-block">
<a-space class="flex"> <a-space class="flex">
<a-button type="primary" @click="visible = true"> <a-button type="primary" @click="visible = true">
<template #icon><icon-select-all /></template>{{ props.text }} <template #icon><icon-select-all /></template>{{ props.text }}
</a-button> </a-button>
<a-tag size="large" color="blue" v-if="props.isEcho" <a-tag size="large" color="blue" v-if="props.isEcho">已选择 {{ isArray(selecteds) ? selecteds.length : 0 }} </a-tag>
>已选择 {{ isArray(selecteds) ? selecteds.length : 0 }} </a-tag <a-input-tag v-model="userList" v-if="props.isEcho" :style="{ width:'320px' }" :placeholder="'请点击前面按钮' + props.text" :max-tag-count="3" disabled/>
> </a-space>
<a-input-tag
v-model="userList"
v-if="props.isEcho"
:style="{ width: '320px' }"
:placeholder="'请点击前面按钮' + props.text"
:max-tag-count="3"
disabled
/>
</a-space>
<a-modal v-model:visible="visible" width="1000px" draggable :on-before-ok="close" unmountOnClose> <a-modal v-model:visible="visible" width="1000px" draggable :on-before-ok="close" unmountOnClose>
<template #title>{{ props.text }}</template> <template #title>{{ props.text }}</template>
<ma-crud <ma-crud
ref="crudRef" ref="crudRef"
:options="crud" :options="crud"
:columns="columns" :columns="columns"
v-model:selected-keys="selecteds" v-model:selected-keys="selecteds"
@selection-change="selectHandler" @selection-change="selectHandler"
/> />
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, watch } from "vue" import { onMounted, ref, watch } from 'vue'
import commonApi from "@/api/common" import commonApi from '@/api/common'
import { Message } from "@arco-design/web-vue" import { Message } from '@arco-design/web-vue'
import { isArray, isEmpty } from "lodash" import { isArray, isEmpty } from 'lodash'
const props = defineProps({ const props = defineProps({
modelValue: { type: Array }, modelValue: { type: Array },
isEcho: { type: Boolean, default: false }, isEcho: { type: Boolean, default: false },
multiple: { type: Boolean, default: true }, multiple: { type: Boolean, default: true },
onlyId: { type: Boolean, default: true }, onlyId: { type: Boolean, default: true },
text: { type: String, default: "选择用户" } text: { type: String, default: '选择用户' }
}) })
const emit = defineEmits(["update:modelValue", "success"]) const emit = defineEmits(['update:modelValue', 'success'])
const visible = ref(false) const visible = ref(false)
const selecteds = ref([]) const selecteds = ref([])
const userList = ref([]) const userList = ref([])
onMounted(() => { onMounted(() => {
if (props.isEcho && props.onlyId) selecteds.value = props.modelValue if (props.isEcho && props.onlyId) selecteds.value = props.modelValue
}) })
watch( watch(
() => props.modelValue, () => props.modelValue,
(val) => { val => {
if (props.isEcho && props.onlyId) selecteds.value = val if (props.isEcho && props.onlyId) selecteds.value = val
} }
) )
const selectHandler = (rows) => { const selectHandler = (rows) => {
selecteds.value = rows selecteds.value = rows
} }
const close = async (done) => { const close = async (done) => {
if (isArray(selecteds.value) && selecteds.value.length > 0) { if (isArray(selecteds.value) && selecteds.value.length > 0) {
const response = await commonApi.getUserInfoByIds({ ids: selecteds.value }) const response = await commonApi.getUserInfoByIds({ ids: selecteds.value })
if (!isEmpty(response) && isArray(response.data)) { if (! isEmpty(response) && isArray(response.data)) {
userList.value = response.data.map((item) => { userList.value = response.data.map( item => {
return `${item.username}(${item.id})` return `${item.username}(${item.id})`
}) })
if (props.onlyId) { if (props.onlyId) {
emit("update:modelValue", selecteds.value) emit('update:modelValue', selecteds.value)
} else { } else {
emit("update:modelValue", response.data) emit('update:modelValue', response.data)
}
emit("success", true)
Message.success("选择成功")
} }
emit('success', true)
Message.success('选择成功')
}
} else { } else {
emit("update:modelValue", []) emit('update:modelValue', [])
userList.value = [] userList.value = []
} }
done(true) done(true)
} }
const crud = ref({ const crud = ref({
showIndex: false, showIndex: false,
api: commonApi.getUserList, api: commonApi.getUserList,
rowSelection: props.multiple ? { type: "checkbox", showCheckedAll: true } : { type: "radio" } rowSelection: props.multiple ? { type: 'checkbox', showCheckedAll: true } : { type: 'radio' }
}) })
const columns = ref([ const columns = ref([
{ title: "账户", dataIndex: "username", search: true }, { title: '账户', dataIndex: 'username', search: true },
{ title: "昵称", dataIndex: "nickname", search: true }, { title: '昵称', dataIndex: 'nickname', search: true },
{ title: "手机", dataIndex: "phone", search: true }, { title: '手机', dataIndex: 'phone', search: true },
{ title: "邮箱", dataIndex: "email", search: true }, { title: '邮箱', dataIndex: 'email', search: true },
{ {
title: "部门", title: '部门',
dataIndex: "dept_id", dataIndex: 'dept_id',
search: true, search: true,
formType: "tree-select", formType: 'tree-select',
hide: true, hide: true,
dict: { url: "system/common/getDeptTreeList" } dict: { url: 'system/common/getDeptTreeList' }
}, },
{ {
title: "角色", title: '角色',
dataIndex: "role_id", dataIndex: 'role_id',
search: true, search: true,
formType: "select", formType: 'select',
hide: true, hide: true,
dict: { url: "system/common/getRoleList", props: { label: "name", value: "code" } } dict: { url: 'system/common/getRoleList', props: { label: 'name', value: 'code' } }
}, },
{ {
title: "岗位", title: '岗位',
dataIndex: "post_id", dataIndex: 'post_id',
search: true, search: true,
formType: "select", formType: 'select',
hide: true, hide: true,
dict: { url: "system/common/getPostList", props: { label: "name", value: "code" } } dict: { url: 'system/common/getPostList', props: { label: 'name', value: 'code' } }
} },
]) ])
</script> </script>
<style scoped> <style scoped>
:deep(.arco-tabs-nav-type-capsule .arco-tabs-nav-tab) { :deep(.arco-tabs-nav-type-capsule .arco-tabs-nav-tab) {
justify-content: flex-start; justify-content: flex-start;
} }
</style> </style>

View File

@@ -1,41 +1,37 @@
<!-- <!--
- MineAdmin is committed to providing solutions for quickly building web applications - @Author XXX
- Please view the LICENSE file that was distributed with this source code, - @Link XXX
- For the full copyright and license information.
- Thank you very much for using MineAdmin.
-
- @Author X.Mo<root@imoi.cn>
- @Link https://gitee.com/xmo/mineadmin-vue
--> -->
<template> <template>
<a-input v-model="val" :label="props.title" disabled class="w-full" /> <a-input v-model="val" :label="props.title" disabled class="w-full" />
</template> </template>
<script setup> <script setup>
import { ref, watch } from "vue" import { ref, watch } from 'vue'
import { useUserStore } from "@/store" import { useUserStore } from '@/store'
const user = useUserStore().user const user = useUserStore().user
const val = ref() const val = ref()
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(['update:modelValue'])
const props = defineProps({ const props = defineProps({
modelValue: [String, Number], modelValue: [ String, Number ],
title: { type: String, default: "用户信息" }, title: { type: String, default: '用户信息'},
field: { type: String, default: "id" } field: { type: String, default: 'id'},
}) })
val.value = user[props.field] ? user[props.field].toString() : user.id.toString() val.value = user[props.field] ? user[props.field].toString() : user.id.toString()
watch( watch(
() => val.value, () => val.value,
(vl) => emit("update:modelValue", vl), vl => emit('update:modelValue', vl),
{ immediate: true } { immediate: true }
) )
</script> </script>
<style scoped> <style scoped>
:deep(.arco-select-option-content) { :deep(.arco-select-option-content) {
width: 100%; width: 100%;
} }
</style> </style>

Some files were not shown because too many files have changed in this diff Show More