refactor: 重构图片裁剪ReCropper组件,添加更多实用功能

This commit is contained in:
xiaoxian521 2023-06-25 18:33:46 +08:00
parent bf67e36731
commit b5996ed80b
23 changed files with 653 additions and 255 deletions

View File

@ -15,10 +15,13 @@ const include = [
"intro.js",
"vue-i18n",
"js-cookie",
"vue-tippy",
"cropperjs",
"jsbarcode",
"pinyin-pro",
"sortablejs",
"swiper/vue",
"mint-filter",
"md-editor-v3",
"@vueuse/core",
"vue3-danmaku",

View File

@ -73,7 +73,8 @@
"vue-json-pretty": "^2.2.4",
"vue-pdf-embed": "^1.1.6",
"vue-router": "^4.2.2",
"vue-types": "^5.0.4",
"vue-tippy": "^6.2.0",
"vue-types": "^5.1.0",
"vue-virtual-scroller": "2.0.0-beta.7",
"vue-waterfall-plugin-next": "^2.2.1",
"vue3-danmaku": "^1.4.0",
@ -82,8 +83,8 @@
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^17.6.5",
"@commitlint/config-conventional": "^17.6.5",
"@commitlint/cli": "^17.6.6",
"@commitlint/config-conventional": "^17.6.6",
"@iconify-icons/ep": "^1.2.12",
"@iconify-icons/ri": "^1.2.9",
"@iconify/vue": "^4.1.1",
@ -108,7 +109,7 @@
"cssnano": "^6.0.1",
"eslint": "^8.43.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.15.0",
"eslint-plugin-vue": "^9.15.1",
"husky": "^8.0.3",
"lint-staged": "^13.2.2",
"picocolors": "^1.0.0",
@ -120,9 +121,9 @@
"pretty-quick": "^3.1.3",
"rimraf": "^5.0.1",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.63.5",
"sass": "^1.63.6",
"sass-loader": "^13.3.2",
"stylelint": "^15.8.0",
"stylelint": "^15.9.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.2.0",
"stylelint-config-recommended": "^12.0.0",

298
pnpm-lock.yaml generated
View File

@ -2,8 +2,8 @@ lockfileVersion: 5.4
specifiers:
"@amap/amap-jsapi-loader": ^1.0.1
"@commitlint/cli": ^17.6.5
"@commitlint/config-conventional": ^17.6.5
"@commitlint/cli": ^17.6.6
"@commitlint/config-conventional": ^17.6.6
"@howdyjs/mouse-menu": ^2.0.7
"@iconify-icons/ep": ^1.2.12
"@iconify-icons/ri": ^1.2.9
@ -46,7 +46,7 @@ specifiers:
element-plus: ^2.3.7
eslint: ^8.43.0
eslint-plugin-prettier: ^4.2.1
eslint-plugin-vue: ^9.15.0
eslint-plugin-vue: ^9.15.1
husky: ^8.0.3
intro.js: ^7.0.1
js-cookie: ^3.0.5
@ -72,10 +72,10 @@ specifiers:
responsive-storage: ^2.2.0
rimraf: ^5.0.1
rollup-plugin-visualizer: ^5.9.2
sass: ^1.63.5
sass: ^1.63.6
sass-loader: ^13.3.2
sortablejs: ^1.15.0
stylelint: ^15.8.0
stylelint: ^15.9.0
stylelint-config-html: ^1.1.0
stylelint-config-recess-order: ^4.2.0
stylelint-config-recommended: ^12.0.0
@ -107,8 +107,9 @@ specifiers:
vue-json-pretty: ^2.2.4
vue-pdf-embed: ^1.1.6
vue-router: ^4.2.2
vue-tippy: ^6.2.0
vue-tsc: ^1.8.1
vue-types: ^5.0.4
vue-types: ^5.1.0
vue-virtual-scroller: 2.0.0-beta.7
vue-waterfall-plugin-next: ^2.2.1
vue3-danmaku: ^1.4.0
@ -161,7 +162,8 @@ dependencies:
vue-json-pretty: 2.2.4_vue@3.3.4
vue-pdf-embed: 1.1.6_vue@3.3.4
vue-router: 4.2.2_vue@3.3.4
vue-types: 5.0.4_vue@3.3.4
vue-tippy: 6.2.0_vue@3.3.4
vue-types: 5.1.0_vue@3.3.4
vue-virtual-scroller: 2.0.0-beta.7_vue@3.3.4
vue-waterfall-plugin-next: 2.2.1_vue@3.3.4
vue3-danmaku: 1.4.0_vue@3.3.4
@ -170,8 +172,8 @@ dependencies:
xlsx: 0.18.5
devDependencies:
"@commitlint/cli": 17.6.5
"@commitlint/config-conventional": 17.6.5
"@commitlint/cli": 17.6.6
"@commitlint/config-conventional": 17.6.6
"@iconify-icons/ep": 1.2.12
"@iconify-icons/ri": 1.2.9
"@iconify/vue": 4.1.1_vue@3.3.4
@ -190,13 +192,13 @@ devDependencies:
"@vitejs/plugin-vue": 4.2.3_vite@4.3.9+vue@3.3.4
"@vitejs/plugin-vue-jsx": 3.0.1_vite@4.3.9+vue@3.3.4
"@vue/eslint-config-prettier": 7.1.0_bxz4zaiplh63a3nbhxngrogoky
"@vue/eslint-config-typescript": 11.0.3_xatovp6glrmk2fdmmi35pvc4ke
"@vue/eslint-config-typescript": 11.0.3_5wcsfo7r24w3giceba2tzy7tfq
autoprefixer: 10.4.14_postcss@8.4.24
cloc: 2.11.0
cssnano: 6.0.1_postcss@8.4.24
eslint: 8.43.0
eslint-plugin-prettier: 4.2.1_bxz4zaiplh63a3nbhxngrogoky
eslint-plugin-vue: 9.15.0_eslint@8.43.0
eslint-plugin-vue: 9.15.1_eslint@8.43.0
husky: 8.0.3
lint-staged: 13.2.2
picocolors: 1.0.0
@ -208,24 +210,24 @@ devDependencies:
pretty-quick: 3.1.3_prettier@2.8.8
rimraf: 5.0.1
rollup-plugin-visualizer: 5.9.2
sass: 1.63.5
sass-loader: 13.3.2_sass@1.63.5
stylelint: 15.8.0
stylelint-config-html: 1.1.0_mvog3pcismoqiofxpbzhc46kxq
stylelint-config-recess-order: 4.2.0_stylelint@15.8.0
stylelint-config-recommended: 12.0.0_stylelint@15.8.0
stylelint-config-recommended-scss: 12.0.0_kljeyyq7v4k44dzugcnpkrggwa
stylelint-config-recommended-vue: 1.4.0_mvog3pcismoqiofxpbzhc46kxq
stylelint-config-standard: 33.0.0_stylelint@15.8.0
stylelint-config-standard-scss: 9.0.0_kljeyyq7v4k44dzugcnpkrggwa
stylelint-order: 6.0.3_stylelint@15.8.0
stylelint-prettier: 3.0.0_l3rlt3ch3sxnybjesonr3v7dca
stylelint-scss: 5.0.1_stylelint@15.8.0
sass: 1.63.6
sass-loader: 13.3.2_sass@1.63.6
stylelint: 15.9.0
stylelint-config-html: 1.1.0_43a2gdw3tlladwfsutfch6434a
stylelint-config-recess-order: 4.2.0_stylelint@15.9.0
stylelint-config-recommended: 12.0.0_stylelint@15.9.0
stylelint-config-recommended-scss: 12.0.0_kg6gx255czvkalpavedathyz5a
stylelint-config-recommended-vue: 1.4.0_43a2gdw3tlladwfsutfch6434a
stylelint-config-standard: 33.0.0_stylelint@15.9.0
stylelint-config-standard-scss: 9.0.0_kg6gx255czvkalpavedathyz5a
stylelint-order: 6.0.3_stylelint@15.9.0
stylelint-prettier: 3.0.0_di75xrvcz6maarrq4xt2dhjdje
stylelint-scss: 5.0.1_stylelint@15.9.0
svgo: 3.0.2
tailwindcss: 3.3.2
terser: 5.18.1
typescript: 5.0.4
vite: 4.3.9_pkrcaghuhlfdigptyvdajp2oau
vite: 4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi
vite-plugin-cdn-import: 0.3.5
vite-plugin-compression: 0.5.1_vite@4.3.9
vite-plugin-mock: 2.9.6_mockjs@1.1.0+vite@4.3.9
@ -798,16 +800,16 @@ packages:
}
dev: false
/@commitlint/cli/17.6.5:
/@commitlint/cli/17.6.6:
resolution:
{
integrity: sha512-3PQrWr/uo6lzF5k7n5QuosCYnzaxP9qGBp3jhWP0Vmsa7XA6wrl9ccPqfQyXpSbQE3zBROVO3TDqgPKe4tfmLQ==
integrity: sha512-sTKpr2i/Fjs9OmhU+beBxjPavpnLSqZaO6CzwKVq2Tc4UYVTMFgpKOslDhUBVlfAUBfjVO8ParxC/MXkIOevEA==
}
engines: { node: ">=v14" }
hasBin: true
dependencies:
"@commitlint/format": 17.4.4
"@commitlint/lint": 17.6.5
"@commitlint/lint": 17.6.6
"@commitlint/load": 17.5.0
"@commitlint/read": 17.5.1
"@commitlint/types": 17.4.4
@ -821,10 +823,10 @@ packages:
- "@swc/wasm"
dev: true
/@commitlint/config-conventional/17.6.5:
/@commitlint/config-conventional/17.6.6:
resolution:
{
integrity: sha512-Xl9H9KLl86NZm5CYNTNF9dcz1xelE/EbvhWIWcYxG/rn3UWYWdWmmnX2q6ZduNdLFSGbOxzUpIx61j5zxbeXxg==
integrity: sha512-phqPz3BDhfj49FUYuuZIuDiw+7T6gNAEy7Yew1IBHqSohVUCWOK2FXMSAExzS2/9X+ET93g0Uz83KjiHDOOFag==
}
engines: { node: ">=v14" }
dependencies:
@ -876,25 +878,25 @@ packages:
chalk: 4.1.2
dev: true
/@commitlint/is-ignored/17.6.5:
/@commitlint/is-ignored/17.6.6:
resolution:
{
integrity: sha512-CQvAPt9gX7cuUbMrIaIMKczfWJqqr6m8IlJs0F2zYwyyMTQ87QMHIj5jJ5HhOaOkaj6dvTMVGx8Dd1I4xgUuoQ==
integrity: sha512-4Fw875faAKO+2nILC04yW/2Vy/wlV3BOYCSQ4CEFzriPEprc1Td2LILmqmft6PDEK5Sr14dT9tEzeaZj0V56Gg==
}
engines: { node: ">=v14" }
dependencies:
"@commitlint/types": 17.4.4
semver: 7.5.0
semver: 7.5.2
dev: true
/@commitlint/lint/17.6.5:
/@commitlint/lint/17.6.6:
resolution:
{
integrity: sha512-BSJMwkE4LWXrOsiP9KoHG+/heSDfvOL/Nd16+ojTS/DX8HZr8dNl8l3TfVr/d/9maWD8fSegRGtBtsyGuugFrw==
integrity: sha512-5bN+dnHcRLkTvwCHYMS7Xpbr+9uNi0Kq5NR3v4+oPNx6pYXt8ACuw9luhM/yMgHYwW0ajIR20wkPAFkZLEMGmg==
}
engines: { node: ">=v14" }
dependencies:
"@commitlint/is-ignored": 17.6.5
"@commitlint/is-ignored": 17.6.6
"@commitlint/parse": 17.6.5
"@commitlint/rules": 17.6.5
"@commitlint/types": 17.4.4
@ -2023,15 +2025,15 @@ packages:
"@nodelib/fs.scandir": 2.1.5
fastq: 1.15.0
/@nuxt/kit/3.5.3:
/@nuxt/kit/3.6.0:
resolution:
{
integrity: sha512-QzoOGqa1zjKQfg7Y50TrrFAL9DhtIpYYs10gihcM1ISPrn9ROht+VEjqsaMvT+L8JuQbNf8wDYl8qzsdWGU29Q==
integrity: sha512-rqQYyWlhE42oWRQNR58KU1JYhoWryN78x8eYzFTHgalfpMjtPqZv2j9K4+hFRk0XLRUKnut4tE/3+UYyZ7ybVw==
}
engines: { node: ^14.18.0 || >=16.10.0 }
requiresBuild: true
dependencies:
"@nuxt/schema": 3.5.3
"@nuxt/schema": 3.6.0
c12: 1.4.2
consola: 3.1.0
defu: 6.1.2
@ -2044,7 +2046,7 @@ packages:
pathe: 1.1.1
pkg-types: 1.0.3
scule: 1.0.0
semver: 7.5.2
semver: 7.5.3
unctx: 2.3.1
unimport: 3.0.8
untyped: 1.3.2
@ -2054,10 +2056,10 @@ packages:
dev: false
optional: true
/@nuxt/schema/3.5.3:
/@nuxt/schema/3.6.0:
resolution:
{
integrity: sha512-Tnon4mYfJZmsCtx4NZ9A+qjwo4DcZ6tERpEhYBY81PX7AiJ+hFPBFR1qR32Tff66/qJjZg5UXj6H9AdzwEYr2w==
integrity: sha512-6/nq+W77JODDfhMBZTi7HCD3hT5oHegsasAzUnDmvwWuY1io7BXX9x2mDhL8E3LhVzQuN5vhi3GBgwHwCfdKEA==
}
engines: { node: ^14.18.0 || >=16.10.0 }
dependencies:
@ -2086,6 +2088,13 @@ packages:
dev: true
optional: true
/@popperjs/core/2.11.8:
resolution:
{
integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
}
dev: false
/@pureadmin/descriptions/1.1.1_element-plus@2.3.7:
resolution:
{
@ -2567,7 +2576,7 @@ packages:
grapheme-splitter: 1.0.4
ignore: 5.2.4
natural-compare-lite: 1.4.0
semver: 7.5.2
semver: 7.5.3
tsutils: 3.21.0_typescript@5.0.4
typescript: 5.0.4
transitivePeerDependencies:
@ -2656,7 +2665,7 @@ packages:
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.2
semver: 7.5.3
tsutils: 3.21.0_typescript@5.0.4
typescript: 5.0.4
transitivePeerDependencies:
@ -2680,7 +2689,7 @@ packages:
"@typescript-eslint/typescript-estree": 5.60.0_typescript@5.0.4
eslint: 8.43.0
eslint-scope: 5.1.1
semver: 7.5.2
semver: 7.5.3
transitivePeerDependencies:
- supports-color
- typescript
@ -2765,8 +2774,8 @@ packages:
dependencies:
"@babel/core": 7.22.5
"@babel/plugin-transform-typescript": 7.22.5_@babel+core@7.22.5
"@vue/babel-plugin-jsx": 1.1.1_@babel+core@7.22.5
vite: 4.3.9_pkrcaghuhlfdigptyvdajp2oau
"@vue/babel-plugin-jsx": 1.1.3_@babel+core@7.22.5
vite: 4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi
vue: 3.3.4
transitivePeerDependencies:
- supports-color
@ -2782,7 +2791,7 @@ packages:
vite: ^4.0.0
vue: ^3.2.25
dependencies:
vite: 4.3.9_pkrcaghuhlfdigptyvdajp2oau
vite: 4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi
vue: 3.3.4
dev: true
@ -2813,30 +2822,32 @@ packages:
"@volar/language-core": 1.7.8
dev: true
/@vue/babel-helper-vue-transform-on/1.0.2:
/@vue/babel-helper-vue-transform-on/1.1.3:
resolution:
{
integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==
integrity: sha512-iSaE7+1+/tPp79XnvsAVjaCjuY7dHjfsArPozi+1USJ1A5lf5JUovCP90Hbc+L9BUSHGlXMEYuQrL2vS3Yz9ow==
}
dev: true
/@vue/babel-plugin-jsx/1.1.1_@babel+core@7.22.5:
/@vue/babel-plugin-jsx/1.1.3_@babel+core@7.22.5:
resolution:
{
integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==
integrity: sha512-q4ekWt6KcWmM7GNTZjBO53EOM/5uczu7q1Ks39Sz0d0PJFJ+oNi6xyu17WZ/uoSi/s81ouq99G18cLoEX9X1OA==
}
peerDependencies:
"@babel/core": ^7.0.0-0
dependencies:
"@babel/core": 7.22.5
"@babel/helper-module-imports": 7.22.5
"@babel/plugin-syntax-jsx": 7.22.5_@babel+core@7.22.5
"@babel/template": 7.22.5
"@babel/traverse": 7.22.5
"@babel/types": 7.22.5
"@vue/babel-helper-vue-transform-on": 1.0.2
"@vue/babel-helper-vue-transform-on": 1.1.3
camelcase: 6.3.0
html-tags: 3.3.1
svg-tags: 1.0.0
transitivePeerDependencies:
- "@babel/core"
- supports-color
dev: true
@ -2907,7 +2918,7 @@ packages:
prettier: 2.8.8
dev: true
/@vue/eslint-config-typescript/11.0.3_xatovp6glrmk2fdmmi35pvc4ke:
/@vue/eslint-config-typescript/11.0.3_5wcsfo7r24w3giceba2tzy7tfq:
resolution:
{
integrity: sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==
@ -2924,7 +2935,7 @@ packages:
"@typescript-eslint/eslint-plugin": 5.60.0_i6u37blvulxlszyhkflwwnyave
"@typescript-eslint/parser": 5.60.0_fsssjpk4ezl7mpaxdgpssv73ie
eslint: 8.43.0
eslint-plugin-vue: 9.15.0_eslint@8.43.0
eslint-plugin-vue: 9.15.1_eslint@8.43.0
typescript: 5.0.4
vue-eslint-parser: 9.3.1_eslint@8.43.0
transitivePeerDependencies:
@ -2947,7 +2958,7 @@ packages:
"@vue/compiler-dom": 3.3.4
"@vue/reactivity": 3.3.4
"@vue/shared": 3.3.4
minimatch: 9.0.1
minimatch: 9.0.2
muggle-string: 0.3.1
typescript: 5.0.4
vue-template-compiler: 2.7.14
@ -3082,7 +3093,7 @@ packages:
style-value-types: 5.1.2
vue: 3.3.4
optionalDependencies:
"@nuxt/kit": 3.5.3
"@nuxt/kit": 3.6.0
transitivePeerDependencies:
- "@vue/composition-api"
- rollup
@ -3665,7 +3676,7 @@ packages:
postcss: ^8.1.0
dependencies:
browserslist: 4.21.9
caniuse-lite: 1.0.30001506
caniuse-lite: 1.0.30001507
fraction.js: 4.2.0
normalize-range: 0.1.2
picocolors: 1.0.0
@ -3855,8 +3866,8 @@ packages:
engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
hasBin: true
dependencies:
caniuse-lite: 1.0.30001506
electron-to-chromium: 1.4.435
caniuse-lite: 1.0.30001507
electron-to-chromium: 1.4.440
node-releases: 2.0.12
update-browserslist-db: 1.0.11_browserslist@4.21.9
@ -3971,15 +3982,15 @@ packages:
}
dependencies:
browserslist: 4.21.9
caniuse-lite: 1.0.30001506
caniuse-lite: 1.0.30001507
lodash.memoize: 4.1.2
lodash.uniq: 4.5.0
dev: true
/caniuse-lite/1.0.30001506:
/caniuse-lite/1.0.30001507:
resolution:
{
integrity: sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==
integrity: sha512-SFpUDoSLCaE5XYL2jfqe9ova/pbQHEmbheDf5r4diNwbAgR3qxM9NQtfsiSscjqoya5K7kFcHPUQ+VsUkIJR4A==
}
/cfb/1.2.2:
@ -5156,10 +5167,10 @@ packages:
- "@vue/composition-api"
dev: false
/electron-to-chromium/1.4.435:
/electron-to-chromium/1.4.440:
resolution:
{
integrity: sha512-B0CBWVFhvoQCW/XtjRzgrmqcgVWg6RXOEM/dK59+wFV93BFGR6AeNKc4OyhM+T3IhJaOOG8o/V+33Y2mwJWtzw==
integrity: sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==
}
/element-plus/2.3.7_vue@3.3.4:
@ -5469,10 +5480,10 @@ packages:
prettier-linter-helpers: 1.0.0
dev: true
/eslint-plugin-vue/9.15.0_eslint@8.43.0:
/eslint-plugin-vue/9.15.1_eslint@8.43.0:
resolution:
{
integrity: sha512-XYzpK6e2REli100+6iCeBA69v6Sm0D/yK2FZP+fCeNt0yH/m82qZQq+ztseyV0JsKdhFysuSEzeE1yCmSC92BA==
integrity: sha512-CJE/oZOslvmAR9hf8SClTdQ9JLweghT6JCBQNrT2Iel1uVw0W0OLJxzvPd6CxmABKCvLrtyDnqGV37O7KQv6+A==
}
engines: { node: ^14.17.0 || >=16.0.0 }
peerDependencies:
@ -5483,7 +5494,7 @@ packages:
natural-compare: 1.4.0
nth-check: 2.1.1
postcss-selector-parser: 6.0.13
semver: 7.5.2
semver: 7.5.3
vue-eslint-parser: 9.3.1_eslint@8.43.0
xml-name-validator: 4.0.0
transitivePeerDependencies:
@ -6168,17 +6179,17 @@ packages:
is-glob: 4.0.3
dev: true
/glob/10.2.7:
/glob/10.3.0:
resolution:
{
integrity: sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==
integrity: sha512-AQ1/SB9HH0yCx1jXAT4vmCbTOPe5RQ+kCurjbel5xSCGhebumUv+GJZfa1rEqor3XIViqwSEmlkZCQD43RWrBg==
}
engines: { node: ">=16 || 14 >=14.17" }
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.2.1
minimatch: 9.0.1
minimatch: 9.0.2
minipass: 6.0.2
path-scurry: 1.9.2
dev: true
@ -7401,7 +7412,7 @@ packages:
jest-util: 27.5.1
natural-compare: 1.4.0
pretty-format: 27.5.1
semver: 7.5.2
semver: 7.5.3
transitivePeerDependencies:
- supports-color
dev: false
@ -8250,10 +8261,10 @@ packages:
dependencies:
brace-expansion: 1.1.11
/minimatch/9.0.1:
/minimatch/9.0.2:
resolution:
{
integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==
integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==
}
engines: { node: ">=16 || 14 >=14.17" }
dependencies:
@ -8513,7 +8524,7 @@ packages:
dependencies:
hosted-git-info: 4.1.0
is-core-module: 2.12.1
semver: 7.5.2
semver: 7.5.3
validate-npm-package-license: 3.0.4
dev: true
@ -10305,7 +10316,7 @@ packages:
engines: { node: ">=14" }
hasBin: true
dependencies:
glob: 10.2.7
glob: 10.3.0
dev: true
/rollup-plugin-external-globals/0.6.1:
@ -10344,10 +10355,10 @@ packages:
yargs: 17.7.2
dev: true
/rollup/3.25.1:
/rollup/3.25.2:
resolution:
{
integrity: sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==
integrity: sha512-VLnkxZMDr3jpxgtmS8pQZ0UvhslmF4ADq/9w4erkctbgjCqLW9oa89fJuXEs4ZmgyoF7Dm8rMDKSS5b5u2hHUg==
}
engines: { node: ">=14.18.0", npm: ">=8.0.0" }
hasBin: true
@ -10394,7 +10405,7 @@ packages:
}
dev: false
/sass-loader/13.3.2_sass@1.63.5:
/sass-loader/13.3.2_sass@1.63.6:
resolution:
{
integrity: sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==
@ -10419,13 +10430,13 @@ packages:
optional: true
dependencies:
neo-async: 2.6.2
sass: 1.63.5
sass: 1.63.6
dev: true
/sass/1.63.5:
/sass/1.63.6:
resolution:
{
integrity: sha512-Q6c5gs482oezdAp+0fWF9cRisvpy7yfYb64knID0OE8AnMgtkluRPfpGMFjeD4/+M4+6QpJZCU6JRSxbjiktkg==
integrity: sha512-MJuxGMHzaOW7ipp+1KdELtqKbfAWbH7OLIdoSMnVe3EXPMTmxTmlaZDCTsgIpPCs3w99lLo9/zDKkOrJuT5byw==
}
engines: { node: ">=14.0.0" }
hasBin: true
@ -10477,10 +10488,10 @@ packages:
}
hasBin: true
/semver/7.5.0:
/semver/7.5.2:
resolution:
{
integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==
integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==
}
engines: { node: ">=10" }
hasBin: true
@ -10488,10 +10499,10 @@ packages:
lru-cache: 6.0.0
dev: true
/semver/7.5.2:
/semver/7.5.3:
resolution:
{
integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==
integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==
}
engines: { node: ">=10" }
hasBin: true
@ -10986,7 +10997,7 @@ packages:
postcss-selector-parser: 6.0.13
dev: true
/stylelint-config-html/1.1.0_mvog3pcismoqiofxpbzhc46kxq:
/stylelint-config-html/1.1.0_43a2gdw3tlladwfsutfch6434a:
resolution:
{
integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==
@ -10997,10 +11008,10 @@ packages:
stylelint: ">=14.0.0"
dependencies:
postcss-html: 1.5.0
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint-config-recess-order/4.2.0_stylelint@15.8.0:
/stylelint-config-recess-order/4.2.0_stylelint@15.9.0:
resolution:
{
integrity: sha512-cWC66tUx74OgurUQaTAH4iJ4JbyisMwlJH8BO/oxglDLZBUNFggjwPFVtgsmd8rS+bUfm7sPlRrF00iAihESwA==
@ -11008,11 +11019,11 @@ packages:
peerDependencies:
stylelint: ">=15"
dependencies:
stylelint: 15.8.0
stylelint-order: 6.0.3_stylelint@15.8.0
stylelint: 15.9.0
stylelint-order: 6.0.3_stylelint@15.9.0
dev: true
/stylelint-config-recommended-scss/11.0.0_kljeyyq7v4k44dzugcnpkrggwa:
/stylelint-config-recommended-scss/11.0.0_kg6gx255czvkalpavedathyz5a:
resolution:
{
integrity: sha512-EDghTDU7aOv2LTsRZvcT1w8mcjUaMhuy+t38iV5I/0Qiu6ixdkRwhLEMul3K/fnB2v9Nwqvb3xpvJfPH+HduDw==
@ -11026,12 +11037,12 @@ packages:
dependencies:
postcss: 8.4.24
postcss-scss: 4.0.6_postcss@8.4.24
stylelint: 15.8.0
stylelint-config-recommended: 12.0.0_stylelint@15.8.0
stylelint-scss: 4.7.0_stylelint@15.8.0
stylelint: 15.9.0
stylelint-config-recommended: 12.0.0_stylelint@15.9.0
stylelint-scss: 4.7.0_stylelint@15.9.0
dev: true
/stylelint-config-recommended-scss/12.0.0_kljeyyq7v4k44dzugcnpkrggwa:
/stylelint-config-recommended-scss/12.0.0_kg6gx255czvkalpavedathyz5a:
resolution:
{
integrity: sha512-5Bb2mlGy6WLa30oNeKpZvavv2lowJUsUJO25+OA68GFTemlwd1zbFsL7q0bReKipOSU3sG47hKneZ6Nd+ctrFA==
@ -11045,12 +11056,12 @@ packages:
dependencies:
postcss: 8.4.24
postcss-scss: 4.0.6_postcss@8.4.24
stylelint: 15.8.0
stylelint-config-recommended: 12.0.0_stylelint@15.8.0
stylelint-scss: 5.0.1_stylelint@15.8.0
stylelint: 15.9.0
stylelint-config-recommended: 12.0.0_stylelint@15.9.0
stylelint-scss: 5.0.1_stylelint@15.9.0
dev: true
/stylelint-config-recommended-vue/1.4.0_mvog3pcismoqiofxpbzhc46kxq:
/stylelint-config-recommended-vue/1.4.0_43a2gdw3tlladwfsutfch6434a:
resolution:
{
integrity: sha512-DVJqyX2KvMCn9U0+keL12r7xlsH26K4Vg8NrIZuq5MoF7g82DpMp326Om4E0Q+Il1o+bTHuUyejf2XAI0iD04Q==
@ -11061,13 +11072,13 @@ packages:
stylelint: ">=14.0.0"
dependencies:
postcss-html: 1.5.0
semver: 7.5.2
stylelint: 15.8.0
stylelint-config-html: 1.1.0_mvog3pcismoqiofxpbzhc46kxq
stylelint-config-recommended: 12.0.0_stylelint@15.8.0
semver: 7.5.3
stylelint: 15.9.0
stylelint-config-html: 1.1.0_43a2gdw3tlladwfsutfch6434a
stylelint-config-recommended: 12.0.0_stylelint@15.9.0
dev: true
/stylelint-config-recommended/12.0.0_stylelint@15.8.0:
/stylelint-config-recommended/12.0.0_stylelint@15.9.0:
resolution:
{
integrity: sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ==
@ -11075,10 +11086,10 @@ packages:
peerDependencies:
stylelint: ^15.5.0
dependencies:
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint-config-standard-scss/9.0.0_kljeyyq7v4k44dzugcnpkrggwa:
/stylelint-config-standard-scss/9.0.0_kg6gx255czvkalpavedathyz5a:
resolution:
{
integrity: sha512-yPKpJsrZn4ybuQZx/DkEHuCjw7pJginErE/47dFhCnrvD48IJ4UYec8tSiCuJWMA3HRjbIa3nh5ZeSauDGuVAg==
@ -11091,12 +11102,12 @@ packages:
optional: true
dependencies:
postcss: 8.4.24
stylelint: 15.8.0
stylelint-config-recommended-scss: 11.0.0_kljeyyq7v4k44dzugcnpkrggwa
stylelint-config-standard: 33.0.0_stylelint@15.8.0
stylelint: 15.9.0
stylelint-config-recommended-scss: 11.0.0_kg6gx255czvkalpavedathyz5a
stylelint-config-standard: 33.0.0_stylelint@15.9.0
dev: true
/stylelint-config-standard/33.0.0_stylelint@15.8.0:
/stylelint-config-standard/33.0.0_stylelint@15.9.0:
resolution:
{
integrity: sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg==
@ -11104,11 +11115,11 @@ packages:
peerDependencies:
stylelint: ^15.5.0
dependencies:
stylelint: 15.8.0
stylelint-config-recommended: 12.0.0_stylelint@15.8.0
stylelint: 15.9.0
stylelint-config-recommended: 12.0.0_stylelint@15.9.0
dev: true
/stylelint-order/6.0.3_stylelint@15.8.0:
/stylelint-order/6.0.3_stylelint@15.9.0:
resolution:
{
integrity: sha512-1j1lOb4EU/6w49qZeT2SQVJXm0Ht+Qnq9GMfUa3pMwoyojIWfuA+JUDmoR97Bht1RLn4ei0xtLGy87M7d29B1w==
@ -11118,10 +11129,10 @@ packages:
dependencies:
postcss: 8.4.24
postcss-sorting: 8.0.2_postcss@8.4.24
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint-prettier/3.0.0_l3rlt3ch3sxnybjesonr3v7dca:
/stylelint-prettier/3.0.0_di75xrvcz6maarrq4xt2dhjdje:
resolution:
{
integrity: sha512-kIks1xw6np0zElokMT2kP6ar3S4MBoj6vUtPJuND1pFELMpZxVS/0uHPR4HDAVn0WAD3I5oF0IA3qBFxBpMkLg==
@ -11133,10 +11144,10 @@ packages:
dependencies:
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint-scss/4.7.0_stylelint@15.8.0:
/stylelint-scss/4.7.0_stylelint@15.9.0:
resolution:
{
integrity: sha512-TSUgIeS0H3jqDZnby1UO1Qv3poi1N8wUYIJY6D1tuUq2MN3lwp/rITVo0wD+1SWTmRm0tNmGO0b7nKInnqF6Hg==
@ -11148,10 +11159,10 @@ packages:
postcss-resolve-nested-selector: 0.1.1
postcss-selector-parser: 6.0.13
postcss-value-parser: 4.2.0
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint-scss/5.0.1_stylelint@15.8.0:
/stylelint-scss/5.0.1_stylelint@15.9.0:
resolution:
{
integrity: sha512-n87iCRZrr2J7//I/QFsDXxFLnHKw633U4qvWZ+mOW6KDAp/HLj06H+6+f9zOuTYy+MdGdTuCSDROCpQIhw5fvQ==
@ -11163,13 +11174,13 @@ packages:
postcss-resolve-nested-selector: 0.1.1
postcss-selector-parser: 6.0.13
postcss-value-parser: 4.2.0
stylelint: 15.8.0
stylelint: 15.9.0
dev: true
/stylelint/15.8.0:
/stylelint/15.9.0:
resolution:
{
integrity: sha512-x9qBk84F3MEjMEUNCE7MtWmfj9G9y5XzJ0cpQeJdy2l/IoqjC8Ih0N0ytmOTnXE4Yv0J7I1cmVRQUVNSPCxTsA==
integrity: sha512-sXtAZi64CllWr6A+8ymDWnlIaYwuAa7XRmGnJxLQXFNnLjd3Izm4HAD+loKVaZ7cpK6SLxhAUX1lwPJKGCn0mg==
}
engines: { node: ^14.13.1 || >=16.0.0 }
hasBin: true
@ -11525,6 +11536,15 @@ packages:
}
dev: false
/tippy.js/6.3.7:
resolution:
{
integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
}
dependencies:
"@popperjs/core": 2.11.8
dev: false
/tmpl/1.0.5:
resolution:
{
@ -12006,7 +12026,7 @@ packages:
chalk: 4.1.2
debug: 4.3.4
fs-extra: 10.1.0
vite: 4.3.9_pkrcaghuhlfdigptyvdajp2oau
vite: 4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi
transitivePeerDependencies:
- supports-color
dev: true
@ -12031,7 +12051,7 @@ packages:
fast-glob: 3.2.12
mockjs: 1.1.0
path-to-regexp: 6.2.1
vite: 4.3.9_pkrcaghuhlfdigptyvdajp2oau
vite: 4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi
transitivePeerDependencies:
- rollup
- supports-color
@ -12054,7 +12074,7 @@ packages:
svgo: 3.0.2
dev: true
/vite/4.3.9_pkrcaghuhlfdigptyvdajp2oau:
/vite/4.3.9_l6lbxg3wmltw2cpo2xi56qo4mi:
resolution:
{
integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==
@ -12085,8 +12105,8 @@ packages:
"@types/node": 18.16.18
esbuild: 0.17.19
postcss: 8.4.24
rollup: 3.25.1
sass: 1.63.5
rollup: 3.25.2
sass: 1.63.6
terser: 5.18.1
optionalDependencies:
fsevents: 2.3.2
@ -12126,7 +12146,7 @@ packages:
espree: 9.5.2
esquery: 1.5.0
lodash: 4.17.21
semver: 7.5.2
semver: 7.5.3
transitivePeerDependencies:
- supports-color
dev: true
@ -12213,6 +12233,18 @@ packages:
he: 1.2.0
dev: true
/vue-tippy/6.2.0_vue@3.3.4:
resolution:
{
integrity: sha512-UytUItp2ZDLXUwAotmioz02uLQoaAl5iVM+5yKsQWrXr29L9ivavtkL684FqbmOfbeCypBw+rVKsXhwdnCt/Cg==
}
peerDependencies:
vue: ^3.2.0
dependencies:
tippy.js: 6.3.7
vue: 3.3.4
dev: false
/vue-tsc/1.8.1_typescript@5.0.4:
resolution:
{
@ -12224,14 +12256,14 @@ packages:
dependencies:
"@vue/language-core": 1.8.1_typescript@5.0.4
"@vue/typescript": 1.8.1_typescript@5.0.4
semver: 7.5.2
semver: 7.5.3
typescript: 5.0.4
dev: true
/vue-types/5.0.4_vue@3.3.4:
/vue-types/5.1.0_vue@3.3.4:
resolution:
{
integrity: sha512-ksYUQpvhk1Xl/K43OPkcm54VcX4tvxQoNYjYLk+n45NOocDsg9+DnviPq/KfDLjGs4P23iAosFPR8JSzuh9IPA==
integrity: sha512-oCSq5MawTli+Jqaf07sCZgJr/FcDCFF5U/VE4WG58S3EAPxqi8nZlbrQ9I50rD5MZdJ3fjgi/IX1tVLL3QUFzA==
}
engines: { node: ">=14.0.0" }
peerDependencies:

View File

@ -0,0 +1,11 @@
@import "cropperjs/dist/cropper.css";
@import "tippy.js/dist/tippy.css";
@import "tippy.js/themes/light.css";
@import "tippy.js/animations/perspective.css";
.re-circled {
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
}

View File

@ -1,22 +1,41 @@
import "./circled.css";
import Cropper from "cropperjs";
import { ElUpload } from "element-plus";
import type { CSSProperties } from "vue";
import { useResizeObserver } from "@vueuse/core";
import { longpress } from "@/directives/longpress";
import { useTippy, directive as tippy } from "vue-tippy";
import { delay, debounce, isArray, downloadByBase64 } from "@pureadmin/utils";
import {
defineComponent,
onMounted,
nextTick,
ref,
unref,
computed,
PropType
PropType,
onMounted,
onUnmounted,
defineComponent
} from "vue";
import { useAttrs } from "@pureadmin/utils";
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
import {
Reload,
Upload,
ArrowH,
ArrowV,
ArrowUp,
ArrowDown,
ArrowLeft,
ChangeIcon,
ArrowRight,
RotateLeft,
SearchPlus,
RotateRight,
SearchMinus,
DownloadIcon
} from "./svg";
type Options = Cropper.Options;
const defaultOptions: Cropper.Options = {
aspectRatio: 16 / 9,
const defaultOptions: Options = {
aspectRatio: 1,
zoomable: true,
zoomOnTouch: true,
zoomOnWheel: true,
@ -39,110 +58,382 @@ const defaultOptions: Cropper.Options = {
};
const props = {
src: {
type: String,
required: true
},
alt: {
type: String
},
width: {
type: [String, Number],
default: ""
},
height: {
type: [String, Number],
default: "360px"
},
src: { type: String, required: true },
alt: { type: String },
circled: { type: Boolean, default: false },
realTimePreview: { type: Boolean, default: true },
height: { type: [String, Number], default: "360px" },
crossorigin: {
type: String || Object,
type: String as PropType<"" | "anonymous" | "use-credentials" | undefined>,
default: undefined
},
imageStyle: {
type: Object as PropType<CSSProperties>,
default() {
return {};
}
},
options: {
type: Object as PropType<Options>,
default() {
return {};
}
}
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
options: { type: Object as PropType<Options>, default: () => ({}) }
};
export default defineComponent({
name: "ReCropper",
props,
setup(props) {
const cropper: any = ref<Nullable<Cropper>>(null);
const imgElRef = ref();
setup(props, { attrs, emit }) {
const tippyElRef = ref<ElRef<HTMLImageElement>>();
const imgElRef = ref<ElRef<HTMLImageElement>>();
const cropper = ref<Nullable<Cropper>>();
const isReady = ref(false);
const imgBase64 = ref();
const inCircled = ref(props.circled);
const inSrc = ref(props.src);
let scaleX = 1;
let scaleY = 1;
const isReady = ref<boolean>(false);
const debounceRealTimeCroppered = debounce(realTimeCroppered, 80);
const getImageStyle = computed((): CSSProperties => {
return {
height: props.height,
width: props.width,
maxWidth: "100%",
...props.imageStyle
};
});
const getWrapperStyle = computed((): CSSProperties => {
const { height, width } = props;
return {
width: `${width}`.replace(/px/, "") + "px",
height: `${height}`.replace(/px/, "") + "px"
};
const getClass = computed(() => {
return [
attrs.class,
{
["re-circled"]: inCircled.value
}
];
});
function init() {
const iconClass = computed(() => {
return [
"p-[6px]",
"h-[30px]",
"w-[30px]",
"outline-none",
"rounded-[4px]",
"cursor-pointer",
"hover:bg-[rgba(0,0,0,0.06)]"
];
});
const getWrapperStyle = computed((): CSSProperties => {
return { height: `${props.height}`.replace(/px/, "") + "px" };
});
onMounted(init);
onUnmounted(() => {
cropper.value?.destroy();
});
useResizeObserver(tippyElRef, () => {
handCropper("reset");
});
async function init() {
const imgEl = unref(imgElRef);
if (!imgEl) {
return;
}
if (!imgEl) return;
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true;
realTimeCroppered();
delay(400).then(() => emit("readied", cropper.value));
},
crop() {
debounceRealTimeCroppered();
},
zoom() {
debounceRealTimeCroppered();
},
cropmove() {
debounceRealTimeCroppered();
},
...props.options
});
}
onMounted(() => {
nextTick(() => {
init();
function realTimeCroppered() {
props.realTimePreview && croppered();
}
function croppered() {
if (!cropper.value) return;
const canvas = inCircled.value
? getRoundedCanvas()
: cropper.value.getCroppedCanvas();
// https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toBlob
canvas.toBlob(blob => {
if (!blob) return;
const fileReader: FileReader = new FileReader();
fileReader.readAsDataURL(blob);
fileReader.onloadend = e => {
if (!e.target?.result || !blob) return;
imgBase64.value = e.target.result;
emit("cropper", {
base64: e.target.result,
blob,
info: { size: blob.size, ...cropper.value.getData() }
});
};
fileReader.onerror = () => {
emit("error");
};
});
}
function getRoundedCanvas() {
const sourceCanvas = cropper.value!.getCroppedCanvas();
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d")!;
const width = sourceCanvas.width;
const height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = "destination-in";
context.beginPath();
context.arc(
width / 2,
height / 2,
Math.min(width, height) / 2,
0,
2 * Math.PI,
true
);
context.fill();
return canvas;
}
function handCropper(event: string, arg?: number | Array<number>) {
if (event === "scaleX") {
scaleX = arg = scaleX === -1 ? 1 : -1;
}
if (event === "scaleY") {
scaleY = arg = scaleY === -1 ? 1 : -1;
}
arg && isArray(arg)
? cropper.value?.[event]?.(...arg)
: cropper.value?.[event]?.(arg);
}
function beforeUpload(file) {
const reader = new FileReader();
reader.readAsDataURL(file);
inSrc.value = "";
reader.onload = e => {
inSrc.value = e.target?.result as string;
};
reader.onloadend = () => {
init();
};
return false;
}
const menuContent = defineComponent({
directives: {
tippy,
longpress
},
setup() {
return () => (
<div class="flex flex-wrap w-[60px] justify-between">
<ElUpload
accept="image/*"
show-file-list={false}
before-upload={beforeUpload}
>
<Upload
class={iconClass.value}
v-tippy={{
content: "上传",
placement: "left-start"
}}
/>
</ElUpload>
<DownloadIcon
class={iconClass.value}
v-tippy={{
content: "下载",
placement: "right-start"
}}
onClick={() => downloadByBase64(imgBase64.value, "cropping.png")}
/>
<ChangeIcon
class={iconClass.value}
v-tippy={{
content: "圆形、矩形裁剪",
placement: "left-start"
}}
onClick={() => {
inCircled.value = !inCircled.value;
realTimeCroppered();
}}
/>
<Reload
class={iconClass.value}
v-tippy={{
content: "重置",
placement: "right-start"
}}
onClick={() => handCropper("reset")}
/>
<ArrowUp
class={iconClass.value}
v-tippy={{
content: "上移(可长按)",
placement: "left-start"
}}
v-longpress={[() => handCropper("move", [0, -10]), "0:100"]}
/>
<ArrowDown
class={iconClass.value}
v-tippy={{
content: "下移(可长按)",
placement: "right-start"
}}
v-longpress={[() => handCropper("move", [0, 10]), "0:100"]}
/>
<ArrowLeft
class={iconClass.value}
v-tippy={{
content: "左移(可长按)",
placement: "left-start"
}}
v-longpress={[() => handCropper("move", [-10, 0]), "0:100"]}
/>
<ArrowRight
class={iconClass.value}
v-tippy={{
content: "右移(可长按)",
placement: "right-start"
}}
v-longpress={[() => handCropper("move", [10, 0]), "0:100"]}
/>
<ArrowH
class={iconClass.value}
v-tippy={{
content: "水平翻转",
placement: "left-start"
}}
onClick={() => handCropper("scaleX", -1)}
/>
<ArrowV
class={iconClass.value}
v-tippy={{
content: "垂直翻转",
placement: "right-start"
}}
onClick={() => handCropper("scaleY", -1)}
/>
<RotateLeft
class={iconClass.value}
v-tippy={{
content: "逆时针旋转",
placement: "left-start"
}}
onClick={() => handCropper("rotate", -45)}
/>
<RotateRight
class={iconClass.value}
v-tippy={{
content: "顺时针旋转",
placement: "right-start"
}}
onClick={() => handCropper("rotate", 45)}
/>
<SearchPlus
class={iconClass.value}
v-tippy={{
content: "放大(可长按)",
placement: "left-start"
}}
v-longpress={[() => handCropper("zoom", 0.1), "0:100"]}
/>
<SearchMinus
class={iconClass.value}
v-tippy={{
content: "缩小(可长按)",
placement: "right-start"
}}
v-longpress={[() => handCropper("zoom", -0.1), "0:100"]}
/>
</div>
);
}
});
function onContextmenu(event) {
event.preventDefault();
const { show, setProps } = useTippy(tippyElRef, {
content: menuContent,
arrow: false,
theme: "light",
trigger: "manual",
interactive: true,
appendTo: "parent",
// hideOnClick: false,
animation: "perspective",
placement: "bottom-start"
});
setProps({
getReferenceClientRect: () => ({
width: 0,
height: 0,
top: event.clientY,
bottom: event.clientY,
left: event.clientX,
right: event.clientX
})
});
show();
}
return {
inSrc,
props,
imgElRef,
cropper,
tippyElRef,
getClass,
getWrapperStyle,
getImageStyle
getImageStyle,
isReady,
croppered,
onContextmenu
};
},
render() {
return (
<>
<div
class={useAttrs({ excludeListeners: true, excludeKeys: ["class"] })}
style={this.getWrapperStyle}
>
<img
ref="imgElRef"
src={this.props.src}
alt={this.props.alt}
crossorigin={this.props.crossorigin}
style={this.getImageStyle}
/>
</div>
</>
);
const {
inSrc,
isReady,
getClass,
getImageStyle,
onContextmenu,
getWrapperStyle
} = this;
const { alt, crossorigin } = this.props;
return inSrc ? (
<div
ref="tippyElRef"
class={getClass}
style={getWrapperStyle}
onContextmenu={event => onContextmenu(event)}
>
<img
v-show={isReady}
ref="imgElRef"
style={getImageStyle}
src={inSrc}
alt={alt}
crossorigin={crossorigin}
/>
</div>
) : null;
}
});

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861a31.96 31.96 0 0 0 48.3 0L868 478.5c4.5-5.2.8-13.2-6-13.2z"/></svg>

After

Width:  |  Height:  |  Size: 347 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path d="m296.992 216.992-272 272L3.008 512l21.984 23.008 272 272 46.016-46.016L126.016 544h772L680.992 760.992l46.016 46.016 272-272L1020.992 512l-21.984-23.008-272-272-46.048 46.048L898.016 480h-772l216.96-216.992z"/></svg>

After

Width:  |  Height:  |  Size: 325 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"/></svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M869 487.8 491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91.5c1.9 0 3.8-.7 5.2-2L869 536.2a32.07 32.07 0 0 0 0-48.4z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M868 545.5 536.1 163a31.96 31.96 0 0 0-48.3 0L156 545.5a7.97 7.97 0 0 0 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"/></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path d="m512 67.008-23.008 21.984-256 256 46.048 46.048L480 190.016v644L279.008 632.96l-46.048 46.08 256 256 23.008 21.984 23.008-21.984 256-256-46.016-46.016L544 834.016v-644l200.992 200.96 46.016-45.984-256-256z"/></svg>

After

Width:  |  Height:  |  Size: 323 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" class="icon" viewBox="0 0 1024 1024"><path d="M956.8 988.8H585.6c-16 0-25.6-9.6-25.6-28.8V576c0-16 9.6-28.8 25.6-28.8h371.2c16 0 25.6 9.6 25.6 28.8v384c0 16-9.6 28.8-25.6 28.8zM608 937.6h326.4V598.4H608v339.2zm-121.6 44.8C262.4 982.4 144 848 144 595.2c0-19.2 9.6-28.8 25.6-28.8s25.6 12.8 25.6 28.8c0 220.8 96 326.4 288 326.4 16 0 25.6 12.8 25.6 28.8s-6.4 32-22.4 32z"/><path d="M262.4 694.4c-6.4 0-9.6-3.2-16-6.4L160 601.6c-9.6-9.6-9.6-22.4 0-28.8s22.4-9.6 28.8 0l86.4 86.4c9.6 9.6 9.6 22.4 0 28.8-3.2 3.2-6.4 6.4-12.8 6.4z"/><path d="M86.4 694.4c-6.4 0-9.6-3.2-16-6.4-9.6-9.6-9.6-22.4 0-28.8l86.4-86.4c9.6-9.6 22.4-9.6 28.8 0 9.6 9.6 9.6 22.4 0 28.8L99.2 688c-3.2 3.2-6.4 6.4-12.8 6.4zm790.4-249.6c-16 0-28.8-12.8-28.8-32 0-224-99.2-336-300.8-336-16 0-28.8-12.8-28.8-32s9.6-32 28.8-32c233.6 0 355.2 137.6 355.2 396.8 0 22.4-9.6 35.2-25.6 35.2z"/><path d="M876.8 448c-6.4 0-9.6-3.2-16-6.4l-86.4-86.4c-9.6-9.6-9.6-22.4 0-28.8s22.4-9.6 28.8 0l86.4 86.4c9.6 9.6 9.6 22.4 0 28.8 0 3.2-6.4 6.4-12.8 6.4z"/><path d="M876.8 448c-6.4 0-9.6-3.2-16-6.4-9.6-9.6-9.6-22.4 0-28.8l86.4-86.4c9.6-9.6 22.4-9.6 28.8 0s9.6 22.4 0 28.8l-86.4 86.4c-3.2 3.2-6.4 6.4-12.8 6.4zM288 524.8C156.8 524.8 48 416 48 278.4S156.8 35.2 288 35.2 528 144 528 281.6 419.2 524.8 288 524.8zm-3.2-432c-99.2 0-179.2 83.2-179.2 185.6S185.6 464 284.8 464 464 380.8 464 278.4 384 92.8 284.8 92.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"/></svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@ -0,0 +1,31 @@
import Reload from "./reload.svg?component";
import Upload from "./upload.svg?component";
import ArrowH from "./arrow-h.svg?component";
import ArrowV from "./arrow-v.svg?component";
import ArrowUp from "./arrow-up.svg?component";
import ChangeIcon from "./change.svg?component";
import ArrowDown from "./arrow-down.svg?component";
import ArrowLeft from "./arrow-left.svg?component";
import DownloadIcon from "./download.svg?component";
import ArrowRight from "./arrow-right.svg?component";
import RotateLeft from "./rotate-left.svg?component";
import SearchPlus from "./search-plus.svg?component";
import RotateRight from "./rotate-right.svg?component";
import SearchMinus from "./search-minus.svg?component";
export {
Reload,
Upload,
ArrowH,
ArrowV,
ArrowUp,
ArrowDown,
ArrowLeft,
ChangeIcon,
ArrowRight,
RotateLeft,
SearchPlus,
RotateRight,
SearchMinus,
DownloadIcon
};

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 0 1 755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 0 0 3 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 0 1 512.1 856a342.24 342.24 0 0 1-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 0 0-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 0 0-8-8.2z"/></svg>

After

Width:  |  Height:  |  Size: 865 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H188V494h440v326z"/><path fill="currentColor" d="M819.3 328.5c-78.8-100.7-196-153.6-314.6-154.2l-.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7.4 12.6-6.1v-63.9c12.9.1 25.9.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z"/></svg>

After

Width:  |  Height:  |  Size: 636 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-.4-12.6 6.1l-.2 64c-118.6.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z"/><path fill="currentColor" d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32zm-44 402H396V494h440v326z"/></svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M637 443H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h312c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"/></svg>

After

Width:  |  Height:  |  Size: 535 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M637 443H519V309c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v134H325c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h118v134c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V519h118c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8zm284 424L775 721c122.1-148.9 113.6-369.5-26-509-148-148.1-388.4-148.1-537 0-148.1 148.6-148.1 389 0 537 139.5 139.6 360.1 148.1 509 26l146 146c3.2 2.8 8.3 2.8 11 0l43-43c2.8-2.7 2.8-7.8 0-11zM696 696c-118.8 118.7-311.2 118.7-430 0-118.7-118.8-118.7-311.2 0-430 118.8-118.7 311.2-118.7 430 0 118.7 118.8 118.7 311.2 0 430z"/></svg>

After

Width:  |  Height:  |  Size: 631 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 1024 1024"><path fill="currentColor" d="M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 0 0-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"/></svg>

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1,60 +1,75 @@
<script setup lang="ts">
import { ref, nextTick } from "vue";
import Cropper from "@/components/ReCropper";
import img from "./picture.jpeg";
<script setup lang="tsx">
import { ref } from "vue";
import avatar from "./avatar.png";
import ReCropper from "@/components/ReCropper";
import { formatBytes } from "@pureadmin/utils";
defineOptions({
name: "Cropping"
});
const infos = ref();
const refCropper = ref();
const info = ref<object>(null);
const showPopover = ref(false);
const cropperImg = ref<string>("");
const onCropper = (): void => {
nextTick(() => {
refCropper.value.cropper.getCroppedCanvas().toBlob(blob => {
const fileReader: FileReader = new FileReader();
fileReader.onloadend = (e: ProgressEvent) => {
cropperImg.value = (e.target as any).result;
info.value = refCropper.value.cropper.getData();
};
fileReader.readAsDataURL(blob);
}, "image/jpeg");
});
};
function onCropper({ base64, blob, info }) {
console.log(blob);
infos.value = info;
cropperImg.value = base64;
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">图片裁剪组件</span>
<span class="font-medium">
图片裁剪组件基于开源的
<el-link
href="https://fengyuanchen.github.io/cropperjs/"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
cropperjs
</el-link>
进行二次封装提示右键下面左侧裁剪区可开启功能菜单
</span>
</div>
</template>
<div class="cropper-container">
<Cropper ref="refCropper" :width="'40vw'" :src="img" />
<img :src="cropperImg" class="croppered" v-if="cropperImg" />
</div>
<el-button type="primary" @click="onCropper">裁剪</el-button>
<p v-if="cropperImg">裁剪后图片信息{{ info }}</p>
<el-popover
:visible="showPopover"
placement="right"
width="300px"
:teleported="false"
>
<template #reference>
<ReCropper
ref="refCropper"
class="w-[30vw]"
:src="avatar"
circled
@cropper="onCropper"
@readied="showPopover = true"
/>
</template>
<div class="flex flex-wrap justify-center items-center text-center">
<el-image
v-if="cropperImg"
:src="cropperImg"
:preview-src-list="Array.of(cropperImg)"
fit="cover"
/>
<div v-if="infos" class="mt-1">
<p>
图像大小{{ parseInt(infos.width) }} ×
{{ parseInt(infos.height) }}像素
</p>
<p>
文件大小{{ formatBytes(infos.size) }}{{ infos.size }} 字节
</p>
</div>
</div>
</el-popover>
</el-card>
</template>
<style scoped>
.cropper-container {
display: flex;
align-items: center;
justify-content: space-between;
}
.el-button {
margin-top: 10px;
}
.croppered {
display: block;
width: 45%;
height: 360px;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB