Merge branch 'client_sync' into v2

# Conflicts:
#	packages/ui/certd-client/.env
#	packages/ui/certd-client/CHANGELOG.md
#	packages/ui/certd-client/index.html
#	packages/ui/certd-client/package.json
#	packages/ui/certd-client/public/images/logo/rect-black.svg
#	packages/ui/certd-client/public/images/logo/square.svg
#	packages/ui/certd-client/src/layout/components/theme/index.vue
#	packages/ui/certd-client/src/layout/layout-framework.vue
#	packages/ui/certd-client/src/layout/layout-outside.vue
#	packages/ui/certd-client/src/main.ts
#	packages/ui/certd-client/src/plugin/fast-crud/index.tsx
#	packages/ui/certd-client/src/router/source/header.ts
#	packages/ui/certd-client/src/store/modules/settings.ts
#	packages/ui/certd-client/src/style/common.less
#	packages/ui/certd-client/src/views/crud/form/independent/index.vue
#	packages/ui/certd-client/src/views/framework/login/index.vue
#	packages/ui/certd-client/src/views/framework/register/index.vue
#	packages/ui/certd-client/vite.config.ts
pull/68/head
xiaojunnuo 2024-06-16 02:47:41 +08:00
commit fa33ff499d
160 changed files with 3209 additions and 708 deletions

View File

@ -2,3 +2,7 @@ VITE_APP_API=/api
#登录与权限关闭 #登录与权限关闭
VITE_APP_PM_ENABLED=true VITE_APP_PM_ENABLED=true
VITE_APP_TITLE=Certd VITE_APP_TITLE=Certd
VITE_APP_SLOGAN=让你的证书永不过期
VITE_APP_COPYRIGHT=Copyright © 2021-2024 handsfree.work
VITE_APP_LOGO_PATH=./images/logo/logo.svg
VITE_APP_PROJECT_PATH=https://github.com/certd/certd

View File

@ -30,6 +30,8 @@ jobs:
- name: push to gitee # 4. 执行同步 - name: push to gitee # 4. 执行同步
run: | run: |
git remote add upstream https://gitee.com/fast-crud/fs-admin-antdv git remote add upstream https://gitee.com/fast-crud/fs-admin-antdv4
git push --set-upstream upstream main git push --set-upstream upstream main

View File

@ -9,7 +9,7 @@
"debug": "vite --mode debug --open", "debug": "vite --mode debug --open",
"debug:pm": "vite --mode debugpm", "debug:pm": "vite --mode debugpm",
"debug:force": "vite --force --mode debug", "debug:force": "vite --force --mode debug",
"build": "vite build ", "build": " vite build ",
"serve": "vite preview", "serve": "vite preview",
"preview": "vite preview", "preview": "vite preview",
"pretty-quick": "pretty-quick", "pretty-quick": "pretty-quick",
@ -22,98 +22,100 @@
"author": "Greper", "author": "Greper",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@ant-design/colors": "^7.0.0", "@ant-design/colors": "^7.0.2",
"@ant-design/icons-vue": "^6.1.0", "@ant-design/icons-vue": "^7.0.1",
"@aws-sdk/client-s3": "^3.383.0", "@aws-sdk/client-s3": "^3.535.0",
"@aws-sdk/s3-request-presigner": "^3.383.0", "@aws-sdk/s3-request-presigner": "^3.535.0",
"@fast-crud/fast-crud": "^1.21.0", "@fast-crud/fast-crud": "^1.21.0",
"@fast-crud/fast-extends": "^1.21.0", "@fast-crud/fast-extends": "^1.21.0",
"@fast-crud/ui-antdv4": "^1.21.0", "@fast-crud/ui-antdv4": "^1.21.0",
"@fast-crud/ui-interface": "^1.21.0", "@fast-crud/ui-interface": "^1.21.0",
"@iconify/iconify": "^3.1.1",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@soerenmartius/vue3-clipboard": "^0.1.2", "@soerenmartius/vue3-clipboard": "^0.1.2",
"ant-design-vue": "^4.1.2", "ant-design-vue": "^4.1.2",
"axios": "^1.3.4", "axios": "^1.6.8",
"axios-mock-adapter": "^1.21.2", "axios-mock-adapter": "^1.22.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
"better-scroll": "^2.5.1", "better-scroll": "^2.5.1",
"china-division": "^2.6.1", "china-division": "^2.7.0",
"core-js": "^3.32.0", "core-js": "^3.36.0",
"cos-js-sdk-v5": "^1.4.19", "cos-js-sdk-v5": "^1.7.0",
"cropperjs": "^1.5.13", "cropperjs": "^1.6.1",
"dayjs": "^1.11.9", "dayjs": "^1.11.10",
"highlight.js": "^11.8.0", "highlight.js": "^11.9.0",
"humanize-duration": "^3.27.3", "humanize-duration": "^3.27.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nanoid": "^4.0.0", "nanoid": "^4.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
"pinia": "2.1.6", "pinia": "2.1.7",
"qiniu-js": "^3.4.1", "qiniu-js": "^3.4.2",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.2",
"vue": "^3.4.0", "vue": "^3.4.21",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.10.2",
"vue-router": "^4.2.4", "vue-router": "^4.3.0",
"vuedraggable": "^2.24.3" "vuedraggable": "^2.24.3"
}, },
"devDependencies": { "devDependencies": {
"@certd/pipeline": "^1.20.10", "@certd/pipeline": "^1.20.10",
"@rollup/plugin-commonjs": "^25.0.3", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.5", "@types/chai": "^4.3.12",
"@types/lodash-es": "^4.17.8", "@types/lodash-es": "^4.17.12",
"@types/mocha": "^10.0.1", "@types/mocha": "^10.0.6",
"@types/node": "^20.4.7", "@types/node": "^20.11.28",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.3",
"@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^6.2.1", "@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-legacy": "^4.1.1", "@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.0.1", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/compiler-sfc": "^3.3.4", "@vue/compiler-sfc": "^3.4.21",
"@vue/eslint-config-typescript": "^11.0.3", "@vue/eslint-config-typescript": "^13.0.0",
"@vue/test-utils": "^2.4.1", "@vue/test-utils": "^2.4.5",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.18",
"caller-path": "^4.0.0", "caller-path": "^4.0.0",
"chai": "^4.3.7", "chai": "^5.1.0",
"dependency-cruiser": "^13.1.1", "dependency-cruiser": "^16.2.3",
"dot": "^1.1.3", "dot": "^1.1.3",
"eslint": "8.46.0", "eslint": "8.57.0",
"eslint-config-prettier": "^8.10.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.28.0", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.16.1", "eslint-plugin-vue": "^9.23.0",
"esno": "^0.17.0", "esno": "^4.7.0",
"husky": "^8.0.3", "husky": "^9.0.11",
"less": "^4.1.3", "less": "^4.2.0",
"less-loader": "^11.1.3", "less-loader": "^12.2.0",
"lint-staged": "^13.2.3", "lint-staged": "^15.2.2",
"postcss": "^8.4.27", "postcss": "^8.4.35",
"prettier": "^2.8.8", "prettier": "3.2.5",
"pretty-quick": "^3.1.3", "pretty-quick": "^4.0.0",
"rimraf": "^5.0.1", "rimraf": "^5.0.5",
"rollup": "^3.27.2", "rollup": "^4.13.0",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.12.0",
"stylelint": "^15.10.2", "stylelint": "^16.2.1",
"stylelint-config-prettier": "^9.0.5", "stylelint-config-prettier": "^9.0.5",
"stylelint-order": "^6.0.3", "stylelint-order": "^6.0.4",
"terser": "^5.19.2", "tailwindcss": "^3.4.1",
"ts-node": "^10.9.1", "terser": "^5.29.2",
"typescript": "5.1.6", "ts-node": "^10.9.2",
"unplugin-vue-define-options": "^1.3.14", "tslint": "^6.1.3",
"vite": "^4.4.8", "typescript": "5.4.2",
"unplugin-vue-define-options": "^1.4.2",
"vite": "^5.1.6",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.2",
"vite-plugin-optimize-persist": "^0.1.2", "vite-plugin-optimize-persist": "^0.1.2",
"vite-plugin-package-config": "^0.1.1", "vite-plugin-package-config": "^0.1.1",
"vite-plugin-purge-icons": "^0.9.2", "vite-plugin-purge-icons": "^0.10.0",
"vite-plugin-theme": "^0.8.6", "vite-plugin-theme": "^0.8.6",
"vite-plugin-windicss": "^1.9.0", "vite-plugin-windicss": "^1.9.3",
"vue-eslint-parser": "^9.3.1", "vue-eslint-parser": "^9.4.2",
"vue-tsc": "^1.8.8" "vue-tsc": "^1.8.8"
}, },
"husky": { "husky": {

View File

@ -1,7 +1,106 @@
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" <?xml version="1.0" encoding="UTF-8" standalone="no"?>
width="500" height="500" viewBox="0 0 500.000000 500.000000" <svg
> xmlns="http://www.w3.org/2000/svg"
<path d="M28.34 56.68h28.34V36.12H28.34a7.79 7.79 0 1 1 0-15.58h19.84v9.05h8.5V12H28.34a16.29 16.29 0 0 0 0 32.58h19.84v3.56H28.34a19.84 19.84 0 0 1 0-39.68h28.34V0H28.34a28.34 28.34 0 0 0 0 56.68z" width="210mm"
transform="translate(70, 76) scale(6,6)" height="210mm"
></path> viewBox="0 0 210 210"
version="1.1"
id="svg8"
>
<g id="layer1" style="display:inline">
<path
style="fill:#002255;stroke:none;stroke-width:0.625348"
d="M 35.587501,69.766667 V 59.766664 h 70.000109 69.99991 v 10.000003 9.999997 H 105.58761 35.587501 Z"
id="path12" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2"
width="32.244232"
height="20"
x="71.506088"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8"
width="32.244232"
height="20"
x="107.42467"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8"
width="32.244232"
height="20"
x="143.34325"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-4"
width="32.244232"
height="20"
x="71.506088"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8-3"
width="32.244232"
height="20"
x="107.42467"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8-2"
width="32.244232"
height="20"
x="143.34325"
y="129.82079" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-7"
width="32.244232"
height="20"
x="35.587502"
y="106.64581" />
<rect
style="fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-4-0"
width="32.244232"
height="20"
x="35.587502"
y="129.82079" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-9"
width="32.244232"
height="20"
x="71.506088"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-8-37"
width="32.244232"
height="20"
x="107.42467"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-8-5-8-4"
width="32.244232"
height="20"
x="143.34325"
y="82.941666" />
<rect
style="display:inline;fill:#2a7fff;fill-rule:evenodd;stroke-width:0.214311"
id="rect22-2-7-1"
width="32.244232"
height="20"
x="35.587502"
y="82.941666" />
</g>
<polygon
points="75.3,174.4 103.1,103.6 79.8,103.6 112.6,41.3 156.4,41.3 129.9,90.5 148.1,90.5 "
fill="#f6cc00"
id="polygon276"
transform="matrix(1.0930933,0,0,0.99853202,-17.517362,-0.52287941)" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,44 +0,0 @@
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="800" height="300" viewBox="0 0 800.000000 300.000000"
>
<g fill="#fff">
<path d="M28.34 56.68h28.34V36.12H28.34a7.79 7.79 0 1 1 0-15.58h19.84v9.05h8.5V12H28.34a16.29 16.29 0 0 0 0 32.58h19.84v3.56H28.34a19.84 19.84 0 0 1 0-39.68h28.34V0H28.34a28.34 28.34 0 0 0 0 56.68z"
transform="translate(40, 40) scale(4,4)"
></path>
<g transform="translate(280, 260) scale(2,2)">
<path d="M28.95-58.70L49.98-58.70L49.98-48.22L33.70-48.22Q27.37-48.22 24.46-47.34Q21.56-46.46 19.62-43.91L19.62-43.91Q18.30-42.15 17.82-40.39Q17.34-38.63 16.98-34.41L16.98-34.41L49.98-34.41L49.98-23.94L16.98-23.94Q17.69-16.37 21.47-13.42Q25.26-10.47 34.32-10.47L34.32-10.47L49.98-10.47L49.98 0L33.70 0Q27.10 0 22.09-0.79L22.09-0.79Q14.17-2.11 9.50-7.74L9.50-7.74Q2.82-15.84 2.82-29.66L2.82-29.66Q2.82-44.97 12.23-53.50L12.23-53.50Q15.49-56.41 19.14-57.55Q22.79-58.70 28.95-58.70L28.95-58.70Z"
transform="translate(0 0) "
></path>
<path d="M28.51-22.26L18.48-22.26L18.48 0L5.98 0L5.98-58.70L34.67-58.70Q46.99-58.70 51.83-55.53L51.83-55.53Q55.18-53.42 56.98-49.94Q58.78-46.46 58.78-42.33L58.78-42.33Q58.78-33.97 54.56-29.39L54.56-29.39Q51.92-26.66 47.61-25.26L47.61-25.26Q51.04-24.02 52.80-22.66Q54.56-21.30 55.97-18.66L55.97-18.66Q57.02-16.54 57.51-14.61Q57.99-12.67 58.26-8.89L58.26-8.89Q58.78-2.55 59.66 0L59.66 0L45.67 0Q45.06-2.02 44.35-8.36L44.35-8.36Q43.91-13.46 42.55-16.19Q41.18-18.92 38.46-20.50L38.46-20.50Q35.02-22.35 28.51-22.26L28.51-22.26ZM18.48-48.22L18.48-32.74L35.99-32.74Q40.39-32.74 42.24-34.06L42.24-34.06Q45.06-35.99 45.06-40.83L45.06-40.83Q45.06-46.20 40.83-47.70L40.83-47.70Q39.34-48.22 35.99-48.22L35.99-48.22L18.48-48.22Z"
transform="translate(58.855999999999995 0) "
></path>
<path d="M19.01 0L19.01-48.22L0.35-48.22L0.35-58.70L50.95-58.70L50.95-48.22L31.50-48.22L31.50 0L19.01 0Z"
transform="translate(126.68799999999999 0) "
></path>
<path d="M5.98 0L5.98-58.70L30.89-58.70Q40.22-58.70 45.32-56.85L45.32-56.85Q54.74-53.42 57.90-44.26L57.90-44.26Q60.28-37.22 60.28-29.22L60.28-29.22Q60.28-21.30 57.99-14.26L57.99-14.26Q55.70-7.04 50.42-3.61L50.42-3.61Q47.08-1.50 43.08-0.75Q39.07 0 30.89 0L30.89 0L5.98 0ZM30.89-48.22L18.48-48.22L18.48-10.47L30.89-10.47Q39.07-10.47 42.24-14.08L42.24-14.08Q44.18-16.37 45.36-20.50Q46.55-24.64 46.55-29.30L46.55-29.30Q46.55-34.50 45.14-38.81Q43.74-43.12 41.45-45.23L41.45-45.23Q38.10-48.22 30.89-48.22L30.89-48.22Z"
transform="translate(183.07999999999998 0) "
></path>
</g>
<!-- <path d="M13.00-21.91L21.24-21.91L21.24-17.23L14.04-17.23Q10.39-17.23-->
<!-- 9-15.62L9-15.62Q7.65-14.08-->
<!-- 7.65-10.93L7.65-10.93Q7.65-7.42 9.86-5.80L9.86-5.80Q10.75-5.17-->
<!-- 11.81-4.93Q12.87-4.68-->
<!-- 14.76-4.68L14.76-4.68L21.24-4.68L21.24-->
<!-- 0L13.00 0Q9.67 0 7.74-0.67Q5.80-1.35-->
<!-- 4.32-3.01L4.32-3.01Q1.48-6.17-->
<!-- 1.48-11.03L1.48-11.03Q1.48-16.88-->
<!-- 4.86-19.75L4.86-19.75Q6.21-20.93-->
<!-- 8.10-21.42Q9.99-21.91-->
<!-- 13.00-21.91L13.00-21.91ZM31.05-13.32L43.74-13.32L43.74-8.64L31.05-8.64Q31.23-6.48-->
<!-- 32.44-5.58Q33.66-4.68 36.41-4.68L36.41-4.68L43.74-4.68L43.74 0L35.73 0Q33.12 0 31.48-0.47Q29.83-0.94 28.39-2.02L28.39-2.02Q24.39-5.13 24.39-11.25L24.39-11.25Q24.39-15.21 26.50-18.18L26.50-18.18Q27.94-20.20 29.97-21.06Q31.99-21.91 35.28-21.91L35.28-21.91L43.74-21.91L43.74-17.23L35.73-17.23Q33.25-17.23 32.27-16.40Q31.27-15.57 31.05-13.32L31.05-13.32ZM48.64 0L48.64-21.91L57.55-21.91Q60.30-21.91 61.81-21.53Q63.31-21.15 64.31-20.25L64.31-20.25Q65.30-19.35 65.70-18Q66.10-16.65 66.10-14.13L66.10-14.13L66.10-12.01L60.30-12.01L60.30-13.18Q60.30-15.52 59.49-16.38Q58.68-17.23 56.38-17.23L56.38-17.23L54.67-17.23L54.67 0L48.64 0ZM67.50-21.91L71.33-21.91L71.33-30.02L77.36-30.02L77.36-21.91L82.94-21.91L82.94-17.23L77.36-17.23L77.36-9.27Q77.36-6.48 77.85-5.76L77.85-5.76Q78.61-4.68 80.55-4.68L80.55-4.68L82.94-4.68L82.94 0L78.57 0Q74.66 0 72.99-1.89Q71.33-3.78 71.33-8.23L71.33-8.23L71.33-17.23L67.50-17.23L67.50-21.91ZM96.08-21.91L101.75-21.91L101.75-30.02L107.73-30.02L107.73 0L97.38 0Q94.23 0 92.61-0.49L92.61-0.49Q88.78-1.71 86.90-5.26L86.90-5.26Q85.59-7.65 85.59-11.12L85.59-11.12Q85.59-16.74 89.37-19.84L89.37-19.84Q91.84-21.91 96.08-21.91L96.08-21.91ZM97.38-4.68L101.75-4.68L101.75-17.23L97.38-17.23Q94.50-17.23 93.02-15.30L93.02-15.30Q91.71-13.68 91.71-11.12L91.71-11.12Q91.71-7.38 93.87-5.76L93.87-5.76Q95.36-4.68 97.38-4.68L97.38-4.68Z"-->
<!-- fill="#2c3e50"-->
<!-- transform="translate(300, 270) scale(4,4)"-->
<!-- ></path>-->
<text x="300" y="100" font-size="50" font-weight="bold">让你的证书永不过期</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,29 +1,32 @@
<template> <template>
<a-config-provider :locale="locale"> <a-config-provider :locale="locale" :theme="settingStore.themeToken">
<router-view v-if="routerEnabled" /> <fs-form-provider>
<router-view v-if="routerEnabled" />
</fs-form-provider>
</a-config-provider> </a-config-provider>
</template> </template>
<script lang="ts"> <script lang="ts">
import zhCN from "ant-design-vue/es/locale/zh_CN"; import zhCN from "ant-design-vue/es/locale/zh_CN";
import enUS from "ant-design-vue/es/locale/en_US"; import enUS from "ant-design-vue/es/locale/en_US";
import { provide, ref, nextTick, getCurrentInstance } from "vue"; import { nextTick, provide, ref } from "vue";
import { usePageStore } from "/src/store/modules/page"; import { usePageStore } from "/src/store/modules/page";
import { useResourceStore } from "/src/store/modules/resource"; import { useResourceStore } from "/src/store/modules/resource";
import { useSettingStore } from "/@/store/modules/settings"; import { useSettingStore } from "/@/store/modules/settings";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import "dayjs/locale/en"; import "dayjs/locale/en";
import dayjs from "dayjs"; import dayjs from "dayjs";
export default { export default {
name: "App", name: "App",
setup(props: any, ctx: any) { setup() {
// //
const routerEnabled = ref(true); const routerEnabled = ref(true);
const locale = ref(zhCN); const locale = ref(zhCN);
async function reload() { async function reload() {
routerEnabled.value = false; // routerEnabled.value = false;
await nextTick(); // await nextTick();
routerEnabled.value = true; // routerEnabled.value = true;
} }
function localeChanged(value: any) { function localeChanged(value: any) {
console.log("locale changed:", value); console.log("locale changed:", value);
@ -49,7 +52,8 @@ export default {
return { return {
routerEnabled, routerEnabled,
locale locale,
settingStore
}; };
} }
}; };

View File

@ -1,3 +1,8 @@
export default { export default {
app: { crud: { i18n: { name: "name", city: "city", status: "status" } } } app: { crud: { i18n: { name: "name", city: "city", status: "status" } } },
fs: {
rowHandle: {
title: "Operation"
}
}
}; };

View File

@ -5,5 +5,10 @@ export default {
logoutTip: "确认", logoutTip: "确认",
logoutMessage: "确定要注销登录吗?" logoutMessage: "确定要注销登录吗?"
} }
},
fs: {
rowHandle: {
title: "操作列"
}
} }
}; };

View File

@ -22,7 +22,7 @@ export default defineComponent({
}, },
{ immediate: true } { immediate: true }
); );
const middle = "/fast-crud/fs-admin-antdv/blob/main/src/views"; const middle = "/fast-crud/fs-admin-antdv4/blob/main/src/views";
function goSource(prefix: any) { function goSource(prefix: any) {
const path = router.currentRoute.value.fullPath; const path = router.currentRoute.value.fullPath;
window.open(prefix + middle + path + "/index.vue"); window.open(prefix + middle + path + "/index.vue");

View File

@ -54,6 +54,7 @@ export default defineComponent({
name: "FsThemeColorPicker", name: "FsThemeColorPicker",
props: { props: {
primaryColor: { primaryColor: {
type: String,
default: "#1890ff" default: "#1890ff"
} }
}, },

View File

@ -2,7 +2,7 @@
<div class="fs-theme" @click="show()"> <div class="fs-theme" @click="show()">
<fs-iconify icon="ion:sparkles-outline" /> <fs-iconify icon="ion:sparkles-outline" />
<a-drawer v-model:open="visible" title="主题设置" placement="right" width="350px" :closable="false" @after-open-change="afterVisibleChange"> <a-drawer v-model:open="visible" title="主题设置" placement="right" width="350px" :closable="false" @after-open-change="afterVisibleChange">
<fs-theme-color-picker :primary-color="setting.getTheme.primaryColor" @change="setting.setPrimaryColor"></fs-theme-color-picker> <fs-theme-color-picker :primary-color="setting.themeConfig?.colorPrimary" @change="setting.setPrimaryColor"></fs-theme-color-picker>
</a-drawer> </a-drawer>
</div> </div>
</template> </template>

View File

@ -0,0 +1,44 @@
<template>
<div class="fs-theme-mode">
<a-switch :checked="setting.themeConfig.mode === 'dark'" @update:checked="onChange">
<template #checkedChildren>
<fs-iconify icon="ion:moon" />
</template>
<template #unCheckedChildren>
<fs-iconify icon="ion:sunny" />
</template>
</a-switch>
</div>
</template>
<script lang="ts">
import { ref, defineComponent } from "vue";
import { useSettingStore } from "/@/store/modules/settings";
export default defineComponent({
name: "FsThemeModeSet",
components: {},
setup() {
const setting = useSettingStore();
const onChange = (checked: boolean) => {
if (checked) {
setting.setDarkMode("dark");
} else {
setting.setDarkMode("light");
}
};
return {
setting,
onChange
};
}
});
</script>
<style lang="less">
.fs-theme-mode {
display: inline-flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -31,9 +31,18 @@
<!-- Button--> <!-- Button-->
<!-- </button>--> <!-- </button>-->
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="headerMenus" /> <fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="headerMenus" />
<fs-locale class="btn" /> <div class="header-btn">
<!-- <fs-theme-set class="btn" />--> <fs-locale />
<fs-user-info class="btn" /> </div>
<!-- <div class="header-btn">-->
<!-- <fs-theme-mode-set />-->
<!-- </div>-->
<div class="header-btn">
<fs-theme-set />
</div>
<div class="header-btn">
<fs-user-info />
</div>
</div> </div>
</a-layout-header> </a-layout-header>
<fs-tabs></fs-tabs> <fs-tabs></fs-tabs>
@ -69,11 +78,12 @@ import { useResourceStore } from "../store/modules/resource";
import { usePageStore } from "/@/store/modules/page"; import { usePageStore } from "/@/store/modules/page";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons-vue"; import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons-vue";
import FsThemeSet from "/@/layout/components/theme/index.vue"; import FsThemeSet from "/@/layout/components/theme/index.vue";
import { notification } from "ant-design-vue"; import { env } from "../utils/util.env";
import FsThemeModeSet from "./components/theme/mode-set.vue";
export default { export default {
name: "LayoutFramework", name: "LayoutFramework",
// eslint-disable-next-line vue/no-unused-components // eslint-disable-next-line vue/no-unused-components
components: { FsThemeSet, MenuFoldOutlined, MenuUnfoldOutlined, FsMenu, FsLocale, FsSourceLink, FsUserInfo, FsTabs }, components: { FsThemeSet, MenuFoldOutlined, MenuUnfoldOutlined, FsMenu, FsLocale, FsSourceLink, FsUserInfo, FsTabs, FsThemeModeSet },
setup() { setup() {
const resourceStore = useResourceStore(); const resourceStore = useResourceStore();
const frameworkMenus = computed(() => { const frameworkMenus = computed(() => {
@ -100,6 +110,8 @@ export default {
return false; return false;
}); });
const version = ref(import.meta.env.VITE_APP_VERSION); const version = ref(import.meta.env.VITE_APP_VERSION);
const envRef = ref(env);
return { return {
version, version,
frameworkMenus, frameworkMenus,
@ -107,7 +119,8 @@ export default {
asideMenus, asideMenus,
keepAlive, keepAlive,
asideCollapsed, asideCollapsed,
asideCollapsedToggle asideCollapsedToggle,
envRef
}; };
} }
}; };
@ -165,11 +178,16 @@ export default {
cursor: pointer; cursor: pointer;
padding: 0 10px; padding: 0 10px;
} }
height: 100%;
& > .btn { & > .header-btn {
display: inline-flex;
justify-content: center;
align-items: center;
height: 100%;
//border-bottom: 1px solid rgba(255, 255, 255, 0);
&:hover { &:hover {
background-color: #fff; background-color: #fff;
color: @primary-color;
} }
} }
} }

View File

@ -19,15 +19,24 @@
<a href="_self">隐私</a> <a href="_self">隐私</a>
<a href="_self">条款</a> <a href="_self">条款</a>
</div> </div>
<div class="copyright">Copyright &copy; 2021 Greper</div> <div class="copyright">{{ envRef.COPYRIGHT }}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { env } from "/@/utils/util.env";
import { ref } from "vue";
export default { export default {
name: "LayoutOutside" name: "LayoutOutside",
setup() {
const envRef = ref(env);
return {
envRef
};
}
}; };
</script> </script>

View File

@ -1,3 +1,10 @@
<template> <template>
<router-view /> <router-view> </router-view>
</template> </template>
<!--<script lang="ts" setup>-->
<!--import { usePageStore } from "/@/store/modules/page";-->
<!--const pageStore = usePageStore();-->
<!--const keepAlive = pageStore.keepAlive;-->
<!--</script>-->

View File

@ -2,14 +2,12 @@ import { createApp } from "vue";
import App from "./App.vue"; import App from "./App.vue";
import router from "./router"; import router from "./router";
import Antd from "ant-design-vue"; import Antd from "ant-design-vue";
import 'ant-design-vue/dist/reset.css';
import "./style/common.less"; import "./style/common.less";
import i18n from "./i18n"; import i18n from "./i18n";
import store from "./store"; import store from "./store";
import components from "./components"; import components from "./components";
import plugin from "./plugin/"; import plugin from "./plugin/";
// 正式项目请删除mock避免影响性能 // 正式项目请删除mock避免影响性能
//import "./mock"; //import "./mock";

View File

@ -27,9 +27,11 @@ let manyStatus = [
{ value: "2", label: "停止", color: "cyan" }, { value: "2", label: "停止", color: "cyan" },
{ value: "0", label: "关闭", color: "red", icon: "ion:radio-button-off" } { value: "0", label: "关闭", color: "red", icon: "ion:radio-button-off" }
]; ];
for (let i = 0; i < 2; i++) { let tempManyStatus: any[] = [];
manyStatus = manyStatus.concat(_.cloneDeep(manyStatus)); for (let i = 0; i < 100; i++) {
tempManyStatus = tempManyStatus.concat(_.cloneDeep(manyStatus));
} }
manyStatus = tempManyStatus;
let idIndex = 0; let idIndex = 0;
for (const item of manyStatus) { for (const item of manyStatus) {
idIndex++; idIndex++;

View File

@ -1,6 +1,7 @@
import { mock } from "../api/service"; import { mock } from "../api/service";
import * as tools from "../api/tools"; import * as tools from "../api/tools";
import _ from "lodash-es"; import _ from "lodash-es";
import { utils } from "@fast-crud/fast-crud";
// @ts-ignore // @ts-ignore
const commonMocks: any = import.meta.glob("./common/mock.*.[j|t]s", { eager: true }); const commonMocks: any = import.meta.glob("./common/mock.*.[j|t]s", { eager: true });
// @ts-ignore // @ts-ignore
@ -22,8 +23,8 @@ _.forEach(viewMocks, (value) => {
list.forEach((apiFile: any) => { list.forEach((apiFile: any) => {
for (const item of apiFile) { for (const item of apiFile) {
mock.onAny(new RegExp(item.path)).reply(async (config: any) => { mock.onAny(new RegExp(item.path)).reply(async (config: any) => {
console.log("------------fake request start -------------"); utils.logger.debug("------------fake request start -------------");
console.log("request:", config); utils.logger.debug("request:", config);
const data = config.data ? JSON.parse(config.data) : {}; const data = config.data ? JSON.parse(config.data) : {};
const query = config.url.indexOf("?") >= 0 ? config.url.substring(config.url.indexOf("?") + 1) : undefined; const query = config.url.indexOf("?") >= 0 ? config.url.substring(config.url.indexOf("?") + 1) : undefined;
const params = config.params || {}; const params = config.params || {};
@ -40,8 +41,8 @@ list.forEach((apiFile: any) => {
params: params params: params
}; };
const ret = await item.handle(req); const ret = await item.handle(req);
console.log("response:", ret); utils.logger.debug("response:", ret);
console.log("------------fake request end-------------"); utils.logger.debug("------------fake request end-------------");
if (ret.code === 0) { if (ret.code === 0) {
return tools.responseSuccess(ret.data, ret.msg); return tools.responseSuccess(ret.data, ret.msg);
} else { } else {

View File

@ -1,9 +1,11 @@
import { request, requestForMock } from "/src/api/service"; import { request, requestForMock } from "/src/api/service";
import { ColumnCompositionProps, CrudOptions, FastCrud, PageQuery, PageRes, setLogger, TransformResProps, useColumns, UseCrudProps, UserPageQuery, useTypes, useUi } from "@fast-crud/fast-crud"; // import "/src/mock";
import { ColumnCompositionProps, CrudOptions, FastCrud, PageQuery, PageRes, setLogger, TransformResProps, useColumns, UseCrudProps, UserPageQuery, useTypes, utils } from "@fast-crud/fast-crud";
import "@fast-crud/fast-crud/dist/style.css"; import "@fast-crud/fast-crud/dist/style.css";
import { FsExtendsCopyable, FsExtendsEditor, FsExtendsJson, FsExtendsTime, FsExtendsUploader, FsUploaderAliossSTS, FsUploaderGetAuthContext, FsUploaderS3SignedUrlType } from "@fast-crud/fast-extends"; import { FsExtendsCopyable, FsExtendsEditor, FsExtendsJson, FsExtendsTime, FsExtendsUploader, FsExtendsInput, FsUploaderS3SignedUrlType, FsUploaderGetAuthContext, FsUploaderAliossSTS } from "@fast-crud/fast-extends";
import "@fast-crud/fast-extends/dist/style.css"; import "@fast-crud/fast-extends/dist/style.css";
import UiAntdv from "@fast-crud/ui-antdv4"; import UiAntdv from "@fast-crud/ui-antdv4";
import "@fast-crud/ui-antdv4/dist/style.css";
import _ from "lodash-es"; import _ from "lodash-es";
import { useCrudPermission } from "../permission"; import { useCrudPermission } from "../permission";
import { App } from "vue"; import { App } from "vue";
@ -28,9 +30,13 @@ function install(app: App, options: any = {}) {
* @param propsuseCrud * @param propsuseCrud
*/ */
commonOptions(props: UseCrudProps): CrudOptions { commonOptions(props: UseCrudProps): CrudOptions {
utils.logger.debug("commonOptions:", props);
const crudBinding = props.crudExpose?.crudBinding; const crudBinding = props.crudExpose?.crudBinding;
const opts: CrudOptions = { const opts: CrudOptions = {
table: { table: {
scroll: {
x: 960
},
size: "small", size: "small",
pagination: false, pagination: false,
onResizeColumn: (w: number, col: any) => { onResizeColumn: (w: number, col: any) => {
@ -41,7 +47,7 @@ function install(app: App, options: any = {}) {
conditionalRender: { conditionalRender: {
match(scope) { match(scope) {
//不能用 !scope.value 否则switch组件设置为关之后就消失了 //不能用 !scope.value 否则switch组件设置为关之后就消失了
const { value, key } = scope; const { value, key, props } = scope;
return !value && key != "_index" && value != false; return !value && key != "_index" && value != false;
}, },
render() { render() {
@ -57,6 +63,7 @@ function install(app: App, options: any = {}) {
rowHandle: { rowHandle: {
buttons: { buttons: {
view: { type: "link", text: null, icon: "ion:eye-outline" }, view: { type: "link", text: null, icon: "ion:eye-outline" },
copy: { show: true, type: "link", text: null, icon: "ion:copy-outline" },
edit: { type: "link", text: null, icon: "ion:create-outline" }, edit: { type: "link", text: null, icon: "ion:create-outline" },
copy: {show:true,type: "link", text: null, icon: "ion:copy-outline"}, copy: {show:true,type: "link", text: null, icon: "ion:copy-outline"},
remove: { type: "link", style: { color: "red" }, text: null, icon: "ion:trash-outline" } remove: { type: "link", style: { color: "red" }, text: null, icon: "ion:trash-outline" }
@ -90,7 +97,7 @@ function install(app: App, options: any = {}) {
if (res.offset % pageSize === 0) { if (res.offset % pageSize === 0) {
currentPage++; currentPage++;
} }
return { currentPage, pageSize, total: res.total, records: res.records }; return { currentPage, pageSize, records: res.records, total: res.total, ...res };
} }
}, },
form: { form: {
@ -111,6 +118,23 @@ function install(app: App, options: any = {}) {
}, },
wrapperCol: { wrapperCol: {
span: null span: null
},
wrapper: {
saveRemind: true
// inner: true,
// innerContainerSelector: "main.fs-framework-content"
}
},
columns: {
createdAt: {
title: "创建时间",
type: "datetime",
form: {
show: false
},
column: {
order: 1000
}
} }
} }
}; };
@ -135,7 +159,7 @@ function install(app: App, options: any = {}) {
region: "ap-guangzhou", region: "ap-guangzhou",
secretId: "", // secretId: "", //
secretKey: "", // 传了secretKey 和secretId 代表使用本地签名模式(不安全,生产环境不推荐) secretKey: "", // 传了secretKey 和secretId 代表使用本地签名模式(不安全,生产环境不推荐)
async getAuthorization(context: FsUploaderGetAuthContext): Promise<FsUploaderAliossSTS> { async getAuthorization(custom: any) {
// 不传secretKey代表使用临时签名模式,此时此参数必传(安全,生产环境推荐) // 不传secretKey代表使用临时签名模式,此时此参数必传(安全,生产环境推荐)
const ret = request({ const ret = request({
url: "http://www.docmirror.cn:7070/api/upload/cos/getAuthorization", url: "http://www.docmirror.cn:7070/api/upload/cos/getAuthorization",
@ -147,7 +171,6 @@ function install(app: App, options: any = {}) {
// TmpSecretKey, // TmpSecretKey,
// XCosSecurityToken, // XCosSecurityToken,
// ExpiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization // ExpiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization
// key //【可选】后台生成的文件key如果不传则用前端自己生成的key
// } // }
return ret; return ret;
}, },
@ -158,19 +181,27 @@ function install(app: App, options: any = {}) {
} }
}, },
alioss: { alioss: {
keepName: true,
domain: "https://d2p-demo.oss-cn-shenzhen.aliyuncs.com", domain: "https://d2p-demo.oss-cn-shenzhen.aliyuncs.com",
bucket: "d2p-plugins", bucket: "d2p-plugins",
region: "oss-cn-shenzhen", region: "oss-cn-shenzhen",
accessKeyId: "", accessKeyId: "",
accessKeySecret: "", accessKeySecret: "",
keepName: true, async getAuthorization(context: FsUploaderGetAuthContext): Promise<FsUploaderAliossSTS> {
async getAuthorization(custom: any, context: any) {
// 不传accessKeySecret代表使用临时签名模式,此时此参数必传(安全,生产环境推荐) // 不传accessKeySecret代表使用临时签名模式,此时此参数必传(安全,生产环境推荐)
const ret = await request({ const ret = await request({
url: "http://www.docmirror.cn:7070/api/upload/alioss/getAuthorization", url: "http://www.docmirror.cn:7070/api/upload/alioss/getAuthorization",
method: "get" method: "get"
}); });
console.log("ret", ret); console.log("ret", ret);
// 返回结构要求如下
// ret.data:{
// TmpSecretId,
// TmpSecretKey,
// XCosSecurityToken,
// ExpiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization
// key //【可选】后台生成的文件key如果不传则用前端自己生成的key
// }
return ret; return ret;
}, },
sdkOpts: { sdkOpts: {
@ -218,7 +249,7 @@ function install(app: App, options: any = {}) {
} }
}, },
//预签名配置,向后端获取上传的预签名连接 //预签名配置,向后端获取上传的预签名连接
async getSignedUrl(bucket: string, key: string, options: any, type: FsUploaderS3SignedUrlType) { async getSignedUrl(bucket: string, key: string, options: any, type: FsUploaderS3SignedUrlType = "put") {
return await GetSignedUrl(bucket, key, type); return await GetSignedUrl(bucket, key, type);
}, },
successHandle(ret: any) { successHandle(ret: any) {
@ -272,11 +303,12 @@ function install(app: App, options: any = {}) {
app.use(FsExtendsJson); app.use(FsExtendsJson);
app.use(FsExtendsTime); app.use(FsExtendsTime);
app.use(FsExtendsCopyable); app.use(FsExtendsCopyable);
app.use(FsExtendsInput);
const { addTypes, getType } = useTypes(); const { addTypes, getType } = useTypes();
//此处演示修改官方字段类型 //此处演示修改官方字段类型
const textType = getType("text"); const textType = getType("text");
textType.search.autoSearchTrigger = "change"; //修改官方的字段类型,设置为文本变化就触发查询 textType.search.autoSearchTrigger = "change"; //修改官方的字段类型,变化就触发 "enter"=回车键触发
// 此处演示自定义字段类型 // 此处演示自定义字段类型
addTypes({ addTypes({

View File

@ -57,6 +57,9 @@ router.afterEach((to: any) => {
NProgress.done(); NProgress.done();
// 多页控制 打开新的页面 // 多页控制 打开新的页面
const pageStore = usePageStore(); const pageStore = usePageStore();
// for (const item of to.matched) {
// pageStore.keepAlivePush(item.name);
// }
pageStore.open(to); pageStore.open(to);
// 更改标题 // 更改标题
site.title(to.meta.title); site.title(to.meta.title);

View File

@ -152,3 +152,4 @@ const routes = [...outsideRoutes, ...frameworkRoutes];
const frameworkMenus = frameworkRet.menus; const frameworkMenus = frameworkRet.menus;
const headerMenus = headerRet.menus; const headerMenus = headerRet.menus;
export { routes, outsideRoutes, frameworkRoutes, frameworkMenus, headerMenus, findMenus, filterMenus }; export { routes, outsideRoutes, frameworkRoutes, frameworkMenus, headerMenus, findMenus, filterMenus };

View File

@ -96,7 +96,22 @@ export const crudResources = [
title: "ResetCrudOptions", title: "ResetCrudOptions",
name: "BasisReset", name: "BasisReset",
path: "/crud/basis/reset", path: "/crud/basis/reset",
component: "/crud/basis/reset/index.vue" component: "/crud/basis/reset/index.vue",
meta: {
cache: true
}
},
{
title: "CrudOptions插件",
name: "BasisPlugin",
path: "/crud/basis/plugin",
component: "/crud/basis/plugin/index.vue"
},
{
title: "Ts定义测试",
name: "BasisTsTest",
path: "/crud/basis/ts",
component: "/crud/basis/ts/index.vue"
} }
] ]
}, },
@ -193,7 +208,7 @@ export const crudResources = [
component: "/crud/component/select/index.vue" component: "/crud/component/select/index.vue"
}, },
{ {
title: "表格选择(table-select)", title: " 表格选择(table-select)",
name: "ComponentTableSelect", name: "ComponentTableSelect",
path: "/crud/component/table-select", path: "/crud/component/table-select",
component: "/crud/component/table-select/index.vue" component: "/crud/component/table-select/index.vue"
@ -300,6 +315,12 @@ export const crudResources = [
path: "/crud/component/json", path: "/crud/component/json",
component: "/crud/component/json/index.vue" component: "/crud/component/json/index.vue"
}, },
{
title: "手机号输入框",
name: "ComponentPhone",
path: "/crud/component/phone",
component: "/crud/component/phone/index.vue"
},
{ {
title: "组件独立使用", title: "组件独立使用",
name: "ComponentIndependent", name: "ComponentIndependent",
@ -449,6 +470,12 @@ export const crudResources = [
path: "/crud/form/view", path: "/crud/form/view",
component: "/crud/form/view/index.vue" component: "/crud/form/view/index.vue"
}, },
{
title: "initialForm",
name: "FormInitial",
path: "/crud/form/initial",
component: "/crud/form/initial/index.vue"
},
{ {
title: "表单Watch", title: "表单Watch",
name: "FormWatch", name: "FormWatch",

View File

@ -1,50 +1,49 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { theme } from "ant-design-vue";
import _ from "lodash-es";
// @ts-ignore // @ts-ignore
import { LocalStorage } from "/src/utils/util.storage"; import { LocalStorage } from "/src/utils/util.storage";
import { SysPublicSetting } from "/@/api/modules/api.basic"; import { SysPublicSetting } from "/@/api/modules/api.basic";
import * as basicApi from '/@/api/modules/api.basic' import * as basicApi from '/@/api/modules/api.basic'
import _ from "lodash-es"; import _ from "lodash-es";
// import { replaceStyleVariables } from "vite-plugin-theme/es/client";
// import { getThemeColors, generateColors } from "/src/../build/theme-colors"; export type ThemeToken = {
// token: {
// import { mixLighten, mixDarken, tinycolor } from "vite-plugin-theme/es/colorUtils"; colorPrimary?: string;
};
// export async function changeTheme(color?: string) { algorithm: any;
// if (color == null) { };
// return; export type ThemeConfig = {
// } colorPrimary: string;
// const colors = generateColors({ mode: string;
// mixDarken, };
// mixLighten, export interface SettingState {
// tinycolor, themeConfig?: ThemeConfig;
// color themeToken: ThemeToken;
// });
//
// return await replaceStyleVariables({
// colorVariables: [...getThemeColors(color), ...colors]
// });
// }
interface SettingState {
theme: any;
sysPublic?: SysPublicSetting sysPublic?: SysPublicSetting
} }
const defaultThemeConfig = {
colorPrimary: "#1890ff",
mode: "light"
};
const SETTING_THEME_KEY = "SETTING_THEME"; const SETTING_THEME_KEY = "SETTING_THEME";
export const useSettingStore = defineStore({ export const useSettingStore = defineStore({
id: "app.setting", id: "app.setting",
state: (): SettingState => ({ state: (): SettingState => ({
// user info themeConfig: null,
theme: null, themeToken: {
token: {},
algorithm: theme.defaultAlgorithm
},
sysPublic: { sysPublic: {
registerEnabled: false registerEnabled: false
} }
}), }),
getters: { getters: {
getTheme(): any { getThemeConfig(): any {
return this.theme || LocalStorage.get(SETTING_THEME_KEY) || {}; return this.themeConfig || _.merge({}, defaultThemeConfig, LocalStorage.get(SETTING_THEME_KEY) || {});
}, },
getSysPublic():SysPublicSetting{ getSysPublic():SysPublicSetting{
return this.sysPublic return this.sysPublic
@ -55,24 +54,44 @@ export const useSettingStore = defineStore({
const settings = await basicApi.getSysPublicSettings() const settings = await basicApi.getSysPublicSettings()
_.merge(this.sysPublic,settings) _.merge(this.sysPublic,settings)
}, },
persistTheme() { persistThemeConfig() {
LocalStorage.set(SETTING_THEME_KEY, this.getTheme); LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig);
}, },
async setTheme(theme?: Object) { async setThemeConfig(themeConfig?: ThemeConfig) {
if (theme == null) { this.themeConfig = _.merge({}, this.themeConfig, themeConfig);
theme = this.getTheme;
this.persistThemeConfig();
this.setPrimaryColor(this.themeConfig.colorPrimary);
this.setDarkMode(this.themeConfig.mode);
},
setPrimaryColor(color: any) {
this.themeConfig.colorPrimary = color;
_.set(this.themeToken, "token.colorPrimary", color);
this.persistThemeConfig();
},
setDarkMode(mode: string) {
this.themeConfig.mode = mode;
if (mode === "dark") {
this.themeToken.algorithm = theme.darkAlgorithm;
// const defaultSeed = theme.defaultSeed;
// const mapToken = theme.darkAlgorithm(defaultSeed);
// less.modifyVars(mapToken);
// less.modifyVars({
// "@colorPrimaryBg": "#111a2c",
// colorPrimaryBg: "#111a2c"
// });
// less.refreshStyles();
} else {
this.themeToken.algorithm = theme.defaultAlgorithm;
// const defaultSeed = theme.defaultSeed;
// const mapToken = theme.defaultAlgorithm(defaultSeed);
// less.modifyVars(mapToken);
} }
this.theme = theme; this.persistThemeConfig();
this.persistTheme();
// await changeTheme(this.theme.primaryColor);
},
async setPrimaryColor(color: any) {
const theme = this.theme;
theme.primaryColor = color;
await this.setTheme();
}, },
async init() { async init() {
await this.setTheme(this.getTheme); await this.setThemeConfig(this.getThemeConfig);
await this.loadSysSettings() await this.loadSysSettings()
} }
} }

View File

@ -108,8 +108,3 @@ export const useUserStore = defineStore({
} }
} }
}); });
// Need to be used outside the setup
export function useUserStoreWidthOut() {
return useUserStore(store);
}

View File

@ -14,6 +14,9 @@ html, body {
box-sizing: border-box; box-sizing: border-box;
} }
body{
min-width: 1000px;
}
div#app { div#app {
height: 100% height: 100%
} }

View File

@ -1,21 +1,21 @@
.ant-layout{ .ant-layout{
background-color: @bg-color; background-color: @colorPrimaryBg;
} }
.ant-layout-header { .ant-layout-header {
background-color: @bg-color background-color: @colorPrimaryBg
} }
.ant-layout-sider { .ant-layout-sider {
background-color:@bg-color background-color:@colorPrimaryBg
} }
.ant-menu{ .ant-menu{
background-color: @bg-color; background-color: @colorPrimaryBg;
&.ant-menu-submenu-popup{ &.ant-menu-submenu-popup{
background-color: transparent; background-color: transparent;
} }
} }
.aside-menu{ .aside-menu{
.ant-menu-submenu > .ant-menu{ .ant-menu-submenu > .ant-menu{
background-color:@bg-color background-color:@colorPrimaryBg
} }
.ant-menu-item-active{ .ant-menu-item-active{

View File

@ -1,4 +1,3 @@
@primary-color: #1890ff;
// theme
@bg-color: #ebf1f6;
@bg-menu-item-color:hsla(0,0%,100%,.5); @bg-menu-item-color:hsla(0,0%,100%,.5);
@colorPrimaryBg:#ebf1f6;
@primary-color: #1890ff;

View File

@ -10,6 +10,9 @@ export class EnvConfig {
MODE: string; MODE: string;
STORAGE: string; STORAGE: string;
TITLE: string; TITLE: string;
SLOGAN: string;
COPYRIGHT: string;
LOGO_PATH: string;
PM_ENABLED: string; PM_ENABLED: string;
constructor() { constructor() {
this.init(); this.init();

View File

@ -1,5 +1,15 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes, dict } from "@fast-crud/fast-crud"; import {
AddReq,
CreateCrudOptionsProps,
CreateCrudOptionsRet,
DelReq,
EditReq,
UserPageQuery,
UserPageRes,
dict,
utils
} from "@fast-crud/fast-crud";
import { SearchOutlined } from "@ant-design/icons-vue"; import { SearchOutlined } from "@ant-design/icons-vue";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -78,7 +88,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
title: "复杂输入", title: "复杂输入",
component: { component: {
render(context: any) { render(context: any) {
console.log("context scope", context); utils.logger.info("context scope", context);
return ( return (
<a-input-group compact> <a-input-group compact>
<a-input placeholder={"render1 input"} style="width: 50%" v-model={[context.form.render, "value"]} /> <a-input placeholder={"render1 input"} style="width: 50%" v-model={[context.form.render, "value"]} />

View File

@ -44,7 +44,14 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
title: "省", title: "省",
type: "dict-select", type: "dict-select",
search: { search: {
show: true show: true,
valueChange({ form, value, getComponentRef }: any) {
form.city = undefined; // 将“city”的值置空
form.county = undefined; // 将“county”的值置空
if (value) {
getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法触发“city”重新加载字典
}
}
}, },
dict: dict({ dict: dict({
url: "/mock/linkage/province", url: "/mock/linkage/province",
@ -53,8 +60,8 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}), }),
form: { form: {
valueChange({ form, value, getComponentRef }) { valueChange({ form, value, getComponentRef }) {
form.city = undefined; // 将“city”的值置空 form.city = ""; // 将“city”的值置空
form.county = undefined; // 将“county”的值置空 form.county = ""; // 将“county”的值置空
if (value) { if (value) {
getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法触发“city”重新加载字典 getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法触发“city”重新加载字典
} }
@ -84,7 +91,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
// 注释同上 // 注释同上
valueChange({ value, form, getComponentRef }: ScopeContext) { valueChange({ value, form, getComponentRef }: ScopeContext) {
if (value) { if (value) {
form.county = undefined; // 将county的value置空 form.county = ""; // 将county的value置空
const countySelect = getComponentRef("county"); const countySelect = getComponentRef("county");
if (form && form.province && form.city) { if (form && form.province && form.city) {
countySelect.reloadDict(); // 重新加载字典项 countySelect.reloadDict(); // 重新加载字典项

View File

@ -96,6 +96,13 @@ mock.push({
const a = tree.filter((item) => { const a = tree.filter((item) => {
return item.id === province; return item.id === province;
}); });
if (a == null || a.length === 0) {
return {
code: 0,
msg: "success",
data: []
};
}
const list = omitChildren(a[0].children); const list = omitChildren(a[0].children);
return { return {
code: 0, code: 0,
@ -113,10 +120,24 @@ mock.push({
const a = tree.filter((item) => { const a = tree.filter((item) => {
return item.id === province; return item.id === province;
}); });
if (a == null || a.length === 0) {
return {
code: 0,
msg: "success",
data: []
};
}
const city = parseInt(req.params.city); const city = parseInt(req.params.city);
const b = a[0].children.filter((item) => { const b = a[0].children.filter((item) => {
return item.id === city; return item.id === city;
}); });
if (b == null || b.length === 0) {
return {
code: 0,
msg: "success",
data: []
};
}
const list = omitChildren(b[0].children); const list = omitChildren(b[0].children);
return { return {

View File

@ -51,11 +51,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}; };
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
const id = await api.AddObj(form); const { id } = await api.AddObj(form);
//本地添加 //本地添加
form.id = id; form.id = id;
localDataRef.value.unshift(form); localDataRef.value.unshift(form);
return id; return form;
}; };
const delRequest = async ({ row }: DelReq) => { const delRequest = async ({ row }: DelReq) => {

View File

@ -25,6 +25,7 @@ export default function ({ crudExpose, context: { asideTableRef } }: CreateCrudO
const onCurrentRowChange = (id: number) => { const onCurrentRowChange = (id: number) => {
currentRow.value = id; currentRow.value = id;
asideTableRef.value.crudBinding.search.initialForm = { gradeId: id };
asideTableRef.value.setSearchFormData({ form: { gradeId: id } }); asideTableRef.value.setSearchFormData({ form: { gradeId: id } });
asideTableRef.value.doRefresh(); asideTableRef.value.doRefresh();
}; };
@ -90,7 +91,8 @@ export default function ({ crudExpose, context: { asideTableRef } }: CreateCrudO
// 嵌套表格字段 // 嵌套表格字段
rules: [{ required: true, message: "请选择用户" }], rules: [{ required: true, message: "请选择用户" }],
component: { component: {
name: SubTable, //局部引用子表格要用shallowRef包裹
name: shallowRef(SubTable),
vModel: "modelValue", vModel: "modelValue",
gradeId: compute(({ form }) => { gradeId: compute(({ form }) => {
return form.id; return form.id;

View File

@ -10,7 +10,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, watch } from "vue"; import { defineComponent, onMounted, watch } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud"; import {useFs, utils} from "@fast-crud/fast-crud";
export default defineComponent({ export default defineComponent({
name: "SubTable", name: "SubTable",
@ -33,7 +33,7 @@ export default defineComponent({
return props.modelValue; return props.modelValue;
}, },
(value) => { (value) => {
console.log("modelValue changed", value); utils.logger.info("modelValue changed", value);
} }
); );
return { return {

View File

@ -1,6 +1,7 @@
import * as api from "./api.js"; import * as api from "./api.js";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { computed } from "vue";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose; const { crudBinding } = crudExpose;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -29,7 +30,10 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
toolbar: { toolbar: {
columnsFilter: { columnsFilter: {
mode: "default" mode: "default",
container: {
width: "500px"
}
} }
}, },
actionbar: { actionbar: {
@ -78,7 +82,12 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "dict-radio", type: "dict-radio",
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}) }),
column: {
show: computed(() => {
return true;
})
}
}, },
disabled: { disabled: {
title: "列设置禁用", title: "列设置禁用",

View File

@ -1,6 +1,7 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { computed, ref } from "vue"; import { computed, ref, shallowRef } from "vue";
import ShallowComponent from "/@/views/crud/basis/compute-more/shallow-component.vue";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -22,13 +23,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
//普通的ref引用可以动态切换配置 //普通的ref引用可以动态切换配置
const defValueRef = ref("我是动态的默认值"); const defValueRef = ref("我是动态的默认值");
const defValueComputed = computed(() => {
return defValueRef.value;
});
return { return {
output: { output: {
defValueRef, defValueRef
defValueComputed
}, },
crudOptions: { crudOptions: {
request: { request: {
@ -67,11 +64,39 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "text", type: "text",
search: { show: true, value: null }, search: { show: true, value: null },
form: { form: {
// form.value不支持asyncCompute // form.value不支持asyncCompute/Compute, 因为上下文动态计算要先有上下文上下文需要先有form数据
// 假如你的默认值异步获取的,那么你自己必须保证先异步计算完成之后,才能打开对话框。
// 因为在打开对话框时,默认值就必须得设置好。
value: defValueRef value: defValueRef
} }
},
switch: {
title: "切换动态组件",
type: "dict-radio",
dict: dict({
data: [
{ value: "radio", label: "radio" },
{ value: "select", label: "select" },
{ value: "shallow", label: "shallowComponent" }
]
})
},
componentName: {
title: "动态组件",
type: "dict-select",
search: { show: true, value: null },
dict: dict({
data: [
{ value: "1", label: "开启" },
{ value: "2", label: "关闭" }
]
}),
form: {
component: {
value: "2",
name: compute(({ form }) => {
return form.switch === "select" ? "fs-dict-select" : form.switch === "radio" ? "fs-dict-radio" : ShallowComponent;
})
}
}
} }
} }
} }

View File

@ -0,0 +1,14 @@
<template>
<component :is="ui.tag.name" type="red">value : {{ modelValue || value }}</component>
</template>
<script setup lang="ts">
import { useUi } from "@fast-crud/ui-interface";
type ScProps = {
modelValue?: string;
value?: string;
};
const props = defineProps<ScProps>();
const { ui } = useUi();
</script>

View File

@ -33,6 +33,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
editRequest, editRequest,
delRequest delRequest
}, },
toolbar: {
compact: false
},
table: { table: {
scroll: { scroll: {
x: 1500 x: 1500
@ -184,6 +187,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
search: { show: false }, search: { show: false },
type: "text", type: "text",
column: { column: {
order: 1000,
fixed: "right", fixed: "right",
component: { component: {
name: "a-switch", name: "a-switch",

View File

@ -48,7 +48,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
column: { column: {
component: { component: {
//引用自定义组件 //引用自定义组件
name: shallowRef(VmodelCounter), name: VmodelCounter,
color: "blue", color: "blue",
slots: { slots: {
//插槽示例 //插槽示例
@ -68,7 +68,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
//form表单 //form表单
component: { component: {
//引用自定义组件 //引用自定义组件
name: shallowRef(VmodelCounter), name: VmodelCounter,
vModel: "modelValue", vModel: "modelValue",
color: "red", color: "red",
on: { on: {

View File

@ -1,7 +1,17 @@
import { requestForMock } from "/src/api/service"; import { requestForMock } from "/src/api/service";
const request = requestForMock; const request = requestForMock;
const apiPrefix = "/mock/FsCrudFirst"; const apiPrefix = "/mock/FsCrudFirst";
export function GetList(query: any) {
/**
*
*/
export type FirstRow = {
id?: number;
name?: string;
type?: number;
};
export function GetList(query: FirstRow) {
return request({ return request({
url: apiPrefix + "/page", url: apiPrefix + "/page",
method: "get", method: "get",
@ -9,7 +19,7 @@ export function GetList(query: any) {
}); });
} }
export function AddObj(obj: any) { export function AddObj(obj: FirstRow) {
return request({ return request({
url: apiPrefix + "/add", url: apiPrefix + "/add",
method: "post", method: "post",
@ -17,7 +27,7 @@ export function AddObj(obj: any) {
}); });
} }
export function UpdateObj(obj: any) { export function UpdateObj(obj: FirstRow) {
return request({ return request({
url: apiPrefix + "/update", url: apiPrefix + "/update",
method: "post", method: "post",

View File

@ -1,31 +1,34 @@
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery } from "@fast-crud/fast-crud";
import * as api from "./api"; import * as api from "./api";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { import { FirstRow } from "./api";
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
if (form.id == null) {
form.id = row.id;
}
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
/**
* context
*/
export type FirstContext = {
test?: number;
};
export default function ({ crudExpose, context }: CreateCrudOptionsProps<FirstRow, FirstContext>): CreateCrudOptionsRet<FirstRow> {
context.test = 111;
return { return {
crudOptions: { crudOptions: {
// 自定义crudOptions配置 // 自定义crudOptions配置
request: { request: {
pageRequest, pageRequest: async (query: UserPageQuery<FirstRow>) => {
addRequest, return await api.GetList(query);
editRequest, },
delRequest addRequest: async ({ form }: AddReq) => {
return await api.AddObj(form);
},
editRequest: async ({ form, row }: EditReq) => {
if (form.id == null) {
form.id = row.id;
}
return await api.UpdateObj(form);
},
delRequest: async ({ row }: DelReq) => {
return await api.DelObj(row.id);
}
}, },
//两个字段 //两个字段
columns: { columns: {

View File

@ -1,5 +1,5 @@
<template> <template>
<fs-page> <fs-page class="page-first">
<template #header> <template #header>
<div class="title">第一个crud</div> <div class="title">第一个crud</div>
<div class="more"> <div class="more">
@ -7,13 +7,17 @@
</div> </div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding" /> <fs-crud ref="crudRef" v-bind="crudBinding" />
<a-tour v-model:current="current" :open="open" :steps="steps" @close="handleOpen(false)" />
</fs-page> </fs-page>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted, ref } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs, utils } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions, { FirstContext } from "./crud";
import { TourProps } from "ant-design-vue";
import { FirstRow } from "./api";
// //
export default defineComponent({ export default defineComponent({
@ -32,15 +36,67 @@ export default defineComponent({
// =======fs========= // =======fs=========
// =============== // ===============
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: {} }); const { crudRef, crudBinding, crudExpose, context } = useFs<FirstRow, FirstContext>({ createCrudOptions, context: {} });
utils.logger.info("test", context.test);
function useTour() {
const open = ref<boolean>(false);
const current = ref(0);
//
const steps: TourProps["steps"] = [
{
title: "查询",
description: "查询数据.",
target: () => {
return document.querySelector(".page-first .fs-search-btn-search") as HTMLElement;
}
},
{
title: "重置",
description: "重置查询条件.",
target: () => {
return document.querySelector(".page-first .fs-search-btn-reset") as HTMLElement;
}
},
{
title: "添加",
description: "打开添加对话框",
target: () => {
return document.querySelector(".page-first .fs-actionbar-btn-add") as HTMLElement;
}
},
{
title: "刷新列表",
description: "刷新列表",
target: () => {
return document.querySelector(".page-first .fs-toolbar-btn-refresh") as HTMLElement;
}
}
];
const handleOpen = (val: boolean): void => {
open.value = val;
};
return {
open,
current,
steps,
handleOpen
};
}
const tour = useTour();
// //
onMounted(() => { onMounted(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
tour.handleOpen(true);
}); });
return { return {
crudBinding, crudBinding,
crudRef crudRef,
...tour
}; };
} }
}); });

View File

@ -18,10 +18,10 @@ import _ from "lodash-es";
//crudOptions //crudOptions
const createCrudOptions = function ({}: CreateCrudOptionsProps): CreateCrudOptionsRet { const createCrudOptions = function ({}: CreateCrudOptionsProps): CreateCrudOptionsRet {
//crud ---- //crud ----
const records = reactive([{ id: 1, name: "Hello World", type: 1 }]); const records = [{ id: 1, name: "Hello World", type: 1 }];
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return { return {
records, records: _.cloneDeep(records),
offset: 0, //transformRescurrentPage offset: 0, //transformRescurrentPage
limit: 20, //transformRespageSize limit: 20, //transformRespageSize
total: records.length total: records.length

View File

@ -100,6 +100,26 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}) })
},
text: {
title: "text",
type: "text",
search: { show: true }
},
text2: {
title: "text2",
type: "text",
search: { show: true }
},
text3: {
title: "text3",
type: "text",
search: { show: true }
},
text4: {
title: "text4",
type: "text",
search: { show: true }
} }
} }
} }

View File

@ -6,7 +6,11 @@
<a target="_blank" href="http://fast-crud.docmirror.cn/guide/advance/layout.html">文档</a> <a target="_blank" href="http://fast-crud.docmirror.cn/guide/advance/layout.html">文档</a>
</div> </div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> <fs-crud ref="crudRef" v-bind="crudBinding">
<template #actionbar-right>
<a-button type="danger">actionbar-right插槽</a-button>
</template>
</fs-crud>
</fs-page> </fs-page>
</template> </template>

View File

@ -0,0 +1,50 @@
import { requestForMock } from "/src/api/service";
const request = requestForMock;
const apiPrefix = "/mock/BasisPlugin";
export function GetList(query: any) {
return request({
url: apiPrefix + "/page",
method: "get",
data: query
});
}
export function AddObj(obj: any) {
return request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
export function UpdateObj(obj: any) {
return request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
export function GetObj(id: any) {
return request({
url: apiPrefix + "/get",
method: "get",
params: { id }
});
}
export function BatchDelete(ids: any) {
return request({
url: apiPrefix + "/batchDelete",
method: "post",
data: { ids }
});
}

View File

@ -0,0 +1,72 @@
import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { ref } from "vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
if (form.id == null) {
form.id = row.id;
}
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
return {
crudOptions: {
settings: {
plugins: {
//这里使用行选择插件生成行选择crudOptions配置最终会与crudOptions合并
rowSelection: {
enabled: true,
order: -2,
before: true,
// handle: (pluginProps,useCrudProps)=>CrudOptions,
props: {
multiple: true,
crossPage: true,
selectedRowKeys,
onSelectedChanged(selected) {
utils.logger.info("已选择变化:", selected);
}
}
}
}
},
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
columns: {
id: {
title: "ID",
type: "number",
column: {
width: 50
},
form: {
show: false
}
},
text: {
title: "Text",
type: "text",
search: { show: true }
}
}
}
};
}

View File

@ -0,0 +1,63 @@
<template>
<fs-page>
<template #header>
<div class="title">
CrudOptionsPlugin
<span class="sub">用于合并CrudOptions做一些可配置化的公共参数此处演示使用rowSelectionPlugin生成行选择配置,支持跨页选择</span>
</div>
<div class="more">
<a target="_blank" href="http://fast-crud.docmirror.cn/api/crud-options/settings.html#plugins">文档</a>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
<a-tooltip title="批量删除">
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
</a-tooltip>
</template>
</fs-crud>
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted } from "vue";
import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud";
import { message, Modal } from "ant-design-vue";
import { BatchDelete } from "./api";
export default defineComponent({
name: "BasisPlugin",
setup() {
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
const selectedRowKeys = context.selectedRowKeys;
//
onMounted(() => {
crudExpose.doRefresh();
});
const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) {
Modal.confirm({
title: "确认",
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
async onOk() {
await BatchDelete(selectedRowKeys.value);
message.info("删除成功");
crudExpose.doRefresh();
selectedRowKeys.value = [];
}
});
} else {
message.error("请先勾选记录");
}
};
return {
crudBinding,
crudRef,
handleBatchDelete
};
}
});
</script>

View File

@ -0,0 +1,22 @@
import mockUtil from "/src/mock/base";
const options: any = {
name: "BasisPlugin",
idGenerator: 0
};
const list = [
{
text: "张三",
radio: "1"
},
{
text: "李四",
radio: "2"
},
{
text: "王五",
radio: "0"
}
];
options.list = list;
const mock = mockUtil.buildMock(options);
export default mock;

View File

@ -1,6 +1,6 @@
import { requestForMock } from "/src/api/service"; import { requestForMock } from "/src/api/service";
const request = requestForMock; const request = requestForMock;
const apiPrefix = "/mock/BasisReset"; const apiPrefix = "/mock/BasisValueChange";
export function GetList(query: any) { export function GetList(query: any) {
return request({ return request({
url: apiPrefix + "/page", url: apiPrefix + "/page",

View File

@ -15,14 +15,13 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted } from "vue";
import { CrudOptions, DynamicType, useFs } from "@fast-crud/fast-crud"; import { CrudOptions, DynamicType, useFs, UseFsProps } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud.js"; import createCrudOptions from "./crud.js";
import _ from "lodash-es"; import _ from "lodash-es";
export default defineComponent({ export default defineComponent({
name: "BasisReset", name: "BasisReset",
setup() { setup() {
const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions, appendBindingOptions } = useFs({ createCrudOptions, context: {} }); const { crudBinding, crudRef, crudExpose, context, crudOptions, resetCrudOptions, appendBindingOptions } = useFs({ createCrudOptions, context: { text: 111 } });
setTimeout(() => { setTimeout(() => {
//crudOptions //crudOptions

View File

@ -1,7 +1,7 @@
// @ts-ignore // @ts-ignore
import mockUtil from "/src/mock/base"; import mockUtil from "/src/mock/base";
const options: any = { const options: any = {
name: "BasisReset", name: "BasisValueChange",
idGenerator: 0 idGenerator: 0
}; };
const list = [ const list = [

View File

@ -0,0 +1,54 @@
//@ts-ignore
import { requestForMock } from "/src/api/service";
const request = requestForMock;
const apiPrefix = "/mock/FsCrudFirst";
/**
*
*/
export type TsTestRow = {
id?: number;
name?: string;
type?: number;
compute?: string;
};
export function GetList(query: TsTestRow) {
return request({
url: apiPrefix + "/page",
method: "get",
data: query
});
}
export function AddObj(obj: TsTestRow) {
return request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
export function UpdateObj(obj: TsTestRow) {
return request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
export function GetObj(id: any) {
return request({
url: apiPrefix + "/get",
method: "get",
params: { id }
});
}

View File

@ -0,0 +1,79 @@
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery } from "@fast-crud/fast-crud";
import * as api from "./api";
import { TsTestRow } from "./api";
/**
* context
*/
export type TsTestContext = {
test?: number;
};
export default function ({ crudExpose, context }: CreateCrudOptionsProps<TsTestRow, TsTestContext>): CreateCrudOptionsRet<TsTestRow> {
context.test = 111;
return {
crudOptions: {
// 自定义crudOptions配置
columns: {
name: {
title: "姓名",
type: "text",
search: { show: true },
column: {
resizable: true,
width: 200
}
},
type: {
title: "类型",
type: "dict-select",
dict: dict({
data: [
{ value: 1, label: "开始", color: "green" },
{ value: 0, label: "停止", color: "red" }
]
}),
valueResolve: ({ form }) => {
console.log("valueResolve", form.type);
},
valueBuilder: ({ row }) => {
console.log("valueBuilder", row.type);
}
},
compute: {
title: "compute",
type: "text",
form: {
component: {
show: compute(({ form }) => {
//自动带form ts提示
return form.type === 1;
}),
disabled: compute<boolean, TsTestRow>(({ form }) => {
//disabled属性 不在component配置的定义中所有不带row、form的ts提示, 需要手动指定类型
return form.type === 1;
})
}
}
}
},
//两个字段
request: {
pageRequest: async (query: UserPageQuery) => {
return await api.GetList(query);
},
addRequest: async ({ form }: AddReq) => {
return await api.AddObj(form);
},
editRequest: async ({ form, row }: EditReq) => {
if (form.id == null) {
form.id = row.id;
}
return await api.UpdateObj(form);
},
delRequest: async ({ row }: DelReq) => {
return await api.DelObj(row.id);
}
}
}
};
}

View File

@ -0,0 +1,47 @@
<template>
<fs-page class="page-first">
<template #header>
<div class="title">Ts定义测试</div>
<div class="more"></div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import { useFs, utils } from "@fast-crud/fast-crud";
import createCrudOptions, { TsTestContext } from "./crud";
import { TsTestRow } from "./api";
//
export default defineComponent({
name: "FsCrudTsTest",
setup() {
// // crudref
// const crudRef: Ref = ref();
// // crud ref
// const crudBinding: Ref<CrudBinding> = ref();
// //
// const { crudExpose } = useExpose({ crudRef, crudBinding });
// // crud
// const { crudOptions, customExport } = createCrudOptions({ crudExpose, customValue });
// // crud
// const { resetCrudOptions, appendCrudBinding } = useCrud({ crudExpose, crudOptions });
// =======fs=========
// ===============
const { crudRef, crudBinding, crudExpose, context } = useFs<TsTestRow, TsTestContext>({ createCrudOptions, context: {} });
utils.logger.info("test", context.test);
//
onMounted(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>

View File

@ -0,0 +1,22 @@
// @ts-ignore
import mockUtil from "/src/mock/base";
const options: any = {
name: "FsCrudFirst",
idGenerator: 0
};
const list = [
{
name: "张三",
type: 1
},
{
name: "李四",
type: 0
},
{
name: "王五"
}
];
options.list = list;
const mock = mockUtil.buildMock(options);
export default mock;

View File

@ -1,6 +1,17 @@
import * as api from "./api"; import * as api from "./api";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, ValueChangeContext } from "@fast-crud/fast-crud"; import {
AddReq,
CreateCrudOptionsProps,
CreateCrudOptionsRet,
DelReq,
dict,
EditReq,
UserPageQuery,
UserPageRes,
utils,
ValueChangeContext
} from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -55,12 +66,12 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
vModel: "checked" vModel: "checked"
}, },
valueChange(context: ValueChangeContext) { valueChange(context: ValueChangeContext) {
console.log("column value changed:", context); utils.logger.info("column value changed:", context);
} }
}, },
form: { form: {
valueChange({ value, key, form }: ValueChangeContext) { valueChange({ value, key, form }: ValueChangeContext) {
console.log("valueChanged,", key, value, form); utils.logger.info("valueChanged,", key, value, form);
message.info(`valueChanged:${key}=${value}`); message.info(`valueChanged:${key}=${value}`);
} }
} }
@ -70,7 +81,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "text", type: "text",
form: { form: {
valueChange({ value, key, form }: ValueChangeContext) { valueChange({ value, key, form }: ValueChangeContext) {
console.log("valueChanged,", key, value, form); utils.logger.info("valueChanged,", key, value, form);
message.info(`valueChanged:${key}=${value}`); message.info(`valueChanged:${key}=${value}`);
} }
} }
@ -84,7 +95,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
form: { form: {
valueChange: { valueChange: {
handle({ value, key, form, immediate }: ValueChangeContext) { handle({ value, key, form, immediate }: ValueChangeContext) {
console.log("valueChange,", key, value, "isImmediate=", immediate); utils.logger.info("valueChange,", key, value, "isImmediate=", immediate);
message.info(`valueChanged:${key}=${value},isImmediate=${immediate}`); message.info(`valueChanged:${key}=${value},isImmediate=${immediate}`);
}, },
immediate: true immediate: true

View File

@ -50,6 +50,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "button", type: "button",
column: { column: {
component: { component: {
icon: "ion:search",
show: compute(({ value }) => { show: compute(({ value }) => {
//当value为null时不显示 //当value为null时不显示
return value != null; return value != null;

View File

@ -1,5 +1,6 @@
<template> <template>
<fs-page> <fs-page>
<div class="title">按钮</div>
<fs-crud ref="crudRef" v-bind="crudBinding" /> <fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page> </fs-page>
</template> </template>

View File

@ -1,6 +1,6 @@
import * as api from "./api"; import * as api from "./api";
import { requestForMock } from "/src/api/service"; import { requestForMock } from "/src/api/service";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils, useUi } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -19,6 +19,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form); return await api.AddObj(form);
}; };
const { ui } = useUi();
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -52,7 +53,18 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
isTree: true, isTree: true,
url: "/mock/dicts/cascaderData?single" url: "/mock/dicts/cascaderData?single"
}) }),
form: {
component: {
on: {
selectedChange({ $event }) {
utils.logger.info("onSelectedChange", $event);
const labels = $event.map((item) => item.label);
ui.message.info(`selected-change:${JSON.stringify(labels)}`);
}
}
}
}
}, },
lazyLoad: { lazyLoad: {
title: "懒加载", title: "懒加载",
@ -65,9 +77,6 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
prototype: true, prototype: true,
getNodesByValues(values: any) { getNodesByValues(values: any) {
//给cell展示组件调用根据value值获取节点每行都会请求一次 //给cell展示组件调用根据value值获取节点每行都会请求一次
if (values == null) {
return [];
}
return requestForMock({ return requestForMock({
url: "/mock/tree/GetNodesByValues", url: "/mock/tree/GetNodesByValues",
params: { values } params: { values }
@ -90,7 +99,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
} }
], ],
loadData: async (selectedOptions: any) => { loadData: async (selectedOptions: any) => {
console.log("lazyLoad", selectedOptions); utils.logger.info("lazyLoad", selectedOptions);
const targetOption = selectedOptions[selectedOptions.length - 1]; const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true; targetOption.loading = true;

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils, useUi } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -18,6 +18,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form); return await api.AddObj(form);
}; };
const { ui } = useUi();
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -44,7 +45,20 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "dict-checkbox", type: "dict-checkbox",
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}) }),
form: {
component: {
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
ui.message.info(`你选择了${JSON.stringify($event)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
}
}
}
} }
} }
} }

View File

@ -2,7 +2,6 @@ import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, ScopeContext, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, ScopeContext, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import dayjs from "dayjs"; import dayjs from "dayjs";
console.log("utils", utils);
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -33,6 +32,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
scroll: { x: 3000 } scroll: { x: 3000 }
}, },
rowHandle: { fixed: "right" }, rowHandle: { fixed: "right" },
search: {
initialForm: {
// datetimerange: [dayjs().subtract(1, "month").startOf("day"), dayjs().endOf("day")]
}
},
columns: { columns: {
id: { id: {
title: "ID", title: "ID",
@ -54,7 +58,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
component: {} component: {}
}, },
valueBuilder({ value, row, key }) { valueBuilder({ value, row, key }) {
console.log("value builder:", key, value, row); utils.logger.info("value builder:", key, value, row);
if (value != null) { if (value != null) {
row[key] = dayjs(value); row[key] = dayjs(value);
} }
@ -76,7 +80,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
} }
}, },
valueBuilder({ value, row, key }) { valueBuilder({ value, row, key }) {
console.log("value builder:", key, value, row); utils.logger.info("value builder:", key, value, row);
if (value != null) { if (value != null) {
row[key] = dayjs(value); row[key] = dayjs(value);
} }
@ -121,7 +125,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
valueFormat: "YYYY-MM-DD HH:mm:ss", //输入值的格式 valueFormat: "YYYY-MM-DD HH:mm:ss", //输入值的格式
on: { on: {
onChange(context: ScopeContext) { onChange(context: ScopeContext) {
console.log("change", context); utils.logger.info("change", context);
} }
} }
} }
@ -187,7 +191,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
daterange: { daterange: {
title: "日期范围", title: "日期范围",
type: "daterange", type: "daterange",
search: { show: true, width: 300 }, search: { show: true, width: 300, col: { span: 6 } },
valueBuilder({ row, key }) { valueBuilder({ row, key }) {
if (!utils.strings.hasEmpty(row.daterangeStart, row.daterangeEnd)) { if (!utils.strings.hasEmpty(row.daterangeStart, row.daterangeEnd)) {
row[key] = [dayjs(row.daterangeStart), dayjs(row.daterangeEnd)]; row[key] = [dayjs(row.daterangeStart), dayjs(row.daterangeEnd)];
@ -197,7 +201,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
datetimerange: { datetimerange: {
title: "日期时间范围", title: "日期时间范围",
type: "datetimerange", type: "datetimerange",
search: { show: true, width: 300 }, search: { show: true, width: 300, col: { span: 8 } },
valueBuilder({ row, key }) { valueBuilder({ row, key }) {
if (!utils.strings.hasEmpty(row.datetimerangeStart, row.datetimerangeEnd)) { if (!utils.strings.hasEmpty(row.datetimerangeStart, row.datetimerangeEnd)) {
row[key] = [dayjs(row.datetimerangeStart), dayjs(row.datetimerangeEnd)]; row[key] = [dayjs(row.datetimerangeStart), dayjs(row.datetimerangeEnd)];

View File

@ -104,7 +104,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
toolbarConfig: {}, toolbarConfig: {},
editorConfig: {}, editorConfig: {},
onOnChange(value: any) { onOnChange(value: any) {
console.log("value changed", value); utils.logger.info("value changed", value);
}, },
uploader: { uploader: {
type: "form", type: "form",

View File

@ -26,6 +26,15 @@
<input type="file" @change="fileUploaderChange" /> <input type="file" @change="fileUploaderChange" />
<a v-if="signedUrl" :href="signedUrl" target="_blank"></a> <a v-if="signedUrl" :href="signedUrl" target="_blank"></a>
</a-form-item> </a-form-item>
<a-form-item label="表格选择">
<fs-table-select v-model="form.tableSelect" v-bind="tableSelectBinding" />
<fs-label label="切换value">
<a-radio-group v-model:value="form.tableSelect">
<a-radio :value="1">王小虎</a-radio>
<a-radio :value="2">id为2的记录</a-radio>
</a-radio-group>
</fs-label>
</a-form-item>
<a-form-item> <a-form-item>
<a-button @click="submit"></a-button> <a-button @click="submit"></a-button>
</a-form-item> </a-form-item>
@ -37,9 +46,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from "vue"; import { reactive, ref } from "vue";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { dict, useUi } from "@fast-crud/fast-crud"; import {dict, useUi, utils} from "@fast-crud/fast-crud";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { FsUploaderS3, loadUploader, useUploader } from "@fast-crud/fast-extends"; import { FsUploaderS3, loadUploader, useUploader } from "@fast-crud/fast-extends";
import createCrudOptionsText from "/@/views/crud/component/text/crud";
import * as textTableApi from "/@/views/crud/component/text/api";
defineOptions({ defineOptions({
name: "ComponentIndependent" name: "ComponentIndependent"
@ -49,7 +60,8 @@ const form = reactive({
avatar: undefined, avatar: undefined,
copyable: "可复制的内容", copyable: "可复制的内容",
select: 1, select: 1,
humanizeTime: dayjs(new Date().getTime() - 100000) humanizeTime: dayjs(new Date().getTime() - 100000),
tableSelect: null
}); });
const uploader = ref({ const uploader = ref({
@ -66,7 +78,7 @@ const cropperUploader = ref({
viewMode: 1 viewMode: 1
}, },
async onReady(context: any) { async onReady(context: any) {
console.log("onReady", context); utils.logger.info("onReady", context);
context.zoom(-0.1); context.zoom(-0.1);
context.zoom(-0.1); context.zoom(-0.1);
context.zoom(-0.1); context.zoom(-0.1);
@ -81,6 +93,26 @@ const dictRef = dict({
] ]
}); });
const tableSelectBinding = ref({
dict: dict({
value: "id",
label: "name",
getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values);
}
}),
crossPage: true,
valuesFormat: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
},
select: {
placeholder: "点击选择"
},
createCrudOptions: createCrudOptionsText
});
const signedUrl = ref(); const signedUrl = ref();
async function fileUploaderChange(event: any) { async function fileUploaderChange(event: any) {
const file = event.target.files[0]; const file = event.target.files[0];
@ -90,7 +122,7 @@ async function fileUploaderChange(event: any) {
file, file,
fileName: file.name, fileName: file.name,
onProgress(progress: any) { onProgress(progress: any) {
console.log("progress:" + progress.percent + "%"); utils.logger.info("progress:" + progress.percent + "%");
} }
}); });
const { ui } = useUi(); const { ui } = useUi();
@ -104,6 +136,6 @@ async function fileUploaderChange(event: any) {
function submit() { function submit() {
message.info("submit:" + JSON.stringify(form)); message.info("submit:" + JSON.stringify(form));
console.log("submit:", form); utils.logger.info("submit:", form);
} }
</script> </script>

View File

@ -0,0 +1,50 @@
import { requestForMock } from "/src/api/service";
const request = requestForMock;
const apiPrefix = "/mock/ComponentPhone";
export function GetList(query: any) {
return request({
url: apiPrefix + "/page",
method: "get",
data: query
});
}
export function AddObj(obj: any) {
return request({
url: apiPrefix + "/add",
method: "post",
data: obj
});
}
export function UpdateObj(obj: any) {
return request({
url: apiPrefix + "/update",
method: "post",
data: obj
});
}
export function DelObj(id: any) {
return request({
url: apiPrefix + "/delete",
method: "post",
params: { id }
});
}
export function GetObj(id: any) {
return request({
url: apiPrefix + "/get",
method: "get",
params: { id }
});
}
export function GetByIds(ids: any) {
return request({
url: apiPrefix + "/byIds",
method: "post",
data: { ids }
});
}

View File

@ -0,0 +1,92 @@
import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, ScopeContext, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { SearchOutlined } from "@ant-design/icons-vue";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
if (form.id == null) {
form.id = row.id;
}
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest
},
columns: {
id: {
title: "ID",
type: "number",
form: { show: false }
},
phone: {
title: "手机号码",
type: "phone",
search: { show: true }
},
phoneNumber: {
title: "区号手机号分开",
type: "phone",
valueBuilder({ row, key, value }) {
row[key] = {
callingCode: row.code || undefined,
phoneNumber: value || ""
};
},
valueResolve({ form, key, value }) {
if (value) {
form.code = value.callingCode;
form.phoneNumber = value.phoneNumber;
}
}
},
only: {
title: "仅某些国家",
type: "phone",
form: {
component: {
onlyCountries: ["CN", "US"]
},
helper: "仅CN,US"
}
},
ignore: {
title: "排除某些国家",
type: "phone",
form: {
component: {
ignoredCountries: ["jp"]
},
helper: "排除JP"
}
},
priority: {
title: "优先某些国家",
type: "phone",
form: {
component: {
priorityCountries: ["CN", "US"],
ignoredCountries: ["jp"]
},
helper: "优先CNUS排除JP"
}
}
}
}
};
}

View File

@ -0,0 +1,37 @@
<template>
<fs-page>
<template #header>
<div class="title">
国际手机号输入
<span class="sub"> </span>
</div>
<div class="more">
<a target="_blank" href="http://fast-crud.docmirror.cn/api/components/extends/input/components/fs-phone-input">文档</a>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page>
</template>
<script lang="ts">
import { defineComponent, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
export default defineComponent({
name: "ComponentPhone",
setup() {
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
//
onMounted(() => {
crudExpose.doRefresh();
});
return {
crudBinding,
crudRef
};
}
});
</script>

View File

@ -0,0 +1,18 @@
import mockUtil from "/src/mock/base";
const options: any = {
name: "ComponentPhone",
idGenerator: 0
};
const list = [
{
phone: {
callingCode: "86",
phoneNumber: "12345678"
},
code: "86",
phoneNumber: "12424354"
}
];
options.list = list;
const mock = mockUtil.buildMock(options);
export default mock;

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi, utils } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -17,7 +17,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form); return await api.AddObj(form);
}; };
const { ui } = useUi();
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -44,7 +44,23 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "dict-radio", type: "dict-radio",
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}) }),
form: {
valueChange({ value }) {
utils.logger.info("change", value);
},
component: {
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
ui.message.info(`你选择了${JSON.stringify($event)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
}
}
}
}, },
button: { button: {
title: "按钮样式", title: "按钮样式",

View File

@ -1,6 +1,6 @@
import * as api from "./api"; import * as api from "./api";
import { requestForMock } from "/src/api/service"; import { requestForMock } from "/src/api/service";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, DictOnReadyContext, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, DictOnReadyContext, EditReq, UserPageQuery, UserPageRes, useUi, utils } from "@fast-crud/fast-crud";
import { ref } from "vue"; import { ref } from "vue";
import _ from "lodash-es"; import _ from "lodash-es";
function useSearchRemote() { function useSearchRemote() {
@ -11,7 +11,7 @@ function useSearchRemote() {
fetching: ref(false) fetching: ref(false)
}; };
const fetchUser = _.debounce((value) => { const fetchUser = _.debounce((value) => {
console.log("fetching user", value); utils.logger.info("fetching user", value);
lastFetchId += 1; lastFetchId += 1;
const fetchId = lastFetchId; const fetchId = lastFetchId;
@ -76,6 +76,8 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
//dictRef.toMap(); //dictRef.toMap();
} }
const { ui } = useUi();
const { fetchUser, searchState } = useSearchRemote(); const { fetchUser, searchState } = useSearchRemote();
return { return {
dynamicUpdateDictOptions, dynamicUpdateDictOptions,
@ -117,7 +119,24 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
statusLocal: { statusLocal: {
title: "单选本地", title: "单选本地",
type: "dict-select", type: "dict-select",
dict: dictRef dict: dictRef,
form: {
component: {
onChange(args: any) {
utils.logger.info("onChange", args);
},
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
ui.message.info(`你选择了${JSON.stringify($event)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
}
},
helper: "selected-change事件可以获取选中的option对象"
}
}, },
statusRemote: { statusRemote: {
title: "单选远程", title: "单选远程",
@ -131,11 +150,6 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}), }),
form: { form: {
rules: [{ required: true, message: "请选择一个选项" }] rules: [{ required: true, message: "请选择一个选项" }]
},
column: {
component: {
type: "text"
}
} }
}, },
filter: { filter: {
@ -191,7 +205,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
getData({ dict }) { getData({ dict }) {
// 覆盖全局获取字典请求配置 // 覆盖全局获取字典请求配置
console.log(`我是从自定义的getData方法中加载的数据字典`, dict); utils.logger.info(`我是从自定义的getData方法中加载的数据字典`, dict);
return requestForMock({ return requestForMock({
url: "/mock/dicts/OpenStatusEnum?cache", url: "/mock/dicts/OpenStatusEnum?cache",
method: "get" method: "get"
@ -221,7 +235,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
// 此处dict配置会覆盖上面dict的属性 // 此处dict配置会覆盖上面dict的属性
prototype: true, // form表单的dict设置为原型复制每次初始化时都会重新loadDict prototype: true, // form表单的dict设置为原型复制每次初始化时都会重新loadDict
onReady({ dict }: DictOnReadyContext) { onReady({ dict }: DictOnReadyContext) {
console.log("字典请求ready", dict); utils.logger.info("字典请求ready", dict);
dict.data[0].disabled = true; // 禁用某个选项, 还可以自己修改选项 dict.data[0].disabled = true; // 禁用某个选项, 还可以自己修改选项
} }
} }
@ -242,7 +256,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
component: { component: {
//监听 dict-change事件 //监听 dict-change事件
onDictChange({ dict, form, key }: any) { onDictChange({ dict, form, key }: any) {
console.log("dict data changed", dict, key); utils.logger.info("dict data changed", dict, key);
if (dict.data != null && form.firstDefault == null) { if (dict.data != null && form.firstDefault == null) {
form.firstDefault = dict.data[0].value; form.firstDefault = dict.data[0].value;
} }

View File

@ -1,5 +1,16 @@
import * as api from "./api"; import * as api from "./api";
import { dict, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, UserPageQuery, UserPageRes, EditReq, DelReq, AddReq } from "@fast-crud/fast-crud"; import {
dict,
compute,
CreateCrudOptionsProps,
CreateCrudOptionsRet,
UserPageQuery,
UserPageRes,
EditReq,
DelReq,
AddReq,
utils
} from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -98,7 +109,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
name: "fs-dict-switch", name: "fs-dict-switch",
vModel: "checked", vModel: "checked",
onChange: (value: any) => { onChange: (value: any) => {
console.log("onChange", value); utils.logger.info("onChange", value);
} }
// onChange: compute((context) => { // onChange: compute((context) => {
// //动态onChange方法测试 // //动态onChange方法测试
@ -132,7 +143,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
form: { form: {
show: compute((context) => { show: compute((context) => {
console.log("context", context); utils.logger.info("context", context);
//根据cellSwitch字段显隐 //根据cellSwitch字段显隐
return context.form.cellSwitch === true; return context.form.cellSwitch === true;
}) })

View File

@ -1,7 +1,8 @@
import * as api from "./api"; import * as api from "./api";
import * as textTableApi from "../text/api"; import * as textTableApi from "../text/api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
import createCrudOptionsText from "../text/crud"; import createCrudOptionsText from "../text/crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -20,6 +21,17 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
return await api.AddObj(form); return await api.AddObj(form);
}; };
const crudOptionsOverride = {
table: {
scroll: {
x: 2000
}
},
rowHandle: {
fixed: "right"
}
};
const { ui } = useUi();
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -40,6 +52,16 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
show: false show: false
} }
}, },
dynamicShow: {
title: "动态显隐",
type: "dict-switch",
dict: dict({
data: [
{ value: true, label: "显示" },
{ value: false, label: "隐藏" }
]
})
},
single: { single: {
title: "单选", title: "单选",
search: { show: true }, search: { show: true },
@ -47,19 +69,31 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
value: "id", value: "id",
label: "name", label: "name",
//重要根据value懒加载数据
getNodesByValues: async (values: any[]) => { getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values); return await textTableApi.GetByIds(values);
} }
}), }),
form: { form: {
show: compute(({ form }) => {
return form.dynamicShow;
}),
component: { component: {
crossPage: true, crossPage: true,
valuesFormat: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
},
select: {
placeholder: "点击选择"
},
createCrudOptions: createCrudOptionsText, createCrudOptions: createCrudOptionsText,
crudOptionsOverride: { crudOptionsOverride,
table: { on: {
scroll: { selectedChange({ $event }) {
x: 2000 console.log("selectedChange", $event);
} ui.message.info(`你选择了${JSON.stringify($event)}`);
} }
} }
} }
@ -72,6 +106,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
value: "id", value: "id",
label: "name", label: "name",
//重要根据value懒加载数据
getNodesByValues: async (values: any[]) => { getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values); return await textTableApi.GetByIds(values);
} }
@ -80,15 +115,126 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
component: { component: {
crossPage: true, crossPage: true,
multiple: true, multiple: true,
valuesFormat: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
},
select: {
placeholder: "点击选择"
},
createCrudOptions: createCrudOptionsText, createCrudOptions: createCrudOptionsText,
crudOptionsOverride: { crudOptionsOverride: crudOptionsOverride
table: { }
scroll: { },
x: 2000 column: {
component: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
}
}
},
valueType: {
title: "object类型",
search: { show: true },
type: "table-select",
dict: dict({
value: "id",
label: "name",
//重要根据value懒加载数据
getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values);
}
}),
column: {
component: {
valueType: "object"
}
},
form: {
helper: "这里提交的值是整个对象",
component: {
valueType: "object",
crossPage: true,
valuesFormat: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
},
select: {
placeholder: "点击选择"
},
createCrudOptions: createCrudOptionsText,
crudOptionsOverride
}
}
},
//值是object类型
valueTypeMulti: {
title: "object类型多选",
search: { show: true },
type: "table-select",
dict: dict({
value: "id",
label: "name",
getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values);
}
}),
column: {
component: {
valueType: "object"
}
},
form: {
helper: "这里提交的值是对象数组",
component: {
valueType: "object",
crossPage: true,
multiple: true,
valuesFormat: {
labelFormatter: (item: any) => {
return `${item.id}.${item.name}`;
}
},
select: {
placeholder: "点击选择"
},
createCrudOptions: createCrudOptionsText,
crudOptionsOverride
}
}
},
viewMode: {
title: "查看模式",
dict: dict({
value: "id",
label: "name"
}),
column: {
component: {
name: "fs-table-select",
//设置为查看模式
viewMode: true,
createCrudOptions: createCrudOptionsText,
crudOptionsOverride,
slots: {
default({ scope, value }) {
async function open() {
//打开时传入默认查询参数
const crudOptions = {
search: {
initialForm: {
classId: value
}
}
};
const { crudExpose } = await scope.open({ crudOptions });
// 这里还能通过crudExpose等返回值操作表格
} }
},
rowHandle: { return <a-button onClick={open}>:{value}</a-button>;
fixed: "right"
} }
} }
} }

View File

@ -1,13 +1,33 @@
<template> <template>
<fs-page> <fs-page>
<template #header>
<div class="title">table-select</div>
<div class="more">
<fs-label label="直接设置值">
<fs-table-select v-model="value" :dict="singleDictRef" :create-crud-options="createCrudOptionsText" />
<a-button @click="setValue"> </a-button>
</fs-label>
<fs-label label="自定义插槽">
{{ value }}
<fs-table-select v-model="value" :dict="singleDictRef" :create-crud-options="createCrudOptionsText">
<template #default="scope">
<fs-button @click="scope.open()"> </fs-button>
</template>
</fs-table-select>
</fs-label>
</div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding" /> <fs-crud ref="crudRef" v-bind="crudBinding" />
</fs-page> </fs-page>
</template> </template>
<script lang="ts"> <script lang="ts">
import createCrudOptionsText from "../text/crud";
import { defineComponent, onMounted, ref } from "vue"; import { defineComponent, onMounted, ref } from "vue";
import createCrudOptions from "./crud.js"; import createCrudOptions from "./crud.js";
import { useFs } from "@fast-crud/fast-crud"; import { dict, useFs } from "@fast-crud/fast-crud";
import * as textTableApi from "/@/views/crud/component/text/api";
export default defineComponent({ export default defineComponent({
name: "ComponentTableSelect", name: "ComponentTableSelect",
@ -20,10 +40,25 @@ export default defineComponent({
}); });
const value = ref(null); const value = ref(null);
const singleDictRef = dict({
value: "id",
label: "name",
//value
getNodesByValues: async (values: any[]) => {
return await textTableApi.GetByIds(values);
}
});
function setValue() {
value.value = 1;
}
return { return {
crudBinding, crudBinding,
crudRef, crudRef,
value value,
singleDictRef,
createCrudOptionsText,
setValue
}; };
} }
}); });

View File

@ -6,11 +6,15 @@ const options: any = {
const list = [ const list = [
{ {
single: 1, single: 1,
multi: [1, 2] multi: [1, 2],
valueType: { id: 1, name: "王小虎" },
viewMode: 1
}, },
{ {
single: 3, single: 3,
multi: [1, 2, 3] multi: [1, 2, 3],
dynamicShow: false,
viewMode: 2
}, },
{ {
single: 2 single: 2

View File

@ -40,6 +40,7 @@ export function GetObj(id: any) {
params: { id } params: { id }
}); });
} }
export function GetByIds(ids: any) { export function GetByIds(ids: any) {
return request({ return request({
url: apiPrefix + "/byIds", url: apiPrefix + "/byIds",

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, ScopeContext, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, ScopeContext, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { SearchOutlined } from "@ant-design/icons-vue"; import { SearchOutlined } from "@ant-design/icons-vue";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
@ -29,6 +29,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
delRequest delRequest
}, },
columns: { columns: {
id: {
title: "ID",
type: "number",
form: { show: false }
},
name: { name: {
title: "姓名", title: "姓名",
type: "text", //虽然不写也能正确显示组件,但不建议省略它 type: "text", //虽然不写也能正确显示组件,但不建议省略它
@ -37,8 +42,19 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
component: { component: {
maxlength: 20 maxlength: 20
} }
},
column: {
formatter(scope) {
utils.logger.info("formatter scope", scope);
return scope.value;
}
} }
}, },
classId: {
title: "班级id",
type: "number", //虽然不写也能正确显示组件,但不建议省略它
search: { show: true }
},
trim: { trim: {
title: "trim空格", title: "trim空格",
type: "text", //虽然不写也能正确显示组件,但不建议省略它 type: "text", //虽然不写也能正确显示组件,但不建议省略它
@ -150,7 +166,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
title: "复杂输入", title: "复杂输入",
component: { component: {
render(context: ScopeContext) { render(context: ScopeContext) {
console.log("context scope", context); utils.logger.info("context scope", context);
return ( return (
<a-input-group compact> <a-input-group compact>
<a-input placeholder={"render1 input"} style="width: 50%" v-model={[context.form.render, "value"]} /> <a-input placeholder={"render1 input"} style="width: 50%" v-model={[context.form.render, "value"]} />

View File

@ -17,21 +17,24 @@ const list = [
address: "123123", address: "123123",
zip: "518000", zip: "518000",
intro: "王小虎是element-plus的table示例出现的名字", intro: "王小虎是element-plus的table示例出现的名字",
copy: "测试文本" copy: "测试文本",
classId: 1
}, },
{ {
name: "张三", name: "张三",
date: "2016-05-04", date: "2016-05-04",
status: "1", status: "1",
province: "2", province: "2",
copy: "测试文本" copy: "测试文本",
classId: 2
}, },
{ {
name: "李四", name: "李四",
date: 2232433534511, date: 2232433534511,
status: "1", status: "1",
province: "0", province: "0",
copy: "测试文本" copy: "测试文本",
classId: 3
}, },
{ {
name: "王五", name: "王五",

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi, utils } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -18,6 +18,26 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
return await api.AddObj(form); return await api.AddObj(form);
}; };
const lazyloadDictRef = dict({
data: [
{
id: "0",
label: "Root",
value: "0"
}
]
});
const genTreeNode = (parentId: number, isLeaf = false) => {
const random = Math.random().toString(36).substring(2, 6);
return {
id: random,
pId: parentId,
value: random,
label: isLeaf ? "Tree Node" : "Expand to load",
isLeaf
};
};
const { ui } = useUi();
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -45,7 +65,30 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
dict: dict({ dict: dict({
isTree: true, isTree: true,
url: "/mock/dicts/cascaderData?single" url: "/mock/dicts/cascaderData?single"
}) }),
form: {
valueChange({ getComponentRef }) {
const compRef = getComponentRef("tree");
console.log("tree ref:", compRef, compRef.$refs.treeRef);
},
component: {
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
ui.message.info(`你选择了${JSON.stringify($event.label)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
},
slots: {
title({ scope }) {
//自定义选项text
return `${scope.label}(${scope.value})`;
}
}
}
}
}, },
multiple: { multiple: {
title: "多选", title: "多选",
@ -57,7 +100,27 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}), }),
form: { form: {
component: { component: {
"tree-checkable": true "tree-checkable": true,
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
const labels = $event.map((item) => item.label);
ui.message.info(`你选择了${JSON.stringify(labels)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
}
},
rules: [{ required: true, message: "请选择" }],
on: {
selectedChange({ form, $event }) {
// $event就是原始的事件值也就是选中的 option对象
utils.logger.info("onSelectedChange", form, $event);
ui.message.info(`你选择了${JSON.stringify($event)}`);
// 你还可以将选中的label值赋值给表单里其他字段
// context.form.xxxLabel = context.$event.label
}
} }
} }
}, },
@ -70,10 +133,25 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
url: "/mock/dicts/littlePca", url: "/mock/dicts/littlePca",
value: "code", value: "code",
label: "name" label: "name"
}), })
},
lazy: {
title: "懒加载",
search: { show: false },
type: "dict-tree",
dict: lazyloadDictRef,
form: { form: {
component: { component: {
fieldNames: { label: "name", key: "code", value: "code" } "tree-data-simple-mode": true,
loadData: (treeNode: any) => {
return new Promise((resolve: (value?: unknown) => void) => {
const { id } = treeNode.dataRef;
setTimeout(() => {
lazyloadDictRef.data = lazyloadDictRef.data.concat([genTreeNode(id, false), genTreeNode(id, true)]);
resolve();
}, 300);
});
}
} }
} }
} }

View File

@ -44,6 +44,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "file-uploader", type: "file-uploader",
form: { form: {
component: { component: {
multiple: true, //可选择多个
uploader: { uploader: {
type: "alioss" type: "alioss"
} }
@ -58,7 +59,8 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
uploader: { uploader: {
type: "alioss" type: "alioss"
} }
} },
helper: "可以同时选择多个文件"
} }
}, },
cropper: { cropper: {

View File

@ -44,10 +44,12 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "file-uploader", type: "file-uploader",
form: { form: {
component: { component: {
multiple: true, //可选择多个
uploader: { uploader: {
type: "cos" type: "cos"
} }
} },
helper: "可以同时选择多个文件"
} }
}, },
pictureCard: { pictureCard: {

View File

@ -41,7 +41,14 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
cropper: { cropper: {
title: "头像裁剪上传", title: "头像裁剪上传",
type: "cropper-uploader" type: "cropper-uploader",
form: {
component: {
uploader: {
type: "form"
}
}
}
}, },
avatar: { avatar: {
title: "数量限制", title: "数量限制",

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { createUploaderRules } from "@fast-crud/fast-extends"; import { createUploaderRules } from "@fast-crud/fast-extends";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
@ -33,7 +33,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
async onOpened() { async onOpened() {
// 异步组件实例的获取 // 异步组件实例的获取
const componentRef = await crudExpose.getFormComponentRef("file", true); const componentRef = await crudExpose.getFormComponentRef("file", true);
console.log("componentRef", componentRef); utils.logger.info("componentRef", componentRef);
} }
} }
}, },
@ -56,9 +56,11 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
component: { component: {
multiple: true, //可选择多个 multiple: true, //可选择多个
uploader: { uploader: {
type: "form" type: "form",
keepName: true
} }
} },
helper: "可以同时选择多个文件"
}, },
column: { column: {
component: { component: {
@ -155,20 +157,14 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
valueType: "key", valueType: "key",
async buildUrl(value: any) { async buildUrl(value: any) {
return new Promise((resolve) => { return "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
resolve(url);
});
} }
} }
}, },
column: { column: {
component: { component: {
async buildUrl(value: any) { async buildUrl(value: any) {
return new Promise((resolve) => { return "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
resolve(url);
});
} }
} }
} }
@ -193,24 +189,19 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
valueType: "fileId", valueType: "fileId",
async buildUrls(value: any[]) { async buildUrls(value: any[]) {
//批量构建url //批量构建url
return new Promise((resolve) => { const urls: string[] = [];
const urls: string[] = []; for (const item of value) {
for (const item of value) { const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + item;
const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + item; urls.push(url);
urls.push(url); }
} return urls;
resolve(urls);
});
} }
} }
}, },
column: { column: {
component: { component: {
async buildUrl(value: any) { async buildUrl(value: any) {
return new Promise((resolve) => { return "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
const url = "http://www.docmirror.cn:7070/api/upload/form/download?key=" + value;
resolve(url);
});
} }
} }
} }

View File

@ -43,10 +43,12 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "file-uploader", type: "file-uploader",
form: { form: {
component: { component: {
multiple: true, //可选择多个
uploader: { uploader: {
type: "qiniu" type: "qiniu"
} }
} },
helper: "可以同时选择多个文件"
} }
}, },
pictureCard: { pictureCard: {

View File

@ -1,7 +1,8 @@
// @ts-ignore // @ts-ignore
import { S3Client, GetObjectCommand, PutObjectCommand, CreateBucketCommand } from "@aws-sdk/client-s3"; import { S3Client, GetObjectCommand, PutObjectCommand, CreateBucketCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import {utils} from "@fast-crud/fast-crud";
// TODO 模拟server 你应该将此代码搬到你的server端
let bucketCreated = false; let bucketCreated = false;
export async function generateSignedUrl(bucket: string, key: string, type: "put" | "get" = "get") { export async function generateSignedUrl(bucket: string, key: string, type: "put" | "get" = "get") {
const client = new S3Client({ const client = new S3Client({
@ -39,7 +40,7 @@ export async function generateSignedUrl(bucket: string, key: string, type: "put"
try { try {
url = await getSignedUrl(client, cmd); url = await getSignedUrl(client, cmd);
} catch (err) { } catch (err) {
console.log("Error getting signed URL ", err); utils.logger.info("Error getting signed URL ", err);
} }
return url; return url;

View File

@ -1,8 +1,7 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import dayjs from "dayjs";
import { computed, Ref, ref } from "vue"; import { computed, Ref, ref } from "vue";
import dayjs from "dayjs";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -23,71 +22,168 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
const options: Ref = ref([]); const options: Ref = ref([]);
let arr = [
{
value: "1",
label: "test"
},
{
value: "1",
label: "test2"
}
];
for (let i = 0; i < 10; i++) {
arr = arr.concat(arr);
}
let i = 0;
for (const item of arr) {
i++;
item.value = i + "";
}
options.value = arr;
return { return {
crudOptions: { crudOptions: {
table: {},
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest delRequest
}, },
form: { toolbar: {},
// 单列布局
col: { span: 24 },
labelCol: { span: 4 },
wrapperCol: { span: 18 }
},
rowHandle: { rowHandle: {
fixed: "right" buttons: {
edit: { show: true }
}
},
form: {
watch({ form }) {
form.totalAmount = form.users * form.months * form.licensePrice;
form.statementAmount = form.totalAmount - form.discountAmount;
form.statementPrice = form.statementAmount / form.months / form.users;
if (form.months && form.startTime) {
// form.endTime = dayjs(form.startTime).add(form.months, "month");
}
}
}, },
columns: { columns: {
id: { id: {
title: "ID", title: "ID",
key: "id", type: "text",
form: { show: false },
column: { show: false }
},
users: {
title: "用户数量",
type: "number", type: "number",
column: { column: { width: 120 },
width: 50
},
form: { form: {
show: false component: { min: 1, max: 10000 },
rules: [{ required: true, message: "用户数量不能为空" }]
} }
}, },
statusRemote: { months: {
title: "单选远程", title: "月数",
search: { type: "number",
show: false column: { width: 100 },
form: {
component: { min: 1, max: 120 },
rules: [{ required: true, message: "采购月数不能为空" }]
}
},
licensePrice: {
title: "单用户价",
type: "number",
column: { width: 150 },
form: {
component: { min: 1, max: 99999 },
helper: "单个用户许可价格"
}
},
totalAmount: {
title: "总额",
type: "number",
search: { show: false },
column: { width: 150 },
form: {
component: { disabled: true }
}
},
discountAmount: {
title: "优惠",
type: "number",
column: { width: 150 },
addForm: {
value: 0
}, },
form: {
component: { min: 0, max: 9999999 },
rules: [{ required: true, message: "优惠金额不能为空" }]
}
},
statementAmount: {
title: "结算金额",
type: "number",
search: { show: false },
column: { width: 150 },
form: {
component: { disabled: true },
rules: [{ required: true, message: "结算金额不能为空" }],
helper: "结算金额 = 总额 - 优惠金额"
}
},
statementPrice: {
title: "结算单价",
type: "number",
column: { width: 150 },
form: {
component: { disabled: true },
helper: "结算单价 = 结算金额 / 月份 / 用户数"
}
},
startTime: {
title: "开始时间",
type: "date",
valueBuilder({ value, row, key }) {
if (value != null) {
row[key] = dayjs.unix(value);
}
},
valueResolve({ value, row, key }) {
if (value != null) {
row[key] = dayjs(value).unix();
}
},
form: {
rules: [{ required: true, message: "订阅起始日期不能为空" }],
component: {
format: "YYYY-MM-DD"
}
}
},
endTime: {
title: "结束时间",
type: "date",
valueResolve({ value, row, key }) {
if (value != null) {
row[key] = dayjs(value).unix();
}
},
form: {
component: {
format: "YYYY-MM-DD",
disabled: true
},
rules: [{ required: true, message: "订阅结束日期不能为空" }],
helper: "结束日期 = 开始日期 + 采购月份"
}
},
paymentStatus: {
title: "状态",
type: "dict-select", type: "dict-select",
column: { width: 100, align: "center" },
search: { show: true },
dict: dict({ dict: dict({
url: "/mock/dicts/ManyOpenStatusEnum?from=dict1" data: [
{ value: "0", label: "待支付", color: "error" },
{ value: "10", label: "部分支付", color: "warning" },
{ value: "20", label: "已支付", color: "success" }
]
}), }),
form: { form: {
component: { mode: "multiple" }, rules: [{ required: true, message: "支付状态不能为空" }]
rules: [{ required: true, message: "请选择一个选项" }] }
}, },
column: { description: {
width: 200 title: "产品描述",
column: { show: false },
type: ["textarea"],
form: {
rules: [{ required: true, message: "描述不能为空" }],
col: {
span: 24
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, ValueChangeContext } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
import { requestForMock } from "../../../../api/service"; import { requestForMock } from "../../../../api/service";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
@ -80,7 +80,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
vModel: "checked" vModel: "checked"
}, },
valueChange({ form, value, getComponentRef }) { valueChange({ form, value, getComponentRef }) {
console.log("form", value); utils.logger.info("form", value);
const targetDict = getComponentRef("remote").getDict(); const targetDict = getComponentRef("remote").getDict();
targetDict.url = form.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; targetDict.url = form.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote";
targetDict.reloadDict(); targetDict.reloadDict();
@ -92,7 +92,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
vModel: "checked" vModel: "checked"
}, },
valueChange({ value, getComponentRef }) { valueChange({ value, getComponentRef }) {
console.log("value", value); utils.logger.info("value", value);
const targetDict = getComponentRef("remote").getDict(); const targetDict = getComponentRef("remote").getDict();
targetDict.url = value ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; targetDict.url = value ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote";
targetDict.reloadDict(); targetDict.reloadDict();

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -55,7 +55,15 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
status: { status: {
title: "本地字典", title: "本地字典",
search: { show: false }, search: {
show: true,
component: {
transformDictData(data: any[]) {
data.unshift({ value: "", label: "全部" });
return data;
}
}
},
dict: statusDict, dict: statusDict,
type: "dict-select" type: "dict-select"
}, },
@ -67,7 +75,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
column: { column: {
component: { component: {
onDictChange(opts: any) { onDictChange(opts: any) {
console.log("字典变化:", opts); utils.logger.info("字典变化:", opts);
} }
} }
} }
@ -82,7 +90,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
vModel: "checked" vModel: "checked"
}, },
valueChange({ form }) { valueChange({ form }) {
console.log("changed", form.modifyDict); utils.logger.info("changed", form.modifyDict);
remoteDict.url = form.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote"; remoteDict.url = form.modifyDict ? "/mock/dicts/moreOpenStatusEnum?remote" : "/mock/dicts/OpenStatusEnum?remote";
// 由于remoteDict.cloneable =false,所以全局公用一个实例,修改会影响全部地方 // 由于remoteDict.cloneable =false,所以全局公用一个实例,修改会影响全部地方
remoteDict.reloadDict(); remoteDict.reloadDict();
@ -101,6 +109,28 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
} }
} }
} }
},
cache1: {
title: "全局缓存1",
search: { show: false },
dict: dict({
url: "/mock/dicts/OpenStatusEnum?cache_flat=1",
cache: true
}),
type: "dict-select"
},
cache2: {
title: "全局缓存2",
search: { show: false },
column: {
show: false
},
dict: dict({
url: "/mock/dicts/OpenStatusEnum?cache_flat=1",
immediate: false,
cache: true
}),
type: "dict-select"
} }
} }
} }

View File

@ -19,7 +19,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import {useFs, utils} from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
export default defineComponent({ export default defineComponent({
@ -33,7 +33,7 @@ export default defineComponent({
}); });
function onClick() { function onClick() {
console.log("this.ref", crudRef.value); utils.logger.info("this.ref", crudRef.value);
} }
return { return {

View File

@ -57,7 +57,7 @@ export function UpdateCell(id: number, key: string, value: any) {
data: { id, key, value } data: { id, key, value }
}); });
} }
export function UpdateColumn(data) { export function UpdateColumn(data: any) {
return request({ return request({
url: apiPrefix + "/columnUpdate", url: apiPrefix + "/columnUpdate",
method: "post", method: "post",

View File

@ -1,8 +1,7 @@
import * as api from "./api"; import * as api from "./api";
import { dict, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, UserPageQuery, UserPageRes, EditReq, DelReq, AddReq } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditableEachCellsOpts, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { computed, reactive, ref } from "vue"; import { reactive, ref } from "vue";
import _ from "lodash-es";
import { EditableEachCellsOpts } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose; const { crudBinding } = crudExpose;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -46,7 +45,6 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
editing: false, editing: false,
loading: false, loading: false,
onSubmit: async () => { onSubmit: async () => {
console.log("onSubmit");
radioColumnEditor.loading = true; radioColumnEditor.loading = true;
try { try {
const data: any[] = []; const data: any[] = [];
@ -61,7 +59,6 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
} }
}, },
onCancel: () => { onCancel: () => {
console.log("cancel");
crudExpose.editable.cancel(); crudExpose.editable.cancel();
radioColumnEditor.editing = false; radioColumnEditor.editing = false;
}, },
@ -111,7 +108,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
mode: "cell", mode: "cell",
exclusive: true, exclusive: true,
//排他式激活效果,将其他行的编辑状态触发保存 //排他式激活效果,将其他行的编辑状态触发保存
exclusiveEffect: "save", exclusiveEffect: "save", //自动保存其他行编辑状态cancel = 自动关闭其他行编辑状态
async updateCell(opts) { async updateCell(opts) {
const { row, key, value } = opts; const { row, key, value } = opts;
//如果是添加,需要返回{[rowKey]:xxx},比如:{id:2} //如果是添加,需要返回{[rowKey]:xxx},比如:{id:2}
@ -160,7 +157,10 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
type: "dict-radio", type: "dict-radio",
dict: radioDictRef, dict: radioDictRef,
column: { column: {
width: 300 width: 300,
valueChange({ value, getComponentRef }) {
console.log("value changed:", value, getComponentRef("radio"));
}
} }
}, },
name: { name: {

View File

@ -5,7 +5,7 @@
单元格编辑 单元格编辑
<span class="sub">单元格修改确认后直接提交到后台</span> <span class="sub">单元格修改确认后直接提交到后台</span>
</div> </div>
<div class="more"></div> <div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/api/crud-options/table.html#editable">文档</a></div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> <fs-crud ref="crudRef" v-bind="crudBinding">
<template #actionbar-right> <template #actionbar-right>
@ -38,7 +38,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud"; import {useFs, utils} from "@fast-crud/fast-crud";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
export default defineComponent({ export default defineComponent({
@ -57,17 +57,14 @@ export default defineComponent({
crudBinding, crudBinding,
crudRef, crudRef,
active() { active() {
crudExpose.editable.active(); crudExpose.editable.active({});
}, },
inactive() { inactive() {
crudExpose.editable.inactive(); crudExpose.editable.inactive();
}, },
save() { save() {
crudExpose.getTableRef().editable.submit(({ changed, removed, setData }: any) => { crudExpose.getTableRef().editable.submit(({ changed, removed, setData }: any) => {
console.log("changed", changed); utils.logger.info("table data:", crudBinding.value.data, crudExpose.getTableData());
console.log("removed", removed);
console.log("table data:", crudBinding.value.data, crudExpose.getTableData());
// setData({ 0: {id:1} }); //data // setData({ 0: {id:1} }); //data
message.success("保存,修改行:" + JSON.stringify(changed) + ";删除行:" + JSON.stringify(removed)); message.success("保存,修改行:" + JSON.stringify(changed) + ";删除行:" + JSON.stringify(removed));
}); });

View File

@ -1,5 +1,6 @@
import * as api from "./api"; import * as api from "./api";
import { dict, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, UserPageQuery, UserPageRes, EditReq, DelReq, AddReq } from "@fast-crud/fast-crud"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose; const { crudBinding } = crudExpose;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -37,9 +38,18 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
} }
} }
}, },
mode: {
//本地模式
name: "local",
isMergeWhenUpdate: true,
isAppendWhenAdd: true
},
table: { table: {
editable: { editable: {
mode: "free" enabled: true,
mode: "free",
activeDefault: true,
showAction: false
} }
}, },
pagination: { pagination: {

View File

@ -2,7 +2,7 @@
<fs-page> <fs-page>
<template #header> <template #header>
<div class="title">可编辑</div> <div class="title">可编辑</div>
<div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/api/expose.html">文档</a></div> <div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/api/crud-options/table.html#editable">文档</a></div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> <fs-crud ref="crudRef" v-bind="crudBinding">
<template #actionbar-right> <template #actionbar-right>
@ -11,10 +11,14 @@
<a-radio-button :value="true">启用编辑</a-radio-button> <a-radio-button :value="true">启用编辑</a-radio-button>
<a-radio-button :value="false">退出编辑</a-radio-button> <a-radio-button :value="false">退出编辑</a-radio-button>
</a-radio-group> </a-radio-group>
<!-- <a-radio-group class="ml-1" v-model="crudBinding.table.editable.mode">--> <a-radio-group v-model:value="crudBinding.table.editable.showAction" class="ml-1">
<!-- <a-radio-button label="free">自由模式</a-radio-button>--> <a-radio-button :value="true">显示操作按钮</a-radio-button>
<!-- <a-radio-button label="row">行编辑模式</a-radio-button>--> <a-radio-button :value="false">不显示</a-radio-button>
<!-- </a-radio-group>--> </a-radio-group>
<a-radio-group v-model:value="crudBinding.table.editable.activeDefault" class="ml-1">
<a-radio-button :value="true">默认激活</a-radio-button>
<a-radio-button :value="false">默认不激活</a-radio-button>
</a-radio-group>
<template v-if="crudBinding.table.editable.enabled"> <template v-if="crudBinding.table.editable.enabled">
<fs-button class="ml-5" @click="save"></fs-button> <fs-button class="ml-5" @click="save"></fs-button>
<fs-button class="ml-5" @click="log">log</fs-button> <fs-button class="ml-5" @click="log">log</fs-button>
@ -25,9 +29,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted, ref } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud"; import { useFs, utils } from "@fast-crud/fast-crud";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
export default defineComponent({ export default defineComponent({
@ -38,14 +42,14 @@ export default defineComponent({
// //
onMounted(() => { onMounted(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
crudExpose.editable.enable({ mode: "free", activeDefault: true }); crudExpose.editable.enable({ mode: "free" });
}); });
return { return {
crudBinding, crudBinding,
crudRef, crudRef,
active() { active() {
crudExpose.editable.active(); crudExpose.editable.active({});
}, },
inactive() { inactive() {
crudExpose.editable.inactive(); crudExpose.editable.inactive();
@ -54,12 +58,13 @@ export default defineComponent({
const res = await crudExpose.editable.validate(); const res = await crudExpose.editable.validate();
if (res !== true) { if (res !== true) {
console.error("validate error:", res); console.error("validate error:", res);
message.error("validate error" + JSON.stringify(res));
return; return;
} }
message.success("保存,修改行:" + JSON.stringify(crudBinding.value.data)); message.success("保存,修改行:" + JSON.stringify(crudBinding.value.data));
}, },
log() { log() {
console.log("table data:", crudBinding.value.data, crudExpose.getTableData()); utils.logger.info("table data:", crudBinding.value.data, crudExpose.getTableData());
}, },
cancel() { cancel() {
crudExpose.editable.resume(); crudExpose.editable.resume();

View File

@ -1,5 +1,5 @@
import * as api from "./api"; import * as api from "./api";
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud";
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -31,7 +31,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
table: { table: {
editable: { editable: {
enabled: true, enabled: true,
mode: "row" mode: "row",
exclusive: true, //排他式激活
exclusiveEffect: "save" //排他式激活时,其他行的编辑状态的处理方式
} }
}, },
columns: { columns: {
@ -68,7 +70,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
form: { form: {
rules: { rules: {
async asyncValidator(context) { async asyncValidator(context) {
console.log("context", context); utils.logger.info("context", context);
return true; return true;
}, },
message: "远程校验测试" message: "远程校验测试"

View File

@ -5,7 +5,7 @@
行编辑模式 行编辑模式
<span class="sub">在表格内编辑每行数据</span> <span class="sub">在表格内编辑每行数据</span>
</div> </div>
<div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/api/expose.html">文档</a></div> <div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/api/crud-options/table.html#editable">文档</a></div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> <fs-crud ref="crudRef" v-bind="crudBinding">
<template #actionbar-right> <template #actionbar-right>
@ -18,7 +18,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud"; import {useFs, utils} from "@fast-crud/fast-crud";
export default defineComponent({ export default defineComponent({
name: "EditableRow", name: "EditableRow",
@ -28,14 +28,14 @@ export default defineComponent({
// //
onMounted(() => { onMounted(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
crudExpose.editable.enable(); // crudExpose.editable.enable({});
}); });
return { return {
crudBinding, crudBinding,
crudRef, crudRef,
log() { log() {
console.log("table data:", crudBinding.value.data, crudExpose.getTableData()); utils.logger.info("table data:", crudBinding.value.data, crudExpose.getTableData());
} }
}; };
} }

View File

@ -2,7 +2,7 @@ import * as api from "./api";
import { dict, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, UserPageQuery, UserPageRes, EditReq, DelReq, AddReq } from "@fast-crud/fast-crud"; import { dict, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, UserPageQuery, UserPageRes, EditReq, DelReq, AddReq } from "@fast-crud/fast-crud";
import EditableRowSub from "/@/views/crud/editable/sub-crud/row/index.vue"; import EditableRowSub from "/@/views/crud/editable/sub-crud/row/index.vue";
export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding,crudRef } = props.crudExpose; const { crudBinding, crudRef } = props.crudExpose;
const { crudExpose } = props; const { crudExpose } = props;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -32,7 +32,7 @@ export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet {
form: { form: {
wrapper: { wrapper: {
width: "80%" width: "80%"
}, }
}, },
columns: { columns: {
id: { id: {
@ -50,7 +50,7 @@ export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet {
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}), }),
form:{ form: {
rules: [{ required: true, message: "请选择状态" }] rules: [{ required: true, message: "请选择状态" }]
} }
}, },
@ -60,25 +60,25 @@ export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet {
form: { form: {
component: { component: {
name: EditableRowSub, name: EditableRowSub,
id:compute(({form})=>{ id: compute(({ form }) => {
return form.id return form.id;
}), }),
on:{ on: {
async saveMain({form}){ async saveMain({ form }) {
//保存主表 //保存主表
const formRef = crudExpose.getFormRef() const formRef = crudExpose.getFormRef();
const ret = await formRef.submit() const ret = await formRef.submit();
//将form改为编辑模式 //将form改为编辑模式
let formWrapperRef = crudExpose.getFormWrapperRef(); const formWrapperRef = crudExpose.getFormWrapperRef();
formWrapperRef.setFormData(ret.res) formWrapperRef.setFormData(ret.res);
crudRef.value.formWrapperRef.formOptions.mode = "edit" crudRef.value.formWrapperRef.formOptions.mode = "edit";
crudRef.value.formWrapperRef.title="编辑" crudRef.value.formWrapperRef.title = "编辑";
} }
} }
}, },
col: { col: {
span: 24 span: 24
}, }
}, },
column: { column: {
formatter: ({ row }) => { formatter: ({ row }) => {

View File

@ -18,7 +18,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted } from "vue"; import { defineComponent, onMounted } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs } from "@fast-crud/fast-crud"; import { useFs, utils } from "@fast-crud/fast-crud";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
export default defineComponent({ export default defineComponent({
@ -35,7 +35,7 @@ export default defineComponent({
crudBinding, crudBinding,
crudRef, crudRef,
log() { log() {
console.log("table data:", crudBinding.value.data); utils.logger.info("table data:", crudBinding.value.data);
} }
}; };
} }

View File

@ -1,16 +1,8 @@
import { import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
AddReq,
CreateCrudOptionsProps,
CreateCrudOptionsRet, DelReq,
dict,
EditReq,
UserPageQuery,
UserPageRes
} from "@fast-crud/fast-crud";
import * as api from "./api"; import * as api from "./api";
export default function ({ crudExpose,context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { crudBinding } = crudExpose; const { crudBinding } = crudExpose;
const {parentIdRef} = context const { parentIdRef } = context;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
}; };
@ -48,8 +40,8 @@ export default function ({ crudExpose,context }: CreateCrudOptionsProps): Create
}, },
search: { search: {
show: false, show: false,
initialForm:{ initialForm: {
parentId:parentIdRef parentId: parentIdRef
} }
}, },
toolbar: { toolbar: {
@ -63,7 +55,7 @@ export default function ({ crudExpose,context }: CreateCrudOptionsProps): Create
editable: { editable: {
enabled: true, enabled: true,
mode: "row", mode: "row",
activeDefault:false, activeDefault: false
} }
}, },
// pagination: { show: false, pageSize: 9999999 }, // pagination: { show: false, pageSize: 9999999 },
@ -83,8 +75,8 @@ export default function ({ crudExpose,context }: CreateCrudOptionsProps): Create
dict: dict({ dict: dict({
url: "/mock/dicts/OpenStatusEnum?single" url: "/mock/dicts/OpenStatusEnum?single"
}), }),
form:{ form: {
value:'1', value: "1"
} }
}, },
name: { name: {
@ -97,16 +89,16 @@ export default function ({ crudExpose,context }: CreateCrudOptionsProps): Create
] ]
} }
}, },
parentId:{ parentId: {
title: "父Id", title: "父Id",
type: "number", type: "number",
search:{ search: {
show:true, show: true
}, },
form:{ form: {
value:parentIdRef, value: parentIdRef,
component:{ component: {
disabled:true disabled: true
} }
} }
}, },

View File

@ -1,20 +1,16 @@
<template> <template>
<div class="sub-table"> <div class="sub-table">
<div v-if="id>0" style="height: 500px; position: relative"> <div v-if="id > 0" style="height: 500px; position: relative">
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> <fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</div> </div>
<div v-else> <div v-else><fs-button @click="saveMain"></fs-button> 保存后即可编辑子表</div>
<fs-button @click="saveMain"></fs-button> 保存后即可编辑子表
</div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, watch,ref } from "vue"; import { defineComponent, onMounted, watch, ref } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useFs, useUi } from "@fast-crud/fast-crud"; import { useFs, useUi, utils } from "@fast-crud/fast-crud";
export default defineComponent({ export default defineComponent({
name: "EditableSubCrudTarget", name: "EditableSubCrudTarget",
@ -22,50 +18,50 @@ export default defineComponent({
/** /**
* 主表id * 主表id
*/ */
id:{ id: {
type:Number, type: Number,
default:0 default: 0
}, },
disabled:{ disabled: {
type:Boolean, type: Boolean,
default:false default: false
},
readonly:{
type:Boolean,
default:false
}, },
readonly: {
type: Boolean,
default: false
}
}, },
emits: ["save-main"], emits: ["save-main"],
setup(props, ctx) { setup(props, ctx) {
const parentIdRef = ref(props.id) // eslint-disable-next-line vue/no-setup-props-destructure
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions ,context:{parentIdRef}}); const parentIdRef = ref(props.id);
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { parentIdRef } });
const { ui } = useUi(); const { ui } = useUi();
let formItemContext = ui.formItem.injectFormItemContext(); let formItemContext = ui.formItem.injectFormItemContext();
function emit(data:any) { function emit(data: any) {
console.log("emit:", data); utils.logger.info("emit:", data);
formItemContext.onBlur(); formItemContext.onBlur();
formItemContext.onChange(); formItemContext.onChange();
} }
function saveMain(){ function saveMain() {
ctx.emit("save-main",true) ctx.emit("save-main", true);
} }
watch( watch(
() => { () => {
return props.id; return props.id;
}, },
(value: any) => { (value: any) => {
if(value>0){ if (value > 0) {
crudExpose.setSearchFormData({ crudExpose.setSearchFormData({
form:{parentId:value}, form: { parentId: value },
mergeForm:true, mergeForm: true,
triggerSearch:true triggerSearch: true
}) });
parentIdRef.value = value parentIdRef.value = value;
} }
}, },
{ {
@ -76,24 +72,27 @@ export default defineComponent({
// //
onMounted(() => { onMounted(() => {
crudExpose.doRefresh(); crudExpose.doRefresh();
watch(()=>{ watch(
return props.disabled || props.readonly () => {
},(value)=>{ return props.disabled || props.readonly;
if(value){ },
crudBinding.value.table.editable.readonly=true (value) => {
crudBinding.value.actionbar.buttons.addRow.show=false if (value) {
crudBinding.value.rowHandle.show=false crudBinding.value.table.editable.readonly = true;
}else{ crudBinding.value.actionbar.buttons.addRow.show = false;
crudBinding.value.table.editable.readonly=false crudBinding.value.rowHandle.show = false;
crudBinding.value.actionbar.buttons.addRow.show=true } else {
crudBinding.value.rowHandle.show=true crudBinding.value.table.editable.readonly = false;
crudBinding.value.actionbar.buttons.addRow.show = true;
crudBinding.value.rowHandle.show = true;
}
},
{
immediate: true
} }
},{ );
immediate:true
})
}); });
return { return {
crudBinding, crudBinding,
crudRef, crudRef,

View File

@ -67,6 +67,10 @@ export default function (props: CreateCrudOptionsProps): CreateCrudOptionsRet {
}, },
col: { col: {
span: 24 span: 24
},
valueResolve({ form }) {
//重要,移除$editable_id字段返回干净的tableData数据
form.subTable = crudExpose.editable.getTableData(form.subTable);
} }
}, },
column: { column: {

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