diff --git a/package.json b/package.json index e23cd109397dbe1d687ae2193ee7b708724a5d5c..c155f9709891cc0b2ad4bc75f1e1811b357d48d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ibiz-template/vue3-components", - "version": "0.0.1-alpha.8", + "version": "0.0.1-alpha.10", "description": "使用 rollup 编译 vue 组件或者 jsx", "main": "lib/index.cjs", "module": "es/index.mjs", @@ -22,16 +22,18 @@ "prepare": "husky install" }, "dependencies": { - "@ibiz-template/core": "^0.0.4-beta.14", - "@ibiz-template/model-helper": "^0.0.4-beta.14", - "@ibiz-template/runtime": "^0.0.4-beta.14", - "@ibiz-template/theme": "^0.0.4-beta.12", - "@ibiz-template/vue3-util": "^0.0.4-beta.14", + "@floating-ui/dom": "^1.2.9", + "@ibiz-template/core": "0.0.4-beta.16", + "@ibiz-template/model-helper": "0.0.4-beta.16", + "@ibiz-template/runtime": "0.0.4-beta.16", + "@ibiz-template/theme": "0.0.4-beta.16", + "@ibiz-template/vue3-util": "0.0.4-beta.16", "@ibiz/model-core": "^0.0.10", "async-validator": "^4.2.5", "dayjs": "^1.11.7", "element-plus": "^2.3.5", "lodash-es": "^4.17.21", + "pinia": "^2.1.3", "qs": "^6.11.2", "qx-util": "^0.4.8", "ramda": "^0.29.0", @@ -41,7 +43,7 @@ "devDependencies": { "@commitlint/cli": "^17.6.5", "@commitlint/config-conventional": "^17.6.5", - "@ibiz-template/cli": "0.0.1-alpha.2", + "@ibiz-template/cli": "0.0.1-alpha.3", "@types/lodash-es": "^4.17.7", "@types/qs": "^6.9.7", "@types/ramda": "^0.29.2", @@ -71,6 +73,7 @@ "vue-tsc": "^1.6.5" }, "peerDependencies": { + "@floating-ui/dom": "^1.2.9", "@ibiz-template/core": "^0.0.4-beta.5", "@ibiz-template/model-helper": "^0.0.4-beta.7", "@ibiz-template/runtime": "^0.0.4-beta.7", @@ -81,6 +84,7 @@ "dayjs": "^1.11.7", "element-plus": "^2.3.4", "lodash-es": "^4.17.21", + "pinia": "^2.1.3", "qs": "^6.11.1", "qx-util": "^0.4.8", "ramda": "^0.29.0", @@ -91,4 +95,4 @@ "*.ts": "eslint --fix", "*.scss": "stylelint --custom-syntax=postcss-scss" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4484f15db691426054e12a5a33b3c8d40d8f07d1..2f454d3aeff9503be24a41bab3f7d78a33629f32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,21 +5,24 @@ settings: excludeLinksFromLockfile: false dependencies: + '@floating-ui/dom': + specifier: ^1.2.9 + version: 1.2.9 '@ibiz-template/core': - specifier: ^0.0.4-beta.14 - version: 0.0.4-beta.14(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + specifier: 0.0.4-beta.16 + version: 0.0.4-beta.16(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) '@ibiz-template/model-helper': - specifier: ^0.0.4-beta.14 - version: 0.0.4-beta.14(@ibiz-template/runtime@0.0.4-beta.14) + specifier: 0.0.4-beta.16 + version: 0.0.4-beta.16(@ibiz-template/runtime@0.0.4-beta.16) '@ibiz-template/runtime': - specifier: ^0.0.4-beta.14 - version: 0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + specifier: 0.0.4-beta.16 + version: 0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) '@ibiz-template/theme': - specifier: ^0.0.4-beta.12 - version: 0.0.4-beta.12 + specifier: 0.0.4-beta.16 + version: 0.0.4-beta.16 '@ibiz-template/vue3-util': - specifier: ^0.0.4-beta.14 - version: 0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz-template/runtime@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0)(vue-router@4.2.2)(vue@3.3.4) + specifier: 0.0.4-beta.16 + version: 0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz-template/runtime@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0)(vue-router@4.2.2)(vue@3.3.4) '@ibiz/model-core': specifier: ^0.0.10 version: 0.0.10 @@ -35,6 +38,9 @@ dependencies: lodash-es: specifier: ^4.17.21 version: 4.17.21 + pinia: + specifier: ^2.1.3 + version: 2.1.3(typescript@5.0.4)(vue@3.3.4) qs: specifier: ^6.11.2 version: 6.11.2 @@ -59,8 +65,8 @@ devDependencies: specifier: ^17.6.5 version: 17.6.5 '@ibiz-template/cli': - specifier: 0.0.1-alpha.2 - version: 0.0.1-alpha.2(vite@4.3.9)(vue@3.3.4) + specifier: 0.0.1-alpha.3 + version: 0.0.1-alpha.3(vite@4.3.9)(vue@3.3.4) '@types/lodash-es': specifier: ^4.17.7 version: 4.17.7 @@ -126,19 +132,19 @@ devDependencies: version: 33.0.0(stylelint@15.6.2) stylelint-config-standard-scss: specifier: ^9.0.0 - version: 9.0.0(postcss@8.4.23)(stylelint@15.6.2) + version: 9.0.0(postcss@8.4.24)(stylelint@15.6.2) stylelint-scss: specifier: ^5.0.0 version: 5.0.0(stylelint@15.6.2) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.1.4)(typescript@5.0.4) + version: 10.9.1(@types/node@20.2.5)(typescript@5.0.4) typescript: specifier: ^5.0.4 version: 5.0.4 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@20.1.4)(sass@1.62.1) + version: 4.3.9(@types/node@20.2.5)(sass@1.62.1) vue-tsc: specifier: ^1.6.5 version: 1.6.5(typescript@5.0.4) @@ -907,8 +913,8 @@ packages: resolution: {integrity: sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==} dev: false - /@floating-ui/dom@1.2.6: - resolution: {integrity: sha512-02vxFDuvuVPs22iJICacezYJyf7zwwOCWkPNkWNBr1U0Qt1cKFYzWvxts0AmqcOQGwt/3KJWcWIgtbUU38keyw==} + /@floating-ui/dom@1.2.9: + resolution: {integrity: sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ==} dependencies: '@floating-ui/core': 1.2.6 dev: false @@ -933,8 +939,8 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@ibiz-template/cli-core@0.0.1-alpha.2(vite@4.3.9)(vue@3.3.4): - resolution: {integrity: sha512-gqCQWm6bIocPApDHJ1p79aW3xpYMDQHPQMvtSBifRpzLhpZSGpgcx+A1jZ47MuBLHsP9XjugMOIo6filRRMytw==} + /@ibiz-template/cli-core@0.0.1-alpha.3(vite@4.3.9)(vue@3.3.4): + resolution: {integrity: sha512-huZn2oonHd0WvjwVmLXIzBy3MxtufxpwFNEfNhccYTVScPZof+4wZx0fs3xVlC6vBtaaAjzEXc7sMK39PZL2bQ==} dependencies: '@qx-chitanda/rollup-plugin': 0.0.4(rollup@3.23.0) '@rollup/plugin-commonjs': 25.0.0(rollup@3.23.0) @@ -962,11 +968,11 @@ packages: - vue dev: true - /@ibiz-template/cli@0.0.1-alpha.2(vite@4.3.9)(vue@3.3.4): - resolution: {integrity: sha512-gopRnrKyxid6m+byYUIgR9/Te5nim0ZyxJ8N6Do0NAVONQRSKMm8ZPN4Hpd7qOeSgRoqs3+f54BVmXUEt1J+Gw==} + /@ibiz-template/cli@0.0.1-alpha.3(vite@4.3.9)(vue@3.3.4): + resolution: {integrity: sha512-TR3y+S3jjnz2wKzA59/rHcyEDbzqou0AL4jNdWt/j6iqfWceVKLSrLkhNlq+9STvnTG5RmFkj+0i6V/shhI0Mw==} hasBin: true dependencies: - '@ibiz-template/cli-core': 0.0.1-alpha.2(vite@4.3.9)(vue@3.3.4) + '@ibiz-template/cli-core': 0.0.1-alpha.3(vite@4.3.9)(vue@3.3.4) commander: 10.0.1 consola: 3.1.0 fs-extra: 11.1.1 @@ -976,8 +982,8 @@ packages: - vue dev: true - /@ibiz-template/core@0.0.4-beta.14(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0): - resolution: {integrity: sha512-vx/b1EllRTWs3FjcLcDAwvkTC5ONnOGH7c/yyroXOjqYEQMvom+x6zvTBuMLibxb75J4I2YSjUxJj98BV/jixg==} + /@ibiz-template/core@0.0.4-beta.16(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0): + resolution: {integrity: sha512-GKQmO2oI3AFRJNJMtikGwCsfUB/PxYUACxok1Tq3Htc01/+OqtpHUc0QBWQokHCpcMnqd4vtqMHmK/2RkTMPnw==} peerDependencies: axios: ^1.3.5 lodash-es: ^4.17.21 @@ -993,19 +999,19 @@ packages: ramda: 0.29.0 dev: false - /@ibiz-template/model-helper@0.0.4-beta.14(@ibiz-template/runtime@0.0.4-beta.14): - resolution: {integrity: sha512-XViaau81PmX0zv3s9n4VZI8zmiqvRFXIVfos/Jr5yVPBmIqf3O31t2XywW6tMaTOclhH3rNz4Vb+01ZDOB7tfQ==} + /@ibiz-template/model-helper@0.0.4-beta.16(@ibiz-template/runtime@0.0.4-beta.16): + resolution: {integrity: sha512-h2gbV4gkUpilOvojxnFkpoVb/kkTkXBuftC3kZUAhby0NryBwQairG9DI8FNJr/CKlb3vXUL31jxNSwr8cPLiA==} peerDependencies: '@ibiz-template/runtime': workspace:^0.0.4-beta.7 dependencies: - '@ibiz-template/runtime': 0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + '@ibiz-template/runtime': 0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) '@ibiz/model-core': 0.0.10 '@ibiz/rt-model-api': 0.0.1-beta.13 pluralize: 8.0.0 dev: false - /@ibiz-template/runtime@0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0): - resolution: {integrity: sha512-7NR/JT5olnQg36X+M/+CumO3FGIy2Ial00wUiWmcWPSJ5XVdcAbseGteRgt35RKlw8a1tXLTX9ZjuWnhNx9Zag==} + /@ibiz-template/runtime@0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0): + resolution: {integrity: sha512-sL0uioeYyWvqgNs98wQ5YbE60T7x86KEyyALh1IYJ+JbYX3kL7b79vogPQvQMtDhUeqMvE+JbyYl7VkhW+EPZQ==} peerDependencies: '@ibiz-template/core': workspace:^0.0.4-beta.1 '@ibiz/model-core': ^0.0.10 @@ -1016,7 +1022,7 @@ packages: qx-util: ^0.4.8 ramda: ^0.29.0 dependencies: - '@ibiz-template/core': 0.0.4-beta.14(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + '@ibiz-template/core': 0.0.4-beta.16(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) '@ibiz/model-core': 0.0.10 async-validator: 4.2.5 dayjs: 1.11.7 @@ -1026,12 +1032,12 @@ packages: ramda: 0.29.0 dev: false - /@ibiz-template/theme@0.0.4-beta.12: - resolution: {integrity: sha512-GvOVSeK5WH8Rrsw+Gv638YLx52EJPCVxpXq+L3QLlnB1SXM3t+i7icsSt5u4YDDsvN4B0sr4+KehpUukWTvklQ==} + /@ibiz-template/theme@0.0.4-beta.16: + resolution: {integrity: sha512-LW3I+XKL4dIMm8ziTwF7u4bqNz53KkQo/XthojckeLfd3Pna0rsvU60KgAf3HRYbvaE4X/rkfvlzrRrZCE7aUQ==} dev: false - /@ibiz-template/vue3-util@0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz-template/runtime@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0)(vue-router@4.2.2)(vue@3.3.4): - resolution: {integrity: sha512-EjB/OSToiLHeVM4IiDLaHHej7NAENeWDiw+UAyYychKjl7aBbi3vYymNK6ljdglgtUu2vwMQXe30DO3YWNl69w==} + /@ibiz-template/vue3-util@0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz-template/runtime@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0)(vue-router@4.2.2)(vue@3.3.4): + resolution: {integrity: sha512-gxQPDKsYjVDwoNvd+MdgB2QkjfIRv0DV2FzcsGMBRBgGcZROuwx7JvxFv0YS0Q8nwwMhu88kE+mImh8I9gNxWw==} peerDependencies: '@ibiz-template/core': workspace:^0.0.4-beta.1 '@ibiz-template/runtime': workspace:^0.0.4-beta.1 @@ -1042,8 +1048,8 @@ packages: vue: ^3.2.47 vue-router: ^4.1.6 dependencies: - '@ibiz-template/core': 0.0.4-beta.14(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) - '@ibiz-template/runtime': 0.0.4-beta.14(@ibiz-template/core@0.0.4-beta.14)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + '@ibiz-template/core': 0.0.4-beta.16(axios@1.4.0)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) + '@ibiz-template/runtime': 0.0.4-beta.16(@ibiz-template/core@0.0.4-beta.16)(@ibiz/model-core@0.0.10)(async-validator@4.2.5)(dayjs@1.11.7)(lodash-es@4.17.21)(qs@6.11.2)(qx-util@0.4.8)(ramda@0.29.0) '@ibiz/model-core': 0.0.10 qs: 6.11.2 qx-util: 0.4.8 @@ -1263,6 +1269,10 @@ packages: resolution: {integrity: sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==} dev: true + /@types/node@20.2.5: + resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} + dev: true + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -1429,7 +1439,7 @@ packages: '@babel/core': 7.22.1 '@babel/plugin-transform-typescript': 7.22.3(@babel/core@7.22.1) '@vue/babel-plugin-jsx': 1.1.1(@babel/core@7.22.1) - vite: 4.3.9(@types/node@20.1.4)(sass@1.62.1) + vite: 4.3.9(@types/node@20.2.5)(sass@1.62.1) vue: 3.3.4 transitivePeerDependencies: - supports-color @@ -1442,7 +1452,7 @@ packages: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.3.9(@types/node@20.1.4)(sass@1.62.1) + vite: 4.3.9(@types/node@20.2.5)(sass@1.62.1) vue: 3.3.4 dev: true @@ -1618,7 +1628,7 @@ packages: /@vueuse/shared@9.13.0(vue@3.3.4): resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} dependencies: - vue-demi: 0.14.0(vue@3.3.4) + vue-demi: 0.14.5(vue@3.3.4) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -1938,7 +1948,7 @@ packages: hasBin: true dev: true - /autoprefixer@10.4.14(postcss@8.4.23): + /autoprefixer@10.4.14(postcss@8.4.24): resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} hasBin: true @@ -1950,7 +1960,7 @@ packages: fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.23 + postcss: 8.4.24 postcss-value-parser: 4.2.0 dev: true @@ -2698,7 +2708,7 @@ packages: dependencies: '@ctrl/tinycolor': 3.6.0 '@element-plus/icons-vue': 2.1.0(vue@3.3.4) - '@floating-ui/dom': 1.2.6 + '@floating-ui/dom': 1.2.9 '@popperjs/core': /@sxzz/popperjs-es@2.11.7 '@types/lodash': 4.14.194 '@types/lodash-es': 4.17.7 @@ -3773,11 +3783,11 @@ packages: gulp: optional: true dependencies: - autoprefixer: 10.4.14(postcss@8.4.23) + autoprefixer: 10.4.14(postcss@8.4.24) fancy-log: 1.3.3 gulp: 4.0.2 plugin-error: 1.0.1 - postcss: 8.4.23 + postcss: 8.4.24 through2: 4.0.2 vinyl-sourcemaps-apply: 0.2.1 dev: true @@ -5392,6 +5402,24 @@ packages: engines: {node: '>=0.10.0'} dev: true + /pinia@2.1.3(typescript@5.0.4)(vue@3.3.4): + resolution: {integrity: sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==} + peerDependencies: + '@vue/composition-api': ^1.4.0 + typescript: '>=4.4.4' + vue: ^2.6.14 || ^3.3.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + typescript: + optional: true + dependencies: + '@vue/devtools-api': 6.5.0 + typescript: 5.0.4 + vue: 3.3.4 + vue-demi: 0.14.5(vue@3.3.4) + dev: false + /pinkie-promise@2.0.1: resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} engines: {node: '>=0.10.0'} @@ -5441,13 +5469,13 @@ packages: postcss: 8.4.23 dev: true - /postcss-scss@4.0.6(postcss@8.4.23): + /postcss-scss@4.0.6(postcss@8.4.24): resolution: {integrity: sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.4.19 dependencies: - postcss: 8.4.23 + postcss: 8.4.24 dev: true /postcss-selector-parser@6.0.13: @@ -5478,6 +5506,15 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 + /postcss@8.4.24: + resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -6261,7 +6298,7 @@ packages: stylelint-order: 6.0.3(stylelint@15.6.2) dev: true - /stylelint-config-recommended-scss@11.0.0(postcss@8.4.23)(stylelint@15.6.2): + /stylelint-config-recommended-scss@11.0.0(postcss@8.4.24)(stylelint@15.6.2): resolution: {integrity: sha512-EDghTDU7aOv2LTsRZvcT1w8mcjUaMhuy+t38iV5I/0Qiu6ixdkRwhLEMul3K/fnB2v9Nwqvb3xpvJfPH+HduDw==} peerDependencies: postcss: ^8.3.3 @@ -6270,8 +6307,8 @@ packages: postcss: optional: true dependencies: - postcss: 8.4.23 - postcss-scss: 4.0.6(postcss@8.4.23) + postcss: 8.4.24 + postcss-scss: 4.0.6(postcss@8.4.24) stylelint: 15.6.2 stylelint-config-recommended: 12.0.0(stylelint@15.6.2) stylelint-scss: 4.7.0(stylelint@15.6.2) @@ -6285,7 +6322,7 @@ packages: stylelint: 15.6.2 dev: true - /stylelint-config-standard-scss@9.0.0(postcss@8.4.23)(stylelint@15.6.2): + /stylelint-config-standard-scss@9.0.0(postcss@8.4.24)(stylelint@15.6.2): resolution: {integrity: sha512-yPKpJsrZn4ybuQZx/DkEHuCjw7pJginErE/47dFhCnrvD48IJ4UYec8tSiCuJWMA3HRjbIa3nh5ZeSauDGuVAg==} peerDependencies: postcss: ^8.3.3 @@ -6294,9 +6331,9 @@ packages: postcss: optional: true dependencies: - postcss: 8.4.23 + postcss: 8.4.24 stylelint: 15.6.2 - stylelint-config-recommended-scss: 11.0.0(postcss@8.4.23)(stylelint@15.6.2) + stylelint-config-recommended-scss: 11.0.0(postcss@8.4.24)(stylelint@15.6.2) stylelint-config-standard: 33.0.0(stylelint@15.6.2) dev: true @@ -6574,6 +6611,37 @@ packages: yn: 3.1.1 dev: true + /ts-node@10.9.1(@types/node@20.2.5)(typescript@5.0.4): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.2.5 + acorn: 8.8.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.0.4 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /ts-toolbelt@9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} dev: true @@ -6667,7 +6735,6 @@ packages: resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} engines: {node: '>=12.20'} hasBin: true - dev: true /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -6851,7 +6918,7 @@ packages: replace-ext: 1.0.1 dev: true - /vite@4.3.9(@types/node@20.1.4)(sass@1.62.1): + /vite@4.3.9(@types/node@20.2.5)(sass@1.62.1): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -6876,7 +6943,7 @@ packages: terser: optional: true dependencies: - '@types/node': 20.1.4 + '@types/node': 20.2.5 esbuild: 0.17.19 postcss: 8.4.23 rollup: 3.23.0 @@ -6900,6 +6967,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.5(vue@3.3.4): + resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-eslint-parser@9.3.0(eslint@8.41.0): resolution: {integrity: sha512-48IxT9d0+wArT1+3wNIy0tascRoywqSUe2E1YalIC1L8jsUGe5aJQItWfRok7DVFGz3UYvzEI7n5wiTXsCMAcQ==} engines: {node: ^14.17.0 || >=16.0.0} diff --git a/src/control/index.ts b/src/control/index.ts index 55c0faec19fe4883e985138fc77008a905971c17..75aac7b7d15b8f96a1fe859a6bde495765256aae 100644 --- a/src/control/index.ts +++ b/src/control/index.ts @@ -10,3 +10,4 @@ export * from './tree'; export * from './pickup-view-panel'; export * from './tab-exp-panel'; export * from './exp-bar'; +export * from './search-bar'; diff --git a/src/control/search-bar/index.ts b/src/control/search-bar/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..90f67e644c5f838d9ddbb1c7f286835a61bddfa6 --- /dev/null +++ b/src/control/search-bar/index.ts @@ -0,0 +1,21 @@ +import { ControlType, registerControlProvider } from '@ibiz-template/runtime'; +import { App } from 'vue'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { SearchBarControl } from './search-bar'; +import { SearchBarProvider } from './search-bar.provider'; + +export * from './search-bar.provider'; +export * from './search-bar.controller'; + +export const IBizSearchBarControl = withInstall( + SearchBarControl, + function (v: App) { + v.component(SearchBarControl.name, SearchBarControl); + registerControlProvider( + ControlType.SEARCHBAR, + () => new SearchBarProvider(), + ); + }, +); + +export default IBizSearchBarControl; diff --git a/src/control/search-bar/search-bar.controller.ts b/src/control/search-bar/search-bar.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..2bd1a4e52022a8ecd4d4fa57f32c1b6edbe5b8df --- /dev/null +++ b/src/control/search-bar/search-bar.controller.ts @@ -0,0 +1,51 @@ +import { + ControlController, + ISearchBarState, + ISearchBarEvent, + ISearchBarController, +} from '@ibiz-template/runtime'; +import { ISearchBar } from '@ibiz/model-core'; + +/** + * 搜索栏控制器 + * + * @author chitanda + * @date 2022-07-24 15:07:07 + * @export + * @class SearchBarController + * @extends {ControlController} + */ +export class SearchBarController + extends ControlController + implements ISearchBarController +{ + protected initState(): void { + super.initState(); + this.state.query = ''; + } + + protected async doCreated(): Promise { + await super.doCreated(); + } + + /** + * 处理输入 + * @param {string} val + * @return {*} + * @author: zhujiamin + * @Date: 2023-06-01 18:08:07 + */ + handleInput(val: string) { + this.state.query = val; + } + + /** + * 处理搜索 + * @return {*} + * @author: zhujiamin + * @Date: 2023-06-01 18:08:18 + */ + handleSearch() { + this.evt.emit('onSearch', undefined); + } +} diff --git a/src/control/search-bar/search-bar.provider.ts b/src/control/search-bar/search-bar.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..adaa06c00573a261d2afb5ee0a2c3916238ddc52 --- /dev/null +++ b/src/control/search-bar/search-bar.provider.ts @@ -0,0 +1,14 @@ +import { IControlProvider } from '@ibiz-template/runtime'; + +/** + * 搜索栏适配器 + * + * @author lxm + * @date 2022-10-25 18:10:57 + * @export + * @class SearchBarProvider + * @implements {IControlProvider} + */ +export class SearchBarProvider implements IControlProvider { + component: string = 'IBizSearchBarControl'; +} diff --git a/src/control/search-bar/search-bar.scss b/src/control/search-bar/search-bar.scss new file mode 100644 index 0000000000000000000000000000000000000000..a8d5e0c80e156cc2b8b25e588474fe06ec32e10d --- /dev/null +++ b/src/control/search-bar/search-bar.scss @@ -0,0 +1,6 @@ +@include b(control-searchbar) { + @include set-component-css-var('control-searchbar', $control-searchbar); + @include b(control-searchbar-quick-search) { + width: getCssVar('control-searchbar', 'quick-search-width'); + } +} diff --git a/src/control/search-bar/search-bar.tsx b/src/control/search-bar/search-bar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e51b2ff4604ee5e34bc5e95dd83afca4bba3344c --- /dev/null +++ b/src/control/search-bar/search-bar.tsx @@ -0,0 +1,67 @@ +import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; +import { computed, defineComponent, PropType } from 'vue'; +import { ISearchBar } from '@ibiz/model-core'; +import { debounce } from 'lodash-es'; +import { SearchBarController } from './search-bar.controller'; +import './search-bar.scss'; + +export const SearchBarControl = defineComponent({ + name: 'IBizSearchBarControl', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + context: { type: Object as PropType, required: true }, + params: { type: Object as PropType, default: () => ({}) }, + }, + setup() { + const c = useControlController( + (...args) => new SearchBarController(...args), + ); + const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); + + const onSearch = () => { + c.handleSearch(); + }; + + const debounceSearch = debounce(() => { + if (onSearch) { + onSearch(); + } + }, 500); + + const onInput = (value: string) => { + c.handleInput(value); + debounceSearch(); + }; + + const cssVars = computed(() => { + if (c.model.quickSearchWidth) { + return ns.cssVarBlock({ + 'quick-search-width': `${c.model.quickSearchWidth}px`, + }); + } + return {}; + }); + + return { c, ns, onInput, onSearch, cssVars }; + }, + render() { + return ( + + {this.c.model.enableQuickSearch && ( + + )} + + ); + }, +}); diff --git a/src/control/toolbar/toolbar.tsx b/src/control/toolbar/toolbar.tsx index b1684fd01e26ad058534998e9d8fbbbac41b12f9..b8cd59ec4afc043300c77ac5703d73c60b621213 100644 --- a/src/control/toolbar/toolbar.tsx +++ b/src/control/toolbar/toolbar.tsx @@ -97,23 +97,35 @@ export const ToolbarControl = defineComponent({ class={[this.ns.e('item'), this.ns.e('item-items')]} > - - {btnContent(item, state.viewMode)} - - - {(item as IDETBGroupItem).detoolbarItems?.map(item2 => { + {{ + default: () => { return ( - - this.handleClick(item2, e) - } - > - {btnContent(item2, state.viewMode)} - + + {btnContent(item, state.viewMode)} + ); - })} - + }, + dropdown: () => { + return ( + + {(item as IDETBGroupItem).detoolbarItems?.map( + item2 => { + return ( + + this.handleClick(item2, e) + } + > + {btnContent(item2, state.viewMode)} + + ); + }, + )} + + ); + }, + }} ); diff --git a/src/editor/code/code-editor.controller.ts b/src/editor/code/code-editor.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..f9c10513ec60171469d9154676702e8318e6113b --- /dev/null +++ b/src/editor/code/code-editor.controller.ts @@ -0,0 +1,11 @@ +import { EditorController } from '@ibiz-template/runtime'; +import { ICode } from '@ibiz/model-core'; + +/** + * 代码框编辑器控制器 + * + * @export + * @class CodeEditorController + * @extends {EditorController} + */ +export class CodeEditorController extends EditorController {} diff --git a/src/editor/code/code-editor.provider.ts b/src/editor/code/code-editor.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..ef87832c466c783a66876790e3944e779ca466c5 --- /dev/null +++ b/src/editor/code/code-editor.provider.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + IEditorContainerController, + IEditorProvider, +} from '@ibiz-template/runtime'; +import { ICode } from '@ibiz/model-core'; +import { CodeEditorController } from './code-editor.controller'; + +/** + * 代码框编辑器适配器 + * + * @author lxm + * @date 2022-09-19 22:09:03 + * @export + * @class CodeEditorProvider + * @implements {EditorProvider} + */ +export class CodeEditorProvider implements IEditorProvider { + formEditor: string = 'IBizCode'; + + gridEditor: string = 'IBizCode'; + + async createController( + editorModel: ICode, + parentController: IEditorContainerController, + ): Promise { + const c = new CodeEditorController(editorModel, parentController); + await c.init(); + return c; + } +} diff --git a/src/editor/code/index.ts b/src/editor/code/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a157ec554a9691559bd880855d86ceccc2ca8d14 --- /dev/null +++ b/src/editor/code/index.ts @@ -0,0 +1,3 @@ +export { IBizCode } from './monaco-editor/monaco-editor'; +export * from './code-editor.controller'; +export * from './code-editor.provider'; diff --git a/src/editor/code/monaco-editor/monaco-editor.scss b/src/editor/code/monaco-editor/monaco-editor.scss new file mode 100644 index 0000000000000000000000000000000000000000..c246aac256bf709878e0b171f794ba7c9af0e663 --- /dev/null +++ b/src/editor/code/monaco-editor/monaco-editor.scss @@ -0,0 +1,6 @@ +@include b(code) { + display: inline-block; + width: 100%; + height: 100%; + min-height: 200px; +} diff --git a/src/editor/code/monaco-editor/monaco-editor.tsx b/src/editor/code/monaco-editor/monaco-editor.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2c4354a0cbb76410ba3db93a2f6f7210a9a5a465 --- /dev/null +++ b/src/editor/code/monaco-editor/monaco-editor.tsx @@ -0,0 +1,99 @@ +import { + defineComponent, + nextTick, + onMounted, + onUnmounted, + ref, + watch, +} from 'vue'; +import './monaco-editor.scss'; +import { + getCodeProps, + getEditorEmits, + useNamespace, +} from '@ibiz-template/vue3-util'; +import * as monaco from 'monaco-editor'; +import loader from '@monaco-editor/loader'; +import { CodeEditorController } from '../code-editor.controller'; + +export const IBizCode = defineComponent({ + name: 'IBizCode', + props: getCodeProps(), + emits: getEditorEmits(), + setup(props, { emit }) { + const ns = useNamespace('code'); + + const currentVal = ref(''); + + watch( + () => props.value, + (newVal, oldVal) => { + if (newVal !== oldVal) { + if (!newVal) { + currentVal.value = ''; + } else { + currentVal.value = newVal; + } + } + }, + { immediate: true }, + ); + + const codeEditBox = ref(); + + let editor: monaco.editor.IStandaloneCodeEditor; + + const editorInit = () => { + nextTick(() => { + loader.init().then(loaderMonaco => { + // 初始化编辑器 + if (!editor) { + editor = loaderMonaco.editor.create(codeEditBox.value, { + value: currentVal.value, // 编辑器初始显示文字 + language: props.language, // 语言支持自行查阅demo + theme: props.theme, // 官方自带三种主题vs, hc-black, or vs-dark + foldingStrategy: 'indentation', + renderLineHighlight: 'all', // 行亮 + selectOnLineNumbers: true, // 显示行号 + minimap: { + enabled: true, + }, + readOnly: false, // 只读 + fontSize: 16, // 字体大小 + scrollBeyondLastLine: false, // 取消代码后面一大段空白 + overviewRulerBorder: false, // 不要滚动条的边框 + }); + setTimeout(() => { + editor.layout(); + }); + } else { + editor.setValue(''); + } + + // 监听值的变化 + editor.onDidChangeModelContent(() => { + currentVal.value = editor.getValue(); + emit('change', currentVal.value); + }); + + window.addEventListener('resize', () => { + editor.layout(); + }); + }); + }); + }; + + onMounted(() => { + editorInit(); + }); + + onUnmounted(() => { + editor?.dispose(); + }); + + return { ns, currentVal, codeEditBox }; + }, + render() { + return
; + }, +}); diff --git a/src/editor/html/html-editor.controller.ts b/src/editor/html/html-editor.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..734c873139556f6bcc208a1f166828ff254f5d42 --- /dev/null +++ b/src/editor/html/html-editor.controller.ts @@ -0,0 +1,124 @@ +import { + downloadFileFromBlob, + RuntimeError, + RuntimeModelError, +} from '@ibiz-template/core'; +import { convertNavData, EditorController } from '@ibiz-template/runtime'; +import { IHtml } from '@ibiz/model-core'; +import qs from 'qs'; + +/** + * html框编辑器控制器 + * + * @export + * @class HtmlEditorController + * @extends {EditorController} + */ +export class HtmlEditorController extends EditorController { + /** + * 上传参数 + */ + public uploadParams?: IParams; + + /** + * 下载参数 + */ + public exportParams?: IParams; + + protected async onInit(): Promise { + await super.onInit(); + + if (this.editorParams) { + const { uploadparams, exportparams } = this.editorParams; + + if (uploadparams) { + try { + this.uploadParams = JSON.parse(uploadparams); + } catch (error) { + throw new RuntimeModelError( + uploadparams, + `配置uploadparams没有按标准JSON格式`, + ); + } + } + if (exportparams) { + try { + this.exportParams = JSON.parse(exportparams); + } catch (error) { + throw new RuntimeModelError( + exportparams, + `配置exportparams没有按标准JSON格式`, + ); + } + } + } + } + + /** + * 计算文件的上传路径和下载路径 + * 下载路径文件id用%fileId%占位,替换即可 + * 配置编辑器参数uploadParams和exportParams时,会像导航参数一样动态添加对应的参数到url上 + * + * @author lxm + * @date 2022-11-17 13:11:43 + * @param {IData} data + * @returns {*} {{ uploadUrl: string; downloadUrl: string }} + */ + calcBaseUrl(data: IData): { uploadUrl: string; downloadUrl: string } { + let uploadUrl = `${ibiz.env.baseUrl}/${ibiz.env.appId}${ibiz.env.uploadFileUrl}`; + let downloadUrl = `${ibiz.env.baseUrl}/${ibiz.env.appId}${ibiz.env.downloadFileUrl}/%fileId%`; + let uploadParams: IParams = {}; + let exportParams: IParams = {}; + if (this.uploadParams) { + uploadParams = convertNavData( + this.uploadParams, + this.context, + this.params, + data, + ); + } + if (this.exportParams) { + exportParams = convertNavData( + this.exportParams, + this.context, + this.params, + data, + ); + } + uploadUrl += qs.stringify(uploadParams, { addQueryPrefix: true }); + downloadUrl += qs.stringify(exportParams, { addQueryPrefix: true }); + + return { uploadUrl, downloadUrl }; + } + + /** + * 请求url获取文件流,并用JS触发文件下载 + * + * @author lxm + * @date 2022-11-17 14:11:09 + * @param {string} url + * @param {IData} file + */ + fileDownload(file: { url: string; name: string }): void { + // 发送get请求 + ibiz.net + .request(file.url, { + method: 'get', + responseType: 'blob', + baseURL: '', // 已经有baseURL了,这里无需再写 + }) + .then((response: IData) => { + if (response.status !== 200) { + throw new RuntimeError('下载文件失败'); + } + // 请求成功,后台返回的是一个文件流 + if (!response.data) { + throw new RuntimeError('文件流数据不存在'); + } else { + // 获取文件名 + const fileName = file.name; + downloadFileFromBlob(response.data, fileName); + } + }); + } +} diff --git a/src/editor/html/html-editor.provider.ts b/src/editor/html/html-editor.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6c1be38d66ce84d8945c48a626c9df83d04e653 --- /dev/null +++ b/src/editor/html/html-editor.provider.ts @@ -0,0 +1,31 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + IEditorContainerController, + IEditorProvider, +} from '@ibiz-template/runtime'; +import { IHtml } from '@ibiz/model-core'; +import { HtmlEditorController } from './html-editor.controller'; + +/** + * html框编辑器适配器 + * + * @author lxm + * @date 2022-09-19 22:09:03 + * @export + * @class HtmlEditorProvider + * @implements {EditorProvider} + */ +export class HtmlEditorProvider implements IEditorProvider { + formEditor: string = 'IBizHtml'; + + gridEditor: string = 'IBizHtml'; + + async createController( + editorModel: IHtml, + parentController: IEditorContainerController, + ): Promise { + const c = new HtmlEditorController(editorModel, parentController); + await c.init(); + return c; + } +} diff --git a/src/editor/html/index.ts b/src/editor/html/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5169b035aa5eec85d9544748468d7db48fc2d63c --- /dev/null +++ b/src/editor/html/index.ts @@ -0,0 +1,3 @@ +export { IBizHtml } from './wang-editor/wang-editor'; +export * from './html-editor.controller'; +export * from './html-editor.provider'; diff --git a/src/editor/html/wang-editor/wang-editor.scss b/src/editor/html/wang-editor/wang-editor.scss new file mode 100644 index 0000000000000000000000000000000000000000..67c823a67853ca8a188aade074e5ec398c2bf971 --- /dev/null +++ b/src/editor/html/wang-editor/wang-editor.scss @@ -0,0 +1,8 @@ +@include b(html) { + display: inline-block; + width: 100%; + + .w-e-full-screen-container { + z-index: 9999; + } +} diff --git a/src/editor/html/wang-editor/wang-editor.tsx b/src/editor/html/wang-editor/wang-editor.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ef1f4c04d72633f90a8ba85cb94a7dc319440b34 --- /dev/null +++ b/src/editor/html/wang-editor/wang-editor.tsx @@ -0,0 +1,343 @@ +import { + onBeforeUnmount, + ref, + shallowRef, + onMounted, + watch, + Ref, + defineComponent, +} from 'vue'; +import { Editor, Toolbar } from '@wangeditor/editor-for-vue'; +import { IEditorConfig, IToolbarConfig } from '@wangeditor/editor'; +import type { IDomEditor } from '@wangeditor/editor'; +import { getCookie } from 'qx-util'; +import { + getEditorEmits, + getHtmlProps, + useNamespace, +} from '@ibiz-template/vue3-util'; +import { HtmlEditorController } from '../html-editor.controller'; +import './wang-editor.scss'; +import '@wangeditor/editor/dist/css/style.css'; + +type InsertFnType = (_url: string, _alt: string, _href: string) => void; + +export const IBizHtml = defineComponent({ + name: 'IBizHtml', + props: getHtmlProps(), + emits: getEditorEmits(), + setup(props, { emit }) { + const ns = useNamespace('html'); + + const c = props.controller!; + + // 编辑器实例,必须用 shallowRef,重要! + const editorRef = shallowRef(); + + // 内容 HTML + const valueHtml = ref(''); + + // 请求头 + const headers: Ref = ref({ + Authorization: `Bearer ${getCookie('access_token')}`, + }); + + // 上传文件路径 + const uploadUrl: Ref = ref(''); + + // 下载文件路径 + const downloadUrl: Ref = ref(''); + + // data响应式变更基础路径 + watch( + () => props.data, + newVal => { + if (newVal) { + const urls = c.calcBaseUrl(newVal); + uploadUrl.value = urls.uploadUrl; + downloadUrl.value = urls.downloadUrl; + } + }, + { immediate: true, deep: true }, + ); + + // 自定义校验链接 + const customCheckLinkFn = ( + text: string, + url: string, + ): string | boolean | undefined => { + if (!url) { + return; + } + // if (url.indexOf('http') !== 0) { + // return '链接必须以 http/https 开头'; + // } + return true; + + // 返回值有三种选择: + // 1. 返回 true ,说明检查通过,编辑器将正常插入链接 + // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) + // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息 + }; + + // 自定义转换链接 url + const customParseLinkUrl = (url: string): string => { + // if (url.indexOf('http') !== 0) { + // return `http://${url}`; + // } + return url; + }; + + // 工具栏配置 + const toolbarConfig: Partial = { + excludeKeys: ['group-video'], + }; + + // 编辑器配置 + const editorConfig: Partial = { + placeholder: '请输入内容...', + readOnly: props.readonly, + MENU_CONF: { + // 图片上传 + uploadImage: { + // 上传地址 + server: uploadUrl.value, + + // form-data fieldName ,默认值 'wangeditor-uploaded-image' + fieldName: 'file', + + // 单个文件的最大体积限制,默认为 2M + maxFileSize: 10 * 1024 * 1024, // 10M + + // 最多可上传几个文件,默认为 100 + maxNumberOfFiles: 10, + + // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] + allowedFileTypes: [], + + // 自定义增加 http header + headers: headers.value, + + // 跨域是否传递 cookie ,默认为 false + withCredentials: true, + + // 上传之前触发 + onBeforeUpload(file: File) { + // TS 语法 + // onBeforeUpload(file) { // JS 语法 + // file 选中的文件,格式如 { key: file } + return file; + + // 可以 return + // 1. return file 或者 new 一个 file ,接下来将上传 + // 2. return false ,不上传这个 file + }, + + // 上传进度的回调函数 + onProgress(progress: number) { + console.log('progress', progress); + }, + + // 单个文件上传成功之后 + onSuccess(file: File, res: IData) { + console.log(`${file.name} 上传成功`, res); + }, + + // 单个文件上传失败 + onFailed(file: File, res: IData) { + console.log(`${file.name} 上传失败`, res); + }, + + // 上传错误,或者触发 timeout 超时 + onError(file: File, err: IData, res: IData) { + console.log(`${file.name} 上传出错`, err, res); + }, + + // 自定义插入图片 + async customInsert(res: IData, insertFn: InsertFnType) { + const url = downloadUrl.value.replace('%fileId%', res.id); + const alt = res.filename; + // 从 res 中找到 url alt href ,然后插入图片 + insertFn(url, alt, ''); + }, + }, + // 插入链接 + insertLink: { + checkLink: customCheckLinkFn, // 也支持 async 函数 + parseLinkUrl: customParseLinkUrl, // 也支持 async 函数 + }, + // 更新链接 + editLink: { + checkLink: customCheckLinkFn, // 也支持 async 函数 + parseLinkUrl: customParseLinkUrl, // 也支持 async 函数 + }, + }, + }; + + // 组件销毁时,也及时销毁编辑器,重要! + onBeforeUnmount(() => { + const editor = editorRef.value; + if (editor == null) return; + + editor.destroy(); + }); + + // 编辑器回调函数 + // 编辑器创建完毕时的回调函数 + const handleCreated = (editor: IDomEditor) => { + editorRef.value = editor; // 记录 editor 实例,重要! + // 配置菜单 + // setTimeout(() => { + // const toolbar = DomEditor.getToolbar(editor); + // const curToolbarConfig = toolbar?.getConfig(); + // console.log(curToolbarConfig?.toolbarKeys); // 当前菜单排序和分组 + // }, 3000); + }; + // 编辑器内容、选区变化时的回调函数 + const handleChange = (editor: IDomEditor) => { + // console.log('change:', editor.getHtml()); + const html = editor.getHtml(); + // wangEditor初始值抛空字符串给后台 + const emitValue = html === '


' ? '' : html; + emit('change', emitValue); + }; + // 编辑器销毁时的回调函数。调用 editor.destroy() 即可销毁编辑器 + const handleDestroyed = (_editor: IDomEditor) => { + // console.log('destroyed', _editor); + }; + // 编辑器 focus 时的回调函数 + const handleFocus = (_editor: IDomEditor) => { + // console.log('focus', _editor); + }; + // 编辑器 blur 时的回调函数。 + const handleBlur = (_editor: IDomEditor) => { + // console.log('blur', _editor); + }; + // 自定义编辑器 alert + const customAlert = (info: string, type: string) => { + // eslint-disable-next-line no-alert + alert(`【自定义提示】${type} - ${info}`); + }; + // 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑 + const customPaste = ( + editor: IDomEditor, + event: ClipboardEvent, + callback: Function, + ) => { + // 返回值(注意,vue 事件的返回值,不能用 return) + // callback(false); // 返回 false ,阻止默认粘贴行为 + callback(true); // 返回 true ,继续默认的粘贴行为 + }; + + // 插入文本 + const insertText = (str: string) => { + const editor = editorRef.value; + if (editor == null) return; + + editor.insertText(str); + }; + + // 获取非格式化的 html + const printHtml = () => { + const editor = editorRef.value; + if (editor == null) return; + console.log(editor.getHtml()); + }; + + // 禁用编辑器 + const disable = () => { + const editor = editorRef.value; + if (editor == null) return; + editor.disable(); + }; + + // 取消禁用编辑器 + const enable = () => { + const editor = editorRef.value; + if (editor == null) return; + editor.enable(); + }; + + onMounted(() => { + // 监听值变化赋值 + watch( + () => props.value, + (newVal, oldVal) => { + if ( + newVal !== oldVal && + (typeof props.value === 'string' || newVal === null) + ) { + if (newVal === null) { + valueHtml.value = ''; + } else { + valueHtml.value = newVal as string; + } + } + }, + { immediate: true }, + ); + + // 监听disabled禁用 + watch( + () => props.disabled, + (newVal, oldVal) => { + if (newVal !== oldVal) { + if (newVal === true) { + disable(); + } else { + enable(); + } + } + }, + { immediate: true }, + ); + }); + + return { + ns, + editorRef, + mode: 'default', + valueHtml, + toolbarConfig, + editorConfig, + handleCreated, + handleChange, + handleDestroyed, + handleFocus, + handleBlur, + customAlert, + customPaste, + insertText, + printHtml, + disable, + enable, + }; + }, + render() { + return ( +
+
+ + +
+
+ ); + }, +}); diff --git a/src/editor/html/wang-editor/wang-edtor.d.ts b/src/editor/html/wang-editor/wang-edtor.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fbe3e549dd3d93e5b4374d881916376bde75382 --- /dev/null +++ b/src/editor/html/wang-editor/wang-edtor.d.ts @@ -0,0 +1,113 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// 声明合并,扩展 Editor 的类型定义 +declare module '@wangeditor/editor-for-vue' { + const Editor = import('vue').DefineComponent< + { + /** 编辑器模式 */ + mode: { + type: StringConstructor; + default: string; + }; + /** 编辑器默认内容 */ + defaultContent: { + type: PropType; + default: never[]; + }; + defaultHtml: { + type: StringConstructor; + default: string; + }; + /** 编辑器默认配置 */ + defaultConfig: { + type: PropType>; + default: {}; + }; + modelValue: { + type: StringConstructor; + default: string; + }; + }, + { + box: import('vue').Ref; + }, + unknown, + {}, + {}, + import('vue').ComponentOptionsMixin, + import('vue').ComponentOptionsMixin, + Record, + string, + import('vue').VNodeProps & + import('vue').AllowedComponentProps & + import('vue').ComponentCustomProps, + Readonly< + { + mode?: unknown; + defaultContent?: unknown; + defaultHtml?: unknown; + defaultConfig?: unknown; + modelValue?: unknown; + } & { + mode: string; + defaultContent: SlateDescendant[]; + defaultHtml: string; + defaultConfig: Partial; + modelValue: string; + } & {} + >, + { + mode: string; + defaultContent: SlateDescendant[]; + defaultHtml: string; + defaultConfig: Partial; + modelValue: string; + } + >; + + const Toolbar = import('vue').DefineComponent< + { + editor: { + type: PropType; + }; + /** 编辑器模式 */ + mode: { + type: StringConstructor; + default: string; + }; + /** 编辑器默认配置 */ + defaultConfig: { + type: PropType>; + default: {}; + }; + }, + { + selector: import('vue').Ref; + }, + unknown, + {}, + {}, + import('vue').ComponentOptionsMixin, + import('vue').ComponentOptionsMixin, + Record, + string, + import('vue').VNodeProps & + import('vue').AllowedComponentProps & + import('vue').ComponentCustomProps, + Readonly< + { + editor?: unknown; + mode?: unknown; + defaultConfig?: unknown; + } & { + mode: string; + defaultConfig: Partial; + } & { + editor?: IDomEditor | undefined; + } + >, + { + mode: string; + defaultConfig: Partial; + } + >; +} diff --git a/src/editor/index.ts b/src/editor/index.ts index 5f885397ba186eb8873c8975b53136d6e49487f0..77e9c7714fc25178e4ade5996a3b72d8663936ff 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -40,6 +40,8 @@ import { NumberRangeEditorProvider, } from './number-range'; import { IBizDateRangePicker, DateRangeEditorProvider } from './date-range'; +import { CodeEditorProvider, IBizCode } from './code'; +import { HtmlEditorProvider, IBizHtml } from './html'; export const IBizEditor = { install: (v: App) => { @@ -73,6 +75,8 @@ export const IBizEditor = { v.component(IBizPickerSelectView.name, IBizPickerSelectView); v.component(IBizNumberRangePicker.name, IBizNumberRangePicker); v.component(IBizDateRangePicker.name, IBizDateRangePicker); + v.component(IBizCode.name, IBizCode); + v.component(IBizHtml.name, IBizHtml); // 标签 registerEditorProvider('SPAN', () => new SpanEditorProvider()); @@ -231,6 +235,10 @@ export const IBizEditor = { 'DATERANGE_NOTIME', () => new DateRangeEditorProvider(), ); + // 代码编辑框 + registerEditorProvider('CODE', () => new CodeEditorProvider()); + // 富文本HTML编辑框 + registerEditorProvider('HTMLEDITOR', () => new HtmlEditorProvider()); }, }; diff --git a/src/index.ts b/src/index.ts index 6d111645320144d31ddda8cca43ec3ab0bcd649c..c9f610e4c71e435ead1079a9c4691d3c4dd796bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,7 @@ import { IBizGridExpBarControl, IBizDataViewExpBarControl, IBizTreeExpBarControl, + IBizSearchBarControl, } from './control'; import IBizEditor from './editor'; import { IBizView } from './view'; @@ -29,6 +30,7 @@ export * from './panel-component'; export * from './editor'; export * from './view'; export * from './view-engine'; +export * from './util'; export default { install: (v: App) => { @@ -53,6 +55,7 @@ export default { v.use(IBizCaptionBarControl); v.use(IBizTabExpPanelControl); v.use(IBizTreeExpBarControl); + v.use(IBizSearchBarControl); // 编辑器 v.use(IBizEditor); }, diff --git a/src/util/app-drawer/app-drawer-component.scss b/src/util/app-drawer/app-drawer-component.scss new file mode 100644 index 0000000000000000000000000000000000000000..98066c86b57c3a815c9f15489aa8811adcf8bda5 --- /dev/null +++ b/src/util/app-drawer/app-drawer-component.scss @@ -0,0 +1,19 @@ +@include b(drawer) { + @include set-component-css-var('drawer', $drawer); + + // 覆盖iView计算的z-index,使用变量 + .ivu-drawer-mask, + .ivu-drawer-wrap { + z-index: getCssVar('drawer', 'z-index') !important; + } + + .ivu-drawer-content { + @include flex(column); + + height: 100%; + + .ivu-drawer-body { + padding: 0; + } + } +} diff --git a/src/util/app-drawer/app-drawer-component.tsx b/src/util/app-drawer/app-drawer-component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..beeca6e99a863f5cda57842a208e250338a23ae6 --- /dev/null +++ b/src/util/app-drawer/app-drawer-component.tsx @@ -0,0 +1,114 @@ +import { defineComponent, PropType, ref, VNode } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { + IDrawerOptions, + IModalData, + IOverlayContainer, + Modal, + ViewMode, +} from '@ibiz-template/runtime'; +import { OverlayContainer } from '../overlay-container/overlay-container'; +import './app-drawer-component.scss'; +import { useUIStore } from '../store'; + +export const AppDrawerComponent = defineComponent({ + props: { + opts: { + type: Object as PropType, + default: () => ({}), + }, + }, + setup(props, ctx) { + const ns = useNamespace('drawer'); + + const isShow = ref(false); + + const { zIndex } = useUIStore(); + const drawerZIndex = zIndex.increment(); + + const modal = new Modal({ + mode: ViewMode.DRAWER, + dismiss: (data: IModalData) => { + zIndex.decrement(); + ctx.emit('dismiss', data); + }, + }); + + // 自身的所有关闭方式最终都走这个 + const onBeforeClose = async (done: () => void) => { + const isClose = await modal.dismiss(); + if (isClose) { + done(); + } + }; + + // 外部函数式调用 + const dismiss = (data?: IModalData) => { + modal.dismiss(data); + }; + + const present = () => { + isShow.value = true; + }; + + let direction = ''; + + switch (props.opts.placement) { + case 'left': + direction = 'ltr'; + break; + case 'top': + direction = 'ttb'; + break; + case 'bottom': + direction = 'btt'; + break; + default: + direction = 'rtl'; + } + + return { + ns, + isShow, + direction, + drawerZIndex, + modal, + dismiss, + present, + onBeforeClose, + }; + }, + render() { + return ( + + {this.$slots.default?.(this.modal)} + + ); + }, +}); + +/** + * 创建抽屉 + * + * @author chitanda + * @date 2022-12-29 15:12:57 + * @export + * @param {() => VNode} render + * @param {(IDrawerOptions | undefined)} [opts] + * @return {*} {IOverlayContainer} + */ +export function createDrawer( + render: () => VNode, + opts?: IDrawerOptions | undefined, +): IOverlayContainer { + return new OverlayContainer(AppDrawerComponent, render, opts); +} diff --git a/src/util/app-modal/app-modal-component.tsx b/src/util/app-modal/app-modal-component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a273c65c98239abe6d4f4ec95fa48d99f0a017f5 --- /dev/null +++ b/src/util/app-modal/app-modal-component.tsx @@ -0,0 +1,119 @@ +import { defineComponent, PropType, reactive, ref, VNode } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { isNumber } from 'lodash-es'; +import { + IModalData, + IModalOptions, + IOverlayContainer, + Modal, + ViewMode, +} from '@ibiz-template/runtime'; +import './modal.scss'; +import { OverlayContainer } from '../overlay-container/overlay-container'; +import { useUIStore } from '../store'; + +export const AppModalComponent = defineComponent({ + props: { + opts: { + type: Object as PropType, + default: () => ({}), + }, + }, + setup(props, ctx) { + const ns = useNamespace('modal'); + + const isShow = ref(false); + + const { zIndex } = useUIStore(); + const modalZIndex = zIndex.increment(); + + // 处理自定义样式,添加z-index变量 + const customStyle = reactive({ + [ns.cssVarBlockName('z-index')]: modalZIndex, + }); + const { width, height } = props.opts; + if (width) { + customStyle.width = isNumber(width) ? `${width}px` : width; + } + if (height) { + customStyle.height = isNumber(height) ? `${height}px` : height; + } + + // 合并options + const options = ref({ footerHide: true, modalClass: '' }); + if (props.opts) { + Object.assign(options.value, props.opts); + } + + const modal = new Modal({ + mode: ViewMode.MODAL, + dismiss: (data: IData) => { + zIndex.decrement(); + ctx.emit('dismiss', data); + }, + }); + + // Modal自身的所有关闭方式最终都走这个 + const onBeforeClose = async (done: () => void) => { + const isClose = await modal.dismiss(); + if (isClose) { + done(); + } + }; + + // 外部函数式调用 + const dismiss = (data?: IModalData) => { + modal.dismiss(data); + }; + + const present = () => { + isShow.value = true; + }; + + return { + ns, + isShow, + options, + modalZIndex, + customStyle, + modal, + present, + dismiss, + onBeforeClose, + }; + }, + render() { + return ( + + {this.$slots.default?.(this.modal)} + + ); + }, +}); + +/** + * 创建模态框 + * + * @author chitanda + * @date 2022-12-29 15:12:50 + * @export + * @param {() => VNode} render + * @param {(IModalOptions | undefined)} [opts] + * @return {*} {IOverlayContainer} + */ +export function createModal( + render: () => VNode, + opts?: IModalOptions | undefined, +): IOverlayContainer { + return new OverlayContainer(AppModalComponent, render, opts); +} diff --git a/src/util/app-modal/modal.scss b/src/util/app-modal/modal.scss new file mode 100644 index 0000000000000000000000000000000000000000..5d61c01bb81deef1b16501672633e73e3411f91d --- /dev/null +++ b/src/util/app-modal/modal.scss @@ -0,0 +1,28 @@ +@include b(modal) { + @include set-component-css-var('modal', $modal); + + // 覆盖element计算的z-index,使用变量 + &.el-dialog{ + z-index: getCssVar('modal', 'z-index') !important; + overflow: auto; + } + + .el-dialog__header{ + position: relative; + padding: 0; + } + + .el-dialog__body { + height: 100%; + padding: 0; + } + + // 模态关闭图标位置 + .el-dialog__headerbtn { + top: 0; + right: -16px; + z-index: getCssVar('modal', 'z-index'); + } + + +} diff --git a/src/util/app-popover/app-popover-component.tsx b/src/util/app-popover/app-popover-component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b17fe8b68918748a56722a962c8e81917ab4f03a --- /dev/null +++ b/src/util/app-popover/app-popover-component.tsx @@ -0,0 +1,220 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { arrow, computePosition, flip, offset, shift } from '@floating-ui/dom'; +import { + IModalData, + IPopoverOptions, + Modal, + ViewMode, +} from '@ibiz-template/runtime'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { + computed, + defineComponent, + onUnmounted, + PropType, + ref, + VNode, +} from 'vue'; +import './popover.scss'; +import { OverlayPopoverContainer } from '../overlay-popover-container/overlay-popover-container'; + +/** + * 计算飘窗显示 + * + * @author chitanda + * @date 2022-11-08 21:11:18 + * @param {HTMLElement} element + * @param {HTMLElement} el + * @param {HTMLElement} arrEl + * @param {IPopoverOptions} opts + * @return {*} {Promise} + */ +async function computePos( + element: HTMLElement, + el: HTMLElement, + arrEl: HTMLElement, + opts: IPopoverOptions, +): Promise { + const middlewareArr = [offset(opts.offsetOpts || 6), flip(), shift()]; + if (!opts.noArrow) { + middlewareArr.push(arrow({ element: arrEl! })); + } + const options = await computePosition(element, el, { + placement: opts.placement, + strategy: 'absolute', + middleware: middlewareArr, + }); + { + const { x, y, placement, middlewareData } = options; + const { style } = el; + style.left = `${x}px`; + style.top = `${y}px`; + + if (!opts.noArrow) { + // 箭头位置 + const { x: arrowX, y: arrowY } = middlewareData.arrow!; + + const staticSide: string = ( + { + top: 'bottom', + right: 'left', + bottom: 'top', + left: 'right', + } as IData + )[placement.split('-')[0]]; + + Object.assign(arrEl.style, { + left: arrowX != null ? `${arrowX}px` : '', + top: arrowY != null ? `${arrowY}px` : '', + right: '', + bottom: '', + [staticSide]: '-4px', + }); + } + } +} + +const AppPopoverComponent = defineComponent({ + props: { + opts: { + type: Object as PropType, + default: () => ({}), + }, + }, + setup(props, ctx) { + // 样式命名空间 + const ns = useNamespace('popover'); + // 是否显示 + const isShow = ref(false); + // 是否悬浮在内容区,当悬浮在内容区时 autoClose 禁用 + const isHover = ref(false); + // 跟 dom 元素 + const el = ref(); + // arrow dom 元素 + const arrEl = ref(); + // 是否可以自动关闭 + const autoClose = computed(() => { + return props.opts.autoClose === true && isHover.value === false; + }); + + // 鼠标入飘窗内容区 + function onMouseenter(e: MouseEvent): void { + e.stopPropagation(); + isHover.value = true; + } + + // 鼠标出飘窗内容区 + function onMouseleave(e: MouseEvent): void { + e.stopPropagation(); + isHover.value = false; + } + + const modal = new Modal({ + mode: ViewMode.POPOVER, + dismiss: (data: IModalData) => { + console.log('popover-dismiss'); + + ctx.emit('dismiss', data); + }, + }); + + let isCloseing = false; + + // 点击容器关闭飘窗 + async function dismiss(data?: IModalData) { + isCloseing = true; + await modal.dismiss(data); + setTimeout(() => { + isCloseing = false; + }, 300); + } + + // 组件销毁用于事件触发 + function destroy(e: Event): void { + e.stopPropagation(); + if (autoClose.value && !isCloseing) { + dismiss(); + } + } + + function addEvents(): void { + window.addEventListener('mousedown', destroy, { capture: true }); + window.addEventListener('blur', destroy, { capture: true }); + window.addEventListener('resize', destroy, { capture: true }); + } + + function removeEvents(): void { + window.removeEventListener('mousedown', destroy, { capture: true }); + window.removeEventListener('blur', destroy, { capture: true }); + window.removeEventListener('resize', destroy, { capture: true }); + } + + addEvents(); + + onUnmounted(() => { + removeEvents(); + }); + + /** + * 飘窗显示并计算位置 + * + * @author chitanda + * @date 2022-11-09 12:11:04 + * @param {HTMLElement} target + * @return {*} {Promise} + */ + async function present(target: HTMLElement): Promise { + isShow.value = true; + await computePos(target, el.value!, arrEl.value!, props.opts); + } + + return { + ns, + el, + arrEl, + isShow, + isHover, + modal, + present, + dismiss, + onMouseenter, + onMouseleave, + }; + }, + render() { + return ( +
+ {!this.opts.noArrow && ( +
+ )} + {this.$slots.default?.(this.modal)} +
+ ); + }, +}); + +/** + * 创建飘窗 + * + * @author chitanda + * @date 2022-12-29 15:12:59 + * @export + * @param {() => VNode} render + * @param {IPopoverOptions} [opts] + * @return {*} {OverlayPopoverContainer} + */ +export function createPopover( + render: () => VNode, + opts?: IPopoverOptions, +): OverlayPopoverContainer { + return new OverlayPopoverContainer(AppPopoverComponent, render, opts); +} diff --git a/src/util/app-popover/popover.scss b/src/util/app-popover/popover.scss new file mode 100644 index 0000000000000000000000000000000000000000..9cc6d76e347f1476066787283fbb153da2297edc --- /dev/null +++ b/src/util/app-popover/popover.scss @@ -0,0 +1,24 @@ +@include b(popover) { + position: absolute; + top: 0; + left: 0; + z-index: 1; + display: none; + width: max-content; + padding: calc(getCssVar('padding') / 2); + background-color: getCssVar('bg-color'); + border: getCssVar('border'); + border-radius: getCssVar('border-radius', 'base'); + + @include e(arrow) { + position: absolute; + width: 8px; + height: 8px; + background: getCssVar('bg-color'); + transform: rotate(45deg); + } + + @include when(show) { + display: block; + } +} diff --git a/src/util/index.ts b/src/util/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6af9040a97f3b97b66baabb1aaf387b8d7988812 --- /dev/null +++ b/src/util/index.ts @@ -0,0 +1,9 @@ +export { LoadingUtil } from './loading-util/loading-util'; +export { MessageUtil } from './message-util/message-util'; +export { ModalUtil } from './modal-util/modal-util'; +export { NotificationUtil } from './notification-util/notification-util'; +export { OpenViewUtil } from './open-view-util/open-view-util'; +export { OverlayContainer } from './overlay-container/overlay-container'; +export { OverlayController } from './overlay-controller/overlay-controller'; +export { OverlayPopoverContainer } from './overlay-popover-container/overlay-popover-container'; +export * from './store'; diff --git a/src/util/loading-util/loading-util.ts b/src/util/loading-util/loading-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..8817dffb5fc4e4893fb5752daaac8d6cec2a64e5 --- /dev/null +++ b/src/util/loading-util/loading-util.ts @@ -0,0 +1,98 @@ +import { ILoadingUtil } from '@ibiz-template/runtime'; +import { ElLoading } from 'element-plus'; + +/** + * 全局加载动画工具 + * + * @author chitanda + * @date 2022-08-17 17:08:44 + * @export + * @class LoadingUtil + * @implements {ILoadingUtil} + */ +export class LoadingUtil implements ILoadingUtil { + /** + * 当前只在触发的全局加载次数 + * + * @author chitanda + * @date 2022-08-17 17:08:44 + * @protected + */ + protected count = 0; + + /** + * 当前在触发的重定向加载动画次数 + * + * @author chitanda + * @date 2022-10-08 17:10:02 + * @protected + */ + protected countRedirect = 0; + + /** + * element plus loading 实例 + * + * @author chitanda + * @date 2022-12-29 15:12:26 + * @protected + */ + protected loading!: { close: () => void }; + + /** + * 显示全局加载动画 + * + * @author chitanda + * @date 2022-12-29 15:12:26 + */ + show(): void { + if (this.count === 0) { + this.loading = ElLoading.service({ lock: true, fullscreen: true }); + } + this.count += 1; + } + + /** + * 隐藏全局加载动画 + * + * @author chitanda + * @date 2022-08-17 17:08:11 + */ + hide(): void { + if (this.count > 0) { + this.count -= 1; + } + if (this.count === 0) { + if (this.loading) { + this.loading.close(); + } + } + } + + /** + * 显示顶部加载动画 + * + * @author chitanda + * @date 2022-10-08 16:10:01 + */ + showRedirect(): void { + // if (this.countRedirect === 0) { + // NProgress.start(); + // } + // this.countRedirect += 1; + } + + /** + * 隐藏顶部加载动画 + * + * @author chitanda + * @date 2022-10-08 16:10:09 + */ + hideRedirect(): void { + // if (this.countRedirect > 0) { + // this.countRedirect -= 1; + // } + // if (this.countRedirect === 0) { + // NProgress.done(); + // } + } +} diff --git a/src/util/message-util/message-util.ts b/src/util/message-util/message-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..795e3aa90dc1ebf0bf9a75484ae650c6f06a4d5a --- /dev/null +++ b/src/util/message-util/message-util.ts @@ -0,0 +1,61 @@ +import { IMessageUtil } from '@ibiz-template/runtime'; +import { ElMessage } from 'element-plus'; + +/** + * 消息通知 + * + * @author chitanda + * @date 2022-08-17 16:08:24 + * @export + * @class MessageUtil + * @implements {IMessageUtil} + */ +export class MessageUtil implements IMessageUtil { + info( + msg: string, + duration?: number | undefined, + closable?: boolean | undefined, + ): void { + ElMessage.info({ + message: msg, + duration: duration ? duration * 1000 : duration, + showClose: closable, + }); + } + + success( + msg: string, + duration?: number | undefined, + closable?: boolean | undefined, + ): void { + ElMessage.success({ + message: msg, + duration: duration ? duration * 1000 : duration, + showClose: closable, + }); + } + + warning( + msg: string, + duration?: number | undefined, + closable?: boolean | undefined, + ): void { + ElMessage.warning({ + message: msg, + duration: duration ? duration * 1000 : duration, + showClose: closable, + }); + } + + error( + msg: string, + duration?: number | undefined, + closable?: boolean | undefined, + ): void { + ElMessage.error({ + message: msg, + duration: duration ? duration * 1000 : duration, + showClose: closable, + }); + } +} diff --git a/src/util/modal-util/modal-util.ts b/src/util/modal-util/modal-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4a9c9dfb092325312f6410d5be1eddb02dcb958 --- /dev/null +++ b/src/util/modal-util/modal-util.ts @@ -0,0 +1,37 @@ +import { IModalUtil, ModalParams } from '@ibiz-template/runtime'; +import { ElMessageBox } from 'element-plus'; + +/** + * 简洁确认操作框 + * + * @author chitanda + * @date 2022-08-17 16:08:52 + * @export + * @class ModalUtil + * @implements {IModalUtil} + */ +export class ModalUtil implements IModalUtil { + async info(params: ModalParams): Promise { + ElMessageBox.alert(params.desc, params.title, { type: 'info' }); + } + + async success(params: ModalParams): Promise { + ElMessageBox.alert(params.desc, params.title, { type: 'success' }); + } + + async warning(params: ModalParams): Promise { + ElMessageBox.alert(params.desc, params.title, { type: 'warning' }); + } + + async error(params: ModalParams): Promise { + ElMessageBox.alert(params.desc, params.title, { type: 'error' }); + } + + async confirm(params: ModalParams): Promise { + return new Promise(resolve => { + ElMessageBox.confirm(params.desc, params.title) + .then(() => resolve(true)) + .catch(() => resolve(false)); + }); + } +} diff --git a/src/util/notification-util/notification-util.ts b/src/util/notification-util/notification-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..92e64fc633e49a82f05d21f06f47e3fe4e9ca1bd --- /dev/null +++ b/src/util/notification-util/notification-util.ts @@ -0,0 +1,53 @@ +import { INotificationUtil, NotificationParams } from '@ibiz-template/runtime'; +import { ElNotification } from 'element-plus'; + +/** + * 在界面右上角显示可关闭的全局通知 + * + * @author chitanda + * @date 2022-08-17 16:08:26 + * @export + * @class NotificationUtil + * @implements {INotificationUtil} + */ +export class NotificationUtil implements INotificationUtil { + info(params: NotificationParams): void { + const duration = params.duration ? params.duration * 1000 : params.duration; + ElNotification({ + title: params.title, + message: params.desc, + type: 'info', + duration, + }); + } + + success(params: NotificationParams): void { + const duration = params.duration ? params.duration * 1000 : params.duration; + ElNotification({ + title: params.title, + message: params.desc, + type: 'success', + duration, + }); + } + + warning(params: NotificationParams): void { + const duration = params.duration ? params.duration * 1000 : params.duration; + ElNotification({ + title: params.title, + message: params.desc, + type: 'warning', + duration, + }); + } + + error(params: NotificationParams): void { + const duration = params.duration ? params.duration * 1000 : params.duration; + ElNotification({ + title: params.title, + message: params.desc, + type: 'error', + duration, + }); + } +} diff --git a/src/util/open-view-util/open-view-util.ts b/src/util/open-view-util/open-view-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..d596400eb089d0dbcfb8d85500e0936248bdd4b9 --- /dev/null +++ b/src/util/open-view-util/open-view-util.ts @@ -0,0 +1,132 @@ +import { IAppView } from '@ibiz/model-core'; +import { IModalData, IOpenViewUtil } from '@ibiz-template/runtime'; +import { generateRoutePath } from '@ibiz-template/vue3-util'; +import { Router } from 'vue-router'; +import { + getDrawerPlacement, + openViewDrawer, + openViewModal, + openViewPopover, +} from '../overlay-view-util/overlay-view-util'; + +/** + * 打开视图方式工具类 + * + * @description 此实现类挂载在 ibiz.openViewUtil + * @author chitanda + * @date 2022-08-16 20:08:54 + * @export + * @class OpenViewUtil + * @implements {IOpenViewUtil} + */ +export class OpenViewUtil implements IOpenViewUtil { + // eslint-disable-next-line no-useless-constructor, no-empty-function + constructor(protected router: Router) {} + + root( + appView: IAppView, + context: IContext, + params?: IParams | undefined, + ): void { + const { path } = generateRoutePath( + appView, + this.router.currentRoute.value, + context, + params, + ); + + this.router.push({ path }); + } + + /** + * 模态打开视图 + * + * @author lxm + * @date 2022-09-12 01:09:06 + * @param {IAppView} appView + * @param {(IContext | undefined)} [context] + * @param {(IParams | undefined)} [params] + * @returns {*} {Promise} + */ + async modal( + appView: IAppView, + context: IContext, + params?: IParams | undefined, + ): Promise { + // 设置默认的modal参数 + const opts = { + width: appView.width || '80%', + height: appView.height || '80%', + footerHide: true, + }; + + return openViewModal( + { + context, + params, + modelData: appView, + }, + opts, + ); + } + + async popover( + appView: IAppView, + event: MouseEvent, + context: IContext, + params?: IParams | undefined, + ): Promise { + return openViewPopover( + event, + { + context, + params, + modelData: appView, + }, + { autoClose: true, placement: 'bottom' }, + ); + } + + /** + * 抽屉打开视图 + * + * @author lxm + * @date 2022-09-15 15:09:50 + * @param {IAppView} appView + * @param {(IContext | undefined)} [context] + * @param {(IParams | undefined)} [params] + * @returns {*} {Promise} + */ + async drawer( + appView: IAppView, + context: IContext, + params?: IParams | undefined, + ): Promise { + const placement = getDrawerPlacement(appView.openMode!); + + // 设置默认的modal参数 + const opts = { + width: appView.width || '800', + height: appView.height || '600', + placement, + }; + + return openViewDrawer( + { + context, + params, + modelData: appView, + }, + opts, + ); + } + + async custom( + appView: IAppView, + context: IContext, + params?: IParams | undefined, + ): Promise { + ibiz.log.warn('openUserCustom', appView, context, params); + throw new Error(); + } +} diff --git a/src/util/overlay-container/overlay-container.ts b/src/util/overlay-container/overlay-container.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e27679c4b7cc835b28d35409925ee6c0fdf756e --- /dev/null +++ b/src/util/overlay-container/overlay-container.ts @@ -0,0 +1,154 @@ +/* eslint-disable no-unused-vars */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { QXEvent } from 'qx-util'; +import { IOverlayContainer } from '@ibiz-template/runtime'; +import { App, Component, h, VNode } from 'vue'; +import { RuntimeError } from '@ibiz-template/core'; + +/** + * 全局弹出承载组件 + * + * @author chitanda + * @date 2022-11-09 12:11:09 + * @export + * @class OverlayContainer + */ +export class OverlayContainer implements IOverlayContainer { + protected vm?: App; + + /** + * 具体模态组件 + * + * @author chitanda + * @date 2022-11-09 12:11:34 + * @protected + * @type {*} + */ + protected modal: any; + + /** + * 外面调用dismiss时传的result结果 + * + * @author lxm + * @date 2022-11-09 20:11:06 + * @protected + * @type {unknown} + */ + protected result?: unknown; + + /** + * 内部事件 + * + * @author chitanda + * @date 2022-11-09 12:11:42 + * @protected + */ + protected evt: QXEvent<{ dismiss: (data?: unknown) => void }> = new QXEvent(); + + /** + * 创建全局呈现 + * + * @author chitanda + * @date 2022-11-09 14:11:52 + * @param {unknown} component + * @param {(h: CreateElement) => VNode} render + * @param {IPopoverOptions} [opts] + */ + constructor( + protected component: unknown, + protected render: (...args: any[]) => VNode, + protected opts?: O, + ) { + this.init(); + } + + static createVueApp( + _rootComponent: Component, + _rootProps?: IData, + ): App { + throw new RuntimeError('没有注入createVueApp方法'); + } + + /** + * 初始化飘窗 + * + * @author chitanda + * @date 2022-11-09 12:11:55 + * @protected + * @return {*} {void} + */ + protected init(): void { + const self = this; + const { render, opts } = this; + const container = document.createElement('div'); + document.body.appendChild(container); + const vm = OverlayContainer.createVueApp({ + mounted() { + self.modal = this.$refs.root; + }, + unmounted() { + document.body.removeChild(container); + self.evt.emit('dismiss', self.result); + }, + render() { + return h( + self.component as string, + { + ref: 'root', + opts, + onDismiss(data: IData) { + self.result = data; + vm.unmount(); + }, + }, + { default: render }, + ); + }, + }); + ibiz.plugin.register(vm); + vm.mount(container); + this.vm = vm; + } + + /** + * 打开飘窗 + * + * @author chitanda + * @date 2022-11-09 12:11:52 + * @param {HTMLElement} target + * @return {*} {Promise} + */ + async present(): Promise { + return this.modal.present(); + } + + /** + * 手动调用关闭飘窗 + * + * @author chitanda + * @date 2022-11-09 12:11:39 + * @param {unknown} [data] + * @return {*} {Promise} + */ + async dismiss(data?: unknown): Promise { + this.modal.dismiss(data); + } + + /** + * 订阅窗口关闭 + * + * @author chitanda + * @date 2022-11-09 12:11:20 + * @template T + * @return {*} {Promise} + */ + async onWillDismiss(): Promise { + return new Promise(resolve => { + const callback = (data: unknown) => { + resolve(data as T); + this.evt.off('dismiss', callback); + }; + this.evt.on('dismiss', callback); + }); + } +} diff --git a/src/util/overlay-controller/overlay-controller.ts b/src/util/overlay-controller/overlay-controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..29b63b78c4997fe195ed83c078b2c6af29f6ba04 --- /dev/null +++ b/src/util/overlay-controller/overlay-controller.ts @@ -0,0 +1,93 @@ +import { + IOverlayController, + IDrawerOptions, + IPopoverOptions, + IModalOptions, + IOverlayContainer, + IOverlayPopoverContainer, +} from '@ibiz-template/runtime'; +import { isFunction } from 'lodash-es'; +import { h, resolveComponent } from 'vue'; +import { createPopover } from '../app-popover/app-popover-component'; +import { createModal } from '../app-modal/app-modal-component'; +import { createDrawer } from '../app-drawer/app-drawer-component'; +/** + * 用不同呈现方式绘制组件的通用工具类 + * + * @author lxm + * @date 2022-11-08 16:11:09 + * @export + * @class OverlayController + * @implements {IOverlayController} + */ +export class OverlayController implements IOverlayController { + popover( + element: HTMLElement, + component: unknown, + props?: IParams | undefined, + opts?: IPopoverOptions | undefined, + ): Promise { + const popover = this.createPopover(component, props, opts); + popover.present(element); + return popover.onWillDismiss(); + } + + createPopover( + component: unknown, + props?: IParams | undefined, + opts?: IPopoverOptions | undefined, + ): IOverlayPopoverContainer { + return createPopover( + isFunction(component) + ? component + : () => h(resolveComponent(component as string), { ...props }), + opts, + ); + } + + drawer( + component: unknown, + props?: IParams | undefined, + opts?: IDrawerOptions | undefined, + ): Promise { + const drawer = this.createDrawer(component, props, opts); + drawer.present(); + return drawer.onWillDismiss(); + } + + createDrawer( + component: unknown, + props?: IParams | undefined, + opts?: IDrawerOptions | undefined, + ): IOverlayContainer { + return createDrawer( + isFunction(component) + ? component + : () => h(resolveComponent(component as string), { ...props }), + opts, + ); + } + + async modal( + component: unknown, + props?: IParams | undefined, + opts?: IModalOptions | undefined, + ): Promise { + const modal = this.createModal(component, props, opts); + modal.present(); + return modal.onWillDismiss(); + } + + createModal( + component: unknown, + props?: IParams | undefined, + opts?: IModalOptions | undefined, + ): IOverlayContainer { + return createModal( + isFunction(component) + ? component + : () => h(resolveComponent(component as string), { ...props }), + opts, + ); + } +} diff --git a/src/util/overlay-popover-container/overlay-popover-container.ts b/src/util/overlay-popover-container/overlay-popover-container.ts new file mode 100644 index 0000000000000000000000000000000000000000..659a20635e13b6f60e7c763d883882293274e4ad --- /dev/null +++ b/src/util/overlay-popover-container/overlay-popover-container.ts @@ -0,0 +1,30 @@ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-dupe-class-members */ +import { + IPopoverOptions, + IOverlayPopoverContainer, +} from '@ibiz-template/runtime'; +import { OverlayContainer } from '../overlay-container/overlay-container'; + +/** + * 飘窗组件呈现容器 + * + * @author chitanda + * @date 2022-11-09 14:11:46 + * @export + * @class OverlayPopoverContainer + * @extends {OverlayContainer} + * @implements {IOverlayPopoverContainer} + */ +export class OverlayPopoverContainer + extends OverlayContainer + implements IOverlayPopoverContainer +{ + present(): Promise; + + present(target: HTMLElement): Promise; + + present(target?: HTMLElement): Promise { + return this.modal.present(target); + } +} diff --git a/src/util/overlay-view-util/overlay-view-util.ts b/src/util/overlay-view-util/overlay-view-util.ts new file mode 100644 index 0000000000000000000000000000000000000000..62612a3e3384fa2b7d28c02c97e8a3de62c994f3 --- /dev/null +++ b/src/util/overlay-view-util/overlay-view-util.ts @@ -0,0 +1,76 @@ +import { + IDrawerOptions, + IModal, + IModalData, + IModalOptions, + IPopoverOptions, +} from '@ibiz-template/runtime'; +import { h, resolveComponent, VNode } from 'vue'; + +export function createOverlayView( + props?: IParams | undefined, +): (modal: IModal) => VNode { + return (modal: IModal) => { + const viewShell = resolveComponent('IBizViewShell'); + return h(viewShell, { + ...props, + modal, + }); + }; +} + +export async function openViewModal( + props?: IParams | undefined, + opts?: IModalOptions | undefined, +): Promise { + const overlay = ibiz.overlay.createModal( + createOverlayView(props), + undefined, + opts, + ); + overlay.present(); + const result: IModalData | undefined = await overlay.onWillDismiss(); + return result || { ok: false }; +} + +export async function openViewDrawer( + props?: IParams | undefined, + opts?: IDrawerOptions | undefined, +): Promise { + const overlay = ibiz.overlay.createDrawer( + createOverlayView(props), + undefined, + opts, + ); + await overlay.present(); + const result: IModalData | undefined = await overlay.onWillDismiss(); + return result || { ok: false }; +} + +export async function openViewPopover( + event: MouseEvent, + props?: IParams | undefined, + opts?: IPopoverOptions | undefined, +): Promise { + const overlay = ibiz.overlay.createPopover( + createOverlayView(props), + undefined, + opts, + ); + overlay.present(event.target as HTMLElement); + const result: IModalData | undefined = await overlay.onWillDismiss(); + return result || { ok: false }; +} + +const PlacementMap: IData = { + DRAWER_LEFT: 'left', + DRAWER_RIGHT: 'right', + DRAWER_TOP: 'top', + DRAWER_BOTTOM: 'bottom', +}; + +export function getDrawerPlacement( + openMode: string, +): IDrawerOptions['placement'] { + return PlacementMap[openMode] || 'right'; +} diff --git a/src/util/store/app-store/app-store.ts b/src/util/store/app-store/app-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..06c5be0193c5f62fbdd397933466d1b8949d6fd7 --- /dev/null +++ b/src/util/store/app-store/app-store.ts @@ -0,0 +1,10 @@ +import { defineStore } from 'pinia'; +import { reactive } from 'vue'; + +export interface IAppStore {} + +export const useAppStore = defineStore('appStore', () => { + const appStore = reactive({}); + + return { appStore }; +}); diff --git a/src/util/store/index.ts b/src/util/store/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ead684cb5b831527c09b29aef7ea9b219aefe28b --- /dev/null +++ b/src/util/store/index.ts @@ -0,0 +1,6 @@ +import { createPinia } from 'pinia'; + +export * from './app-store/app-store'; +export * from './ui-store/ui-store'; + +export const piniaInstance = createPinia(); diff --git a/src/util/store/ui-store/ui-store.ts b/src/util/store/ui-store/ui-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..d067f22ed25ce2c6cd4e97282d55293852b79dff --- /dev/null +++ b/src/util/store/ui-store/ui-store.ts @@ -0,0 +1,24 @@ +import { defineStore } from 'pinia'; +import { reactive, Ref } from 'vue'; +import { useZIndexStore } from './z-index'; + +export interface IUIState { + /** + * ui层级 + * + * @author lxm + * @date 2022-08-18 21:08:48 + * @type {number} + */ + zIndex: Ref; +} + +export const useUIStore = defineStore('uiStore', () => { + const zIndex = useZIndexStore(); + + const UIStore = reactive({ + zIndex: zIndex.zIndex, + }); + + return { UIStore, zIndex }; +}); diff --git a/src/util/store/ui-store/z-index.ts b/src/util/store/ui-store/z-index.ts new file mode 100644 index 0000000000000000000000000000000000000000..46fe54729ba66db04b6f13521fba31fcbe4b90b7 --- /dev/null +++ b/src/util/store/ui-store/z-index.ts @@ -0,0 +1,53 @@ +import { Ref, ref } from 'vue'; + +export interface IzIndexStore { + /** + * 当前最高的ui层级 + * + * @author lxm + * @date 2022-08-18 21:08:48 + * @type {number} + */ + zIndex: Ref; + + /** + * 增加Ui层级,返回增加后的层级 + * + * @author lxm + * @date 2022-08-18 21:08:22 + */ + increment(): number; + + /** + * 减少Ui层级 + * + * @author lxm + * @date 2022-08-18 21:08:22 + */ + decrement(): void; +} + +/** + * 定义zIndex的全局状态变量 + * + * @author lxm + * @date 2022-08-18 21:08:26 + * @export + * @returns {*} + */ +export function useZIndexStore(): IzIndexStore { + const DEFAULT_INDEX = 500; + const INCREMENT_VALUE = 1; + const zIndex = ref(DEFAULT_INDEX); + + function increment(): number { + zIndex.value += INCREMENT_VALUE; + return zIndex.value; + } + + function decrement() { + zIndex.value -= INCREMENT_VALUE; + } + + return { zIndex, increment, decrement }; +} diff --git a/src/view/404-view/404-view.scss b/src/view/404-view/404-view.scss new file mode 100644 index 0000000000000000000000000000000000000000..c84e6480533d7fb278de5e14a416f32d741fefe5 --- /dev/null +++ b/src/view/404-view/404-view.scss @@ -0,0 +1,17 @@ +@include b(404-view) { + @include flex(row, center, center); + + width: 100%; + height: 100%; + background-color: #fff; + @include b(404-view-text) { + @include e(text1) { + margin-bottom: 20px; + } + } + + @include when(top) { + width: 100vw; + height: 100vh; + } +} diff --git a/src/view/404-view/404-view.tsx b/src/view/404-view/404-view.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aadcc89775e11d3873b8d47351e5b48f79930128 --- /dev/null +++ b/src/view/404-view/404-view.tsx @@ -0,0 +1,49 @@ +import { useNamespace } from '@ibiz-template/vue3-util'; +import { computed, defineComponent, onMounted } from 'vue'; +import './404-view.scss'; +import { useRoute, useRouter } from 'vue-router'; + +export const View404 = defineComponent({ + setup() { + const ns = useNamespace('404-view'); + const router = useRouter(); + const route = useRoute(); + const gotoIndexView = () => { + router.push('/'); + }; + + onMounted(() => { + setTimeout(() => { + const el = document.querySelector('.app-loading-x') as HTMLDivElement; + if (el) { + el.style.display = 'none'; + } + }, 300); + }); + + const isTop = computed(() => { + return !route.params.view1; + }); + + return { ns, isTop, gotoIndexView }; + }, + render() { + return ( +
+ +
+
+ 抱歉,您访问的页面不存在! +
+ {this.isTop ? ( +
+ 您要找的页面不存在,请返回 + 首页 + 继续浏览 +
+ ) : null} +
+
+ ); + }, +}); diff --git a/src/view/app-redirect-view/app-redirect-view.tsx b/src/view/app-redirect-view/app-redirect-view.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ce83b69e7d7f7e16aa7de3412b3d9ff563d38461 --- /dev/null +++ b/src/view/app-redirect-view/app-redirect-view.tsx @@ -0,0 +1,32 @@ +import { defineComponent, onMounted, onUnmounted } from 'vue'; +import { IBizContext } from '@ibiz-template/core'; +// import { toLocalOpenWFRedirectView } from '@ibiz-template/runtime'; + +export const AppRedirectView = defineComponent({ + setup() { + const context = IBizContext.create(ibiz.appData?.context || {}); + + onUnmounted(() => { + context.destroy(); + }); + + // const { href } = window.location; + + async function toRedirect(): Promise { + // const modelService = await ibiz.model.getModelService(); + // await toLocalOpenWFRedirectView(modelService, context, href); + } + + onMounted(() => { + const el = document.querySelector('.app-loading-x') as HTMLDivElement; + if (el) { + el.style.display = 'block'; + } + }); + + toRedirect(); + }, + render() { + return
重定向跳转中
; + }, +}); diff --git a/src/view/common/index.ts b/src/view/common/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5b4224586c0786ac20f8b89b79d8df61606857bd --- /dev/null +++ b/src/view/common/index.ts @@ -0,0 +1,14 @@ +import { withInstall } from '@ibiz-template/vue3-util'; +import { App } from 'vue'; +import { registerViewProvider } from '@ibiz-template/runtime'; +import { ViewProvider } from './view.provider'; +import { View } from './view'; +import { IBizViewEngine } from '../../view-engine'; + +export { ViewProvider }; + +export const IBizView = withInstall(View, function (v: App) { + v.use(IBizViewEngine); + v.component(View.name, View); + registerViewProvider('DEFAULT', () => new ViewProvider()); +}); diff --git a/src/view/view.provider.ts b/src/view/common/view.provider.ts similarity index 100% rename from src/view/view.provider.ts rename to src/view/common/view.provider.ts diff --git a/src/view/view.scss b/src/view/common/view.scss similarity index 100% rename from src/view/view.scss rename to src/view/common/view.scss diff --git a/src/view/view.tsx b/src/view/common/view.tsx similarity index 100% rename from src/view/view.tsx rename to src/view/common/view.tsx diff --git a/src/view/index.ts b/src/view/index.ts index cc42e72bc634082176d81926148a8f061cf79057..5735b9959066ef0181e1602fff7135fc093bcb5c 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -1,14 +1,5 @@ -import { withInstall } from '@ibiz-template/vue3-util'; -import { App } from 'vue'; -import { registerViewProvider } from '@ibiz-template/runtime'; -import { ViewProvider } from './view.provider'; -import { View } from './view'; -import { IBizViewEngine } from '../view-engine'; - -export { ViewProvider }; - -export const IBizView = withInstall(View, function (v: App) { - v.use(IBizViewEngine); - v.component(View.name, View); - registerViewProvider('DEFAULT', () => new ViewProvider()); -}); +export * from './404-view/404-view'; +export * from './app-redirect-view/app-redirect-view'; +export * from './common/index'; +export * from './login-view/login-view'; +export * from './todo-redirect/todo-redirect'; diff --git a/src/view/login-view/login-view.scss b/src/view/login-view/login-view.scss new file mode 100644 index 0000000000000000000000000000000000000000..b19a83d4e562843b7243bcbf87155a5317cacb1e --- /dev/null +++ b/src/view/login-view/login-view.scss @@ -0,0 +1,78 @@ +/* stylelint-disable selector-class-pattern */ +@include b(login-view) { + display: flex; + align-items: center; + justify-content: center; + width: 100vw; + height: 100vh; + background: linear-gradient(120deg, #a1c4fd 0%, #c2e9fb 100%); +} + + +@include b(login-view-box) { + width: 430px; + margin-bottom: 80px; + overflow: hidden; + background: #fff; +} + +@media screen and (width <= 720px) { + @include b(login-view-box) { + width: 340px; + margin-top: 0; + } +} + +@include b(login-view-box-header) { + background: #ccf; + + > img { + display: block; + width: 430px; + margin: 0 auto; + user-select: none; + } +} + +@include b(login-view-box-main) { + position: relative; + + @include e(avatar) { + position: absolute; + top: -50px; + right: calc(50% - 50px); + z-index: 2; + display: block; + width: 100px; + height: 100px; + user-select: none; + border: 4px solid #fff; + border-radius: 50%; + } +} + +@include b(login-view-box-main-content) { + padding: 100px 40px 40px; + + // 浏览器自动填写之后的输入框的背景色和文字颜色 + .el-input__inner:-webkit-autofill{ + box-shadow:0 0 0 1000px #fff inset; + -webkit-text-fill-color: #606266; + } + + .el-button { + width: 100%; + margin-top: 15px; + font-weight: 300; + letter-spacing: 2px; + } + + .el-input__prefix{ + width: 20px; + margin-left: -1px; + } + + ion-icon { + font-size: 16px; + } +} diff --git a/src/view/login-view/login-view.tsx b/src/view/login-view/login-view.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9494cd05f8fe9dffca5924ba454195d4292cb2b6 --- /dev/null +++ b/src/view/login-view/login-view.tsx @@ -0,0 +1,162 @@ +import { CoreConst } from '@ibiz-template/core'; +import { defineComponent, onMounted, reactive, ref } from 'vue'; +import { setCookie } from 'qx-util'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { useRoute, useRouter } from 'vue-router'; +import './login-view.scss'; + +interface LoginData { + username: string; + password: string; +} + +const rules = { + username: [ + { + required: true, + message: '请输入账号', + trigger: 'blur', + }, + ], + password: [ + { + required: true, + message: '请输入密码', + trigger: 'blur', + }, + { + type: 'string', + min: 6, + message: '密码长度不能少于6位', + trigger: 'blur', + }, + ], +}; + +export const LoginView = defineComponent({ + setup() { + const ns = useNamespace('login-view'); + + const loginData = reactive({ + username: '', + password: '', + }); + + const formRef = ref(null); + + const route = useRoute(); + const router = useRouter(); + const ru = (route.query.ru as string) || '/'; + + onMounted(() => { + setTimeout(() => { + const el = document.querySelector('.app-loading-x') as HTMLDivElement; + if (el) { + el.style.display = 'none'; + } + }, 300); + }); + + const loading = ref(false); + + ibiz.appData = undefined; + ibiz.orgData = undefined; + + const onClick = async () => { + formRef.value!.validate(async (vaild: boolean) => { + if (vaild) { + try { + loading.value = true; + const res = await ibiz.auth.v7login( + loginData.username, + loginData.password, + ); + if (res.ok) { + const { data } = res; + if (data && data.token) { + setCookie(CoreConst.TOKEN, data.token, 0, true); + const expiredDate = + new Date().getTime() + (data.expirein || 7199) * 1000; + setCookie(CoreConst.TOKEN_EXPIRES, `${expiredDate}`, 0, true); + router.push({ path: ru }); + return; + } + } + ibiz.notification.error({ + title: res.data?.message || '登录失败', + }); + loading.value = false; + } catch (error) { + ibiz.notification.error({ + title: (error as IData).response?.data?.message || '登录失败', + }); + loading.value = false; + } + } + }); + }; + + // 支持enter登录 + const onKeyUp = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + onClick(); + } + }; + + return () => ( +
+
+
+ +
+
+ +
+ + + onKeyUp(e)} + > + {{ + prefix: () => , + }} + + + + onKeyUp(e)} + > + {{ + prefix: () => , + }} + + + + + 登录 + + + +
+
+
+
+ ); + }, +}); diff --git a/src/view/todo-redirect/todo-redirect.tsx b/src/view/todo-redirect/todo-redirect.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4ddce78d0c3fbb9479c85288b1df0dbfd1cc8003 --- /dev/null +++ b/src/view/todo-redirect/todo-redirect.tsx @@ -0,0 +1,51 @@ +import qs from 'qs'; +import { defineComponent } from 'vue'; +import { useRouter } from 'vue-router'; + +export const TodoRedirect = defineComponent({ + setup() { + const router = useRouter(); + + const { href } = window.location; + + const i = href.lastIndexOf('?'); + + const queryStr: string = decodeURIComponent( + href.substring(i + 1, href.length), + ); + + if (!queryStr) { + throw new Error(`重定向参数不足无法跳转`); + } + + const params = qs.parse(queryStr, { delimiter: ';' }) as IData; + + const { apptype, todotype, todoid } = params; + + const data: IData = { srfapptype: 'pc', srfapp: '' }; + + if (!apptype) { + data.todourltype = 'RouterUrl'; + } + + async function getLinkUrl(): Promise { + const res = await ibiz.net.post(`/systodos/${todoid}/getlinkurl`, data); + let url: string = res.data.linkurl; + // apptype存在,带ip、端口等完整数据 + if (apptype) { + window.location.href = url; + } else { + if (url.indexOf('/') !== 0) { + url = `/${url}`; + } + url += `;srfwf=${todotype}`; + router.push(`/index${url}`); + } + } + + getLinkUrl(); + }, + render() { + return
待办列表重定向
; + }, +});