diff --git a/build/optimize.ts b/build/optimize.ts index 0ae3dc7fc..7d7aac0a4 100644 --- a/build/optimize.ts +++ b/build/optimize.ts @@ -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", diff --git a/locales/en.yaml b/locales/en.yaml index e72a50e29..8030d000b 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -68,7 +68,7 @@ menus: hsguide: Guide hsAble: Able hsMenuTree: Menu Tree - hsOptimize: Debounce、Throttle、Copy Directives + hsOptimize: Debounce、Throttle、Copy、Longpress Directives hsWatermark: Water Mark hsPrint: Print hsDownload: Download diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index 5e56ac0b3..fe207f9a7 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -68,7 +68,7 @@ menus: hsguide: 引导页 hsAble: 功能 hsMenuTree: 菜单树结构 - hsOptimize: 防抖、截流、复制指令 + hsOptimize: 防抖、截流、复制、长按指令 hsWatermark: 水印 hsPrint: 打印 hsDownload: 下载 diff --git a/package.json b/package.json index b48008f32..1bd5b8e24 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,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", @@ -81,8 +82,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", @@ -107,7 +108,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", @@ -119,9 +120,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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18e1dbef4..38e7ed009 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: diff --git a/src/components/ReCropper/src/circled.css b/src/components/ReCropper/src/circled.css new file mode 100644 index 000000000..41b0d9931 --- /dev/null +++ b/src/components/ReCropper/src/circled.css @@ -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%; + } +} diff --git a/src/components/ReCropper/src/index.tsx b/src/components/ReCropper/src/index.tsx index 2d8fac79f..62dd2fa22 100644 --- a/src/components/ReCropper/src/index.tsx +++ b/src/components/ReCropper/src/index.tsx @@ -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, - default() { - return {}; - } - }, - options: { - type: Object as PropType, - default() { - return {}; - } - } + imageStyle: { type: Object as PropType, default: () => ({}) }, + options: { type: Object as PropType, default: () => ({}) } }; export default defineComponent({ name: "ReCropper", props, - setup(props) { - const cropper: any = ref>(null); - const imgElRef = ref(); + setup(props, { attrs, emit }) { + const tippyElRef = ref>(); + const imgElRef = ref>(); + const cropper = ref>(); + 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(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) { + 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 () => ( +
+ + + + downloadByBase64(imgBase64.value, "cropping.png")} + /> + { + inCircled.value = !inCircled.value; + realTimeCroppered(); + }} + /> + handCropper("reset")} + /> + handCropper("move", [0, -10]), "0:100"]} + /> + handCropper("move", [0, 10]), "0:100"]} + /> + handCropper("move", [-10, 0]), "0:100"]} + /> + handCropper("move", [10, 0]), "0:100"]} + /> + handCropper("scaleX", -1)} + /> + handCropper("scaleY", -1)} + /> + handCropper("rotate", -45)} + /> + handCropper("rotate", 45)} + /> + handCropper("zoom", 0.1), "0:100"]} + /> + handCropper("zoom", -0.1), "0:100"]} + /> +
+ ); + } }); + 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 ( - <> -
- {this.props.alt} -
- - ); + const { + inSrc, + isReady, + getClass, + getImageStyle, + onContextmenu, + getWrapperStyle + } = this; + const { alt, crossorigin } = this.props; + + return inSrc ? ( +
onContextmenu(event)} + > + {alt} +
+ ) : null; } }); diff --git a/src/components/ReCropper/src/svg/arrow-down.svg b/src/components/ReCropper/src/svg/arrow-down.svg new file mode 100644 index 000000000..283954738 --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/arrow-h.svg b/src/components/ReCropper/src/svg/arrow-h.svg new file mode 100644 index 000000000..f955c41e7 --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-h.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/arrow-left.svg b/src/components/ReCropper/src/svg/arrow-left.svg new file mode 100644 index 000000000..66742bb73 --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/arrow-right.svg b/src/components/ReCropper/src/svg/arrow-right.svg new file mode 100644 index 000000000..45fbb4dc5 --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/arrow-up.svg b/src/components/ReCropper/src/svg/arrow-up.svg new file mode 100644 index 000000000..7761be47c --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/arrow-v.svg b/src/components/ReCropper/src/svg/arrow-v.svg new file mode 100644 index 000000000..bbd0476f0 --- /dev/null +++ b/src/components/ReCropper/src/svg/arrow-v.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/change.svg b/src/components/ReCropper/src/svg/change.svg new file mode 100644 index 000000000..2edc20901 --- /dev/null +++ b/src/components/ReCropper/src/svg/change.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/download.svg b/src/components/ReCropper/src/svg/download.svg new file mode 100644 index 000000000..f011250c8 --- /dev/null +++ b/src/components/ReCropper/src/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/index.ts b/src/components/ReCropper/src/svg/index.ts new file mode 100644 index 000000000..1306ba771 --- /dev/null +++ b/src/components/ReCropper/src/svg/index.ts @@ -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 +}; diff --git a/src/components/ReCropper/src/svg/reload.svg b/src/components/ReCropper/src/svg/reload.svg new file mode 100644 index 000000000..e8fab2cca --- /dev/null +++ b/src/components/ReCropper/src/svg/reload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/rotate-left.svg b/src/components/ReCropper/src/svg/rotate-left.svg new file mode 100644 index 000000000..f7029864f --- /dev/null +++ b/src/components/ReCropper/src/svg/rotate-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/rotate-right.svg b/src/components/ReCropper/src/svg/rotate-right.svg new file mode 100644 index 000000000..ffe6bc276 --- /dev/null +++ b/src/components/ReCropper/src/svg/rotate-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/search-minus.svg b/src/components/ReCropper/src/svg/search-minus.svg new file mode 100644 index 000000000..185924c28 --- /dev/null +++ b/src/components/ReCropper/src/svg/search-minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/search-plus.svg b/src/components/ReCropper/src/svg/search-plus.svg new file mode 100644 index 000000000..97447d279 --- /dev/null +++ b/src/components/ReCropper/src/svg/search-plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ReCropper/src/svg/upload.svg b/src/components/ReCropper/src/svg/upload.svg new file mode 100644 index 000000000..f5c9f11ce --- /dev/null +++ b/src/components/ReCropper/src/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/RePureTableBar/src/bar.tsx b/src/components/RePureTableBar/src/bar.tsx index 30aa6d3b2..092f42759 100644 --- a/src/components/RePureTableBar/src/bar.tsx +++ b/src/components/RePureTableBar/src/bar.tsx @@ -1,6 +1,12 @@ import { useEpThemeStoreHook } from "@/store/modules/epTheme"; -import { delay, getKeyList, cloneDeep } from "@pureadmin/utils"; import { defineComponent, ref, computed, type PropType, nextTick } from "vue"; +import { + delay, + cloneDeep, + isBoolean, + isFunction, + getKeyList +} from "@pureadmin/utils"; import Sortable from "sortablejs"; import DragIcon from "./svg/drag.svg?component"; @@ -37,8 +43,13 @@ export default defineComponent({ const loading = ref(false); const checkAll = ref(true); const isIndeterminate = ref(false); + const filterColumns = cloneDeep(props?.columns).filter(column => + isBoolean(column?.hide) + ? !column.hide + : !(isFunction(column?.hide) && column?.hide()) + ); let checkColumnList = getKeyList(cloneDeep(props?.columns), "label"); - const checkedColumns = ref(checkColumnList); + const checkedColumns = ref(getKeyList(cloneDeep(filterColumns), "label")); const dynamicColumns = ref(cloneDeep(props?.columns)); const getDropdownItemStyle = computed(() => { @@ -120,7 +131,7 @@ export default defineComponent({ dynamicColumns.value = cloneDeep(props?.columns); checkColumnList = []; checkColumnList = await getKeyList(cloneDeep(props?.columns), "label"); - checkedColumns.value = checkColumnList; + checkedColumns.value = getKeyList(cloneDeep(filterColumns), "label"); } const dropdown = { diff --git a/src/directives/auth/index.ts b/src/directives/auth/index.ts index 62ca87c2e..a7a4f2216 100644 --- a/src/directives/auth/index.ts +++ b/src/directives/auth/index.ts @@ -1,5 +1,5 @@ import { hasAuth } from "@/router/utils"; -import { Directive, type DirectiveBinding } from "vue"; +import type { Directive, DirectiveBinding } from "vue"; export const auth: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { diff --git a/src/directives/copy/index.ts b/src/directives/copy/index.ts index 11db44618..8e9783381 100644 --- a/src/directives/copy/index.ts +++ b/src/directives/copy/index.ts @@ -1,7 +1,7 @@ import { message } from "@/utils/message"; import { useEventListener } from "@vueuse/core"; import { copyTextToClipboard } from "@pureadmin/utils"; -import { Directive, type DirectiveBinding } from "vue"; +import type { Directive, DirectiveBinding } from "vue"; interface CopyEl extends HTMLElement { copyValue: string; diff --git a/src/directives/index.ts b/src/directives/index.ts index 5e81e0657..f4238c9af 100644 --- a/src/directives/index.ts +++ b/src/directives/index.ts @@ -1,3 +1,4 @@ export * from "./auth"; export * from "./copy"; +export * from "./longpress"; export * from "./optimize"; diff --git a/src/directives/longpress/index.ts b/src/directives/longpress/index.ts new file mode 100644 index 000000000..544278448 --- /dev/null +++ b/src/directives/longpress/index.ts @@ -0,0 +1,63 @@ +import { useEventListener } from "@vueuse/core"; +import type { Directive, DirectiveBinding } from "vue"; +import { subBefore, subAfter, isFunction } from "@pureadmin/utils"; + +export const longpress: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const cb = binding.value; + if (cb && isFunction(cb)) { + let timer = null; + let interTimer = null; + let num = 500; + let interNum = null; + const isInter = binding?.arg?.includes(":") ?? false; + + if (isInter) { + num = Number(subBefore(binding.arg, ":")); + interNum = Number(subAfter(binding.arg, ":")); + } else if (binding.arg) { + num = Number(binding.arg); + } + + const clear = () => { + if (timer) { + clearTimeout(timer); + timer = null; + } + if (interTimer) { + clearInterval(interTimer); + interTimer = null; + } + }; + + const onDownInter = (ev: PointerEvent) => { + ev.preventDefault(); + if (interTimer === null) { + interTimer = setInterval(() => cb(), interNum); + } + }; + + const onDown = (ev: PointerEvent) => { + clear(); + ev.preventDefault(); + if (timer === null) { + timer = isInter + ? setTimeout(() => { + cb(); + onDownInter(ev); + }, num) + : setTimeout(() => cb(), num); + } + }; + + // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + useEventListener(el, "pointerdown", onDown); + useEventListener(el, "pointerup", clear); + useEventListener(el, "pointerleave", clear); + } else { + throw new Error( + '[Directive: longpress]: need callback and callback must be a function! Like v-longpress="callback"' + ); + } + } +}; diff --git a/src/directives/optimize/index.ts b/src/directives/optimize/index.ts index 43ac38ac8..5fbdd1fec 100644 --- a/src/directives/optimize/index.ts +++ b/src/directives/optimize/index.ts @@ -6,7 +6,7 @@ import { throttle } from "@pureadmin/utils"; import { useEventListener } from "@vueuse/core"; -import { Directive, type DirectiveBinding } from "vue"; +import type { Directive, DirectiveBinding } from "vue"; /** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */ export const optimize: Directive = { diff --git a/src/views/able/directives.vue b/src/views/able/directives.vue index f3e566536..e032b24ba 100644 --- a/src/views/able/directives.vue +++ b/src/views/able/directives.vue @@ -13,6 +13,9 @@ const searchFour = ref(""); const searchFive = ref(""); const searchSix = ref("copy"); const text = ref("可复制的文本"); +const long = ref(false); +const cbText = ref(""); +const idx = ref(0); function onInput() { message(search.value); @@ -30,13 +33,30 @@ function onInputFour() { function onInputFive({ name, sex }) { message(`${name}${sex}${searchFive.value}`); } + +function onLongpress() { + long.value = true; +} +function onCustomLongpress() { + long.value = true; +} +function onCbLongpress() { + idx.value += 1; + long.value = true; + cbText.value = `持续回调${idx.value}次`; +} +function onReset() { + long.value = false; + cbText.value = ""; + idx.value = 0; +} diff --git a/src/views/components/cropping/avatar.png b/src/views/components/cropping/avatar.png new file mode 100644 index 000000000..035c0e267 Binary files /dev/null and b/src/views/components/cropping/avatar.png differ diff --git a/src/views/components/cropping/index.vue b/src/views/components/cropping/index.vue index d513513e6..5360edd8c 100644 --- a/src/views/components/cropping/index.vue +++ b/src/views/components/cropping/index.vue @@ -1,60 +1,75 @@ - - - diff --git a/src/views/components/cropping/picture.jpeg b/src/views/components/cropping/picture.jpeg deleted file mode 100644 index a0226ae4d..000000000 Binary files a/src/views/components/cropping/picture.jpeg and /dev/null differ