ソースを参照

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu 9 ヶ月 前
コミット
51e2284c99

+ 2 - 0
components.d.ts

@@ -31,6 +31,7 @@ declare module 'vue' {
     HeadSearch: typeof import('./src/components/headSearch/index.vue')['default']
     HotPromoted: typeof import('./src/components/Enterprise/hotPromoted.vue')['default']
     Img: typeof import('./src/components/Upload/img.vue')['default']
+    ImgCropper: typeof import('./src/components/Upload/ImgCropper.vue')['default']
     'Index copy': typeof import('./src/components/CtForm/index copy.vue')['default']
     IndustryTypeCard: typeof import('./src/components/industryTypeCard/index.vue')['default']
     Info: typeof import('./src/components/Enterprise/info.vue')['default']
@@ -38,6 +39,7 @@ declare module 'vue' {
     Item: typeof import('./src/components/Position/item.vue')['default']
     JobTypeCard: typeof import('./src/components/jobTypeCard/index.vue')['default']
     ListGroup: typeof import('./src/components/FormUI/nestedListGroup/components/listGroup.vue')['default']
+    Loading: typeof import('./src/components/Loading/index.vue')['default']
     LongCompany: typeof import('./src/components/Position/longCompany.vue')['default']
     LongStrip: typeof import('./src/components/Position/longStrip.vue')['default']
     NestedListGroup: typeof import('./src/components/FormUI/nestedListGroup/index.vue')['default']

+ 68 - 58
package-lock.json

@@ -24,6 +24,7 @@
         "roboto-fontface": "*",
         "vue": "^3.4.0",
         "vue-i18n": "9",
+        "vue-picture-cropper": "^0.7.0",
         "vue-router": "^4.3.0",
         "vuetify": "^3.6.0",
         "wukongimjssdk": "^1.2.10"
@@ -64,6 +65,15 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@bassist/utils": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/@bassist/utils/-/utils-0.4.0.tgz",
+      "integrity": "sha512-aoFTl0jUjm8/tDZodP41wnEkvB+C5O9NFCuYN/ztL6jSUSsuBkXq90/1ifBm1XhV/zySHgLYlU1+tgo3XtQ+nA==",
+      "license": "MIT",
+      "dependencies": {
+        "@withtypes/mime": "^0.1.2"
+      }
+    },
     "node_modules/@esbuild/aix-ppc64": {
       "version": "0.20.2",
       "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
@@ -71,7 +81,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "aix"
@@ -87,7 +96,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -103,7 +111,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -119,7 +126,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -135,7 +141,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -151,7 +156,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -167,7 +171,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -183,7 +186,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "freebsd"
@@ -199,7 +201,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -215,7 +216,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -231,7 +231,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -247,7 +246,6 @@
       "cpu": [
         "loong64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -263,7 +261,6 @@
       "cpu": [
         "mips64el"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -279,7 +276,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -295,7 +291,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -311,7 +306,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -327,7 +321,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -343,7 +336,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "netbsd"
@@ -359,7 +351,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "openbsd"
@@ -375,7 +366,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "sunos"
@@ -391,7 +381,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -407,7 +396,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -423,7 +411,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -643,7 +630,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -656,7 +642,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "android"
@@ -669,7 +654,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -682,7 +666,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "darwin"
@@ -695,7 +678,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -708,7 +690,6 @@
       "cpu": [
         "arm"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -721,7 +702,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -734,7 +714,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -747,7 +726,6 @@
       "cpu": [
         "ppc64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -760,7 +738,6 @@
       "cpu": [
         "riscv64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -773,7 +750,6 @@
       "cpu": [
         "s390x"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -786,7 +762,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -799,7 +774,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "linux"
@@ -812,7 +786,6 @@
       "cpu": [
         "arm64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -825,7 +798,6 @@
       "cpu": [
         "ia32"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -838,7 +810,6 @@
       "cpu": [
         "x64"
       ],
-      "dev": true,
       "optional": true,
       "os": [
         "win32"
@@ -886,7 +857,7 @@
       "version": "22.1.0",
       "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.1.0.tgz",
       "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==",
-      "dev": true,
+      "devOptional": true,
       "license": "MIT",
       "dependencies": {
         "undici-types": "~6.13.0"
@@ -1057,6 +1028,15 @@
         "vuetify": "^3.0.0"
       }
     },
+    "node_modules/@withtypes/mime": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/@withtypes/mime/-/mime-0.1.2.tgz",
+      "integrity": "sha512-PB9BfZGzwblUONJY0LiOwsHCA6uV3DIPj/w9ReekdHxPOl0VdUFgI5s4avKycuuq9Gf5Nz2ZPA2O36GAUzlMPA==",
+      "license": "MIT",
+      "dependencies": {
+        "mime": "^3.0.0"
+      }
+    },
     "node_modules/acorn": {
       "version": "8.11.3",
       "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
@@ -1132,7 +1112,7 @@
       "version": "3.1.3",
       "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
       "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "normalize-path": "^3.0.0",
         "picomatch": "^2.0.4"
@@ -1359,7 +1339,7 @@
       "version": "2.3.0",
       "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
       "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=8"
       },
@@ -1394,7 +1374,7 @@
       "version": "3.0.2",
       "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz",
       "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "fill-range": "^7.0.1"
       },
@@ -1513,7 +1493,7 @@
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
       "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "anymatch": "~3.1.2",
         "braces": "~3.0.2",
@@ -1537,7 +1517,7 @@
       "version": "5.1.2",
       "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
       "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "is-glob": "^4.0.1"
       },
@@ -1710,6 +1690,12 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/cropperjs": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmmirror.com/cropperjs/-/cropperjs-1.6.2.tgz",
+      "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==",
+      "license": "MIT"
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -2763,7 +2749,7 @@
       "version": "7.0.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "to-regex-range": "^5.0.1"
       },
@@ -2907,7 +2893,6 @@
       "version": "2.3.3",
       "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
       "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
       "hasInstallScript": true,
       "optional": true,
       "os": [
@@ -3406,7 +3391,7 @@
       "version": "4.3.5",
       "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.5.tgz",
       "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/import-fresh": {
       "version": "3.3.0",
@@ -3512,7 +3497,7 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
       "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "binary-extensions": "^2.0.0"
       },
@@ -3642,7 +3627,7 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
       "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -3660,7 +3645,7 @@
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
       "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "is-extglob": "^2.1.1"
       },
@@ -3685,7 +3670,7 @@
       "version": "7.0.0",
       "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=0.12.0"
       }
@@ -4188,6 +4173,18 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/mime": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz",
+      "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -4373,7 +4370,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -4696,7 +4693,7 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-      "dev": true,
+      "devOptional": true,
       "engines": {
         "node": ">=8.6"
       },
@@ -5045,7 +5042,7 @@
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
       "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "picomatch": "^2.2.1"
       },
@@ -5378,7 +5375,7 @@
       "version": "1.75.0",
       "resolved": "https://registry.npmmirror.com/sass/-/sass-1.75.0.tgz",
       "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "chokidar": ">=3.0.0 <4.0.0",
         "immutable": "^4.0.0",
@@ -6255,7 +6252,7 @@
       "version": "5.0.1",
       "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "is-number": "^7.0.0"
       },
@@ -6507,7 +6504,7 @@
       "version": "6.13.0",
       "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.13.0.tgz",
       "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==",
-      "dev": true,
+      "devOptional": true,
       "license": "MIT"
     },
     "node_modules/union-value": {
@@ -6937,6 +6934,19 @@
         "vue": "^3.0.0"
       }
     },
+    "node_modules/vue-picture-cropper": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/vue-picture-cropper/-/vue-picture-cropper-0.7.0.tgz",
+      "integrity": "sha512-NF7+Dgso6d0GB16E5d/BbrcTIHm1VWz8dS3IjLhoBl+ZeC+yDA46CyJphQuO32SisaPmrKHN8VbiE2LgAfhnkQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@bassist/utils": "^0.4.0",
+        "cropperjs": "^1.6.1"
+      },
+      "peerDependencies": {
+        "vue": ">=3.2.13"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.3.2",
       "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.3.2.tgz",

+ 1 - 0
package.json

@@ -25,6 +25,7 @@
     "roboto-fontface": "*",
     "vue": "^3.4.0",
     "vue-i18n": "9",
+    "vue-picture-cropper": "^0.7.0",
     "vue-router": "^4.3.0",
     "vuetify": "^3.6.0",
     "wukongimjssdk": "^1.2.10"

+ 35 - 0
src/components/Loading/index.vue

@@ -0,0 +1,35 @@
+<template>
+  <v-overlay :model-value="visible" class="align-center justify-center" :close-on-content-click="false" :disabled="true">
+    <div class="loader"></div>
+  </v-overlay>
+</template>
+
+<script setup>
+defineOptions({ name: 'LoadingPage'})
+
+defineProps({
+  visible: Boolean
+})
+</script>
+
+<style scoped lang="scss">
+.loader {
+  width: fit-content;
+  font-weight: bold;
+  font-family: monospace;
+  font-size: 30px;
+  color :#0000;
+  overflow: hidden;
+  animation: l9 5s infinite cubic-bezier(0.3,1,0,1);
+}
+.loader:before {
+  content:"Loading..."
+}
+@keyframes l9 {
+  0%  {text-shadow: 0    0 #000, 11ch 0 #dbf03f, 22ch 0 #f03756, 33ch 0 #6fedf8,44ch 0 #000}
+  25% {text-shadow:-11ch 0 #000,  0ch 0 #dbf03f, 11ch 0 #f03756, 22ch 0 #6fedf8,33ch 0 #000}
+  50% {text-shadow:-22ch 0 #000,-11ch 0 #dbf03f,  0ch 0 #f03756, 11ch 0 #6fedf8,22ch 0 #000}
+  75% {text-shadow:-33ch 0 #000,-22ch 0 #dbf03f,-11ch 0 #f03756,  0ch 0 #6fedf8,11ch 0 #000}
+  100%{text-shadow:-44ch 0 #000,-33ch 0 #dbf03f,-22ch 0 #f03756,-11ch 0 #6fedf8, 0ch 0 #000}
+}
+</style>

+ 187 - 0
src/components/Upload/ImgCropper.vue

@@ -0,0 +1,187 @@
+<template>
+  <CtDialog
+    :visible="isShowModal"
+    :widthType="1"
+    titleClass="text-h6"
+    title="图片裁剪"
+    :footer="true"
+    submitText="裁剪"
+    @close="isShowModal = false; cancel"
+    @submit="getResult"
+  >
+    <!-- <div class="text-end"> -->
+      <!-- <v-btn color="error" @click="cancel"><v-icon left>mdi-close</v-icon> 取 消</v-btn> -->
+      <!-- <v-btn v-if="showClear" class="mx-3" color="warning" @click="clear"><v-icon left>mdi-trash-can-outline</v-icon> 清 除</v-btn> -->
+      <!-- <v-btn color="primary" @click="getResult"><v-icon left>mdi-crop</v-icon> 裁 剪</v-btn> -->
+    <!-- </div> -->
+    <div class="modal-content">
+      <VuePictureCropper :img="pic" @ready="ready" :boxStyle="corpBoxStyle" :options="corpOptions" :presetMode="corpPresetMode" />
+    </div>
+  </CtDialog>
+</template>
+
+<script setup>
+defineOptions({ name: 'ImgCorpper' })
+import { reactive, ref, watch, computed } from 'vue'
+import VuePictureCropper, { cropper } from 'vue-picture-cropper'
+import { PhotoCompress } from "@/utils/compressUtil.js"
+
+const props = defineProps({
+  visible: {
+    type: Boolean
+  },
+  image: {
+    type: [String, File]
+  },
+  presetMode: {
+    type: Object
+  },
+  options: {
+    type: Object
+  },
+  boxStyle: {
+    type: Object
+  },
+  showClear: { // 是否显示 Clear 按钮
+    type: Boolean,
+    default: true
+  },
+  showReset: { // 是否显示 Reset 按钮
+    type: Boolean,
+    default: true
+  },
+  isCompress: { // 是否压缩裁剪后的图片大小
+    type: Boolean,
+    default: true
+  },
+  // 压缩图片大小限制
+  maxSize: {
+    type: Number,
+    default: 0.3 * 1024 * 1024
+  },
+  minSize: {
+    type: Number,
+    default: 0.03 * 1024 * 1024
+  },
+  cropBoxResizable: {
+    type: Boolean,
+    default: false
+  },
+  aspectRatio: {
+    type: Number,
+    default: 16 / 9
+  }
+})
+
+/** 裁剪组件属性配置 */
+const corpOptions = computed(() => {
+  return {
+    viewMode: 1, // 裁剪框范围 1-只能在图片区域内活动
+    dragMode: 'move', // 可选值 crop(可绘制裁剪框) | move(仅移动)
+    aspectRatio: props.aspectRatio,// 裁剪框的宽高比
+    cropBoxResizable: props.cropBoxResizable, // 是否可以调整裁剪区的大小
+    ...props.options
+  }
+})
+/** 裁剪区域的样式 */
+const corpBoxStyle = computed(() => {
+  return {
+    width: 'auto',
+    height: '600px',
+    backgroundColor: '#f8f8f8',
+    margin: 'auto',
+    ...props.boxStyle
+  }
+})
+
+/** 预设模式配置 */
+const corpPresetMode = computed(() => {
+  return {
+    mode: 'fixedSize', width: 300, height: 300,
+    ...props.presetMode
+  }
+})
+const emits = defineEmits(["close"])
+
+watch(() => props.visible, (val) => {
+  isShowModal.value = val
+  if (!val) {
+    pic.value = ''
+    result.dataURL = ''
+    result.blobURL = ''
+    reset()
+  }
+})
+
+watch(() => props.image, (val) => {
+  pic.value = val
+})
+
+const isShowModal = ref(false)
+const pic = ref('')
+const result = reactive({
+  dataURL: '',
+  blobURL: '',
+})
+
+/**
+ * Get cropping results
+ */
+async function getResult() {
+  if (!cropper) return
+  const base64 = cropper.getDataURL()
+  const blob = await cropper.getBlob()
+  if (!blob) return
+
+  let file = await cropper.getFile({
+    fileName: "locales.fileName",
+  })
+
+  result.dataURL = base64
+  result.blobURL = URL.createObjectURL(blob)
+  // 是否限制文件大小
+  if (props.isCompress && (file.size > props.maxSize)) {
+    const photoCompress = new PhotoCompress(props.minSize, props.maxSize);
+    photoCompress.compress(file, function (f) {
+      const r = new FileReader()
+      r.readAsDataURL(f)
+      r.onload = function () {
+        emits("close", { result, file: f })
+      }
+    });
+  } else {
+    // console.log("crop")
+    emits("close", { result, file })
+  }
+}
+
+/**
+ * Clear the crop box
+ */
+// function clear() {
+//   if (!cropper) return
+//   cropper.clear()
+// }
+
+/**
+ * Reset the default cropping area
+ */
+function reset() {
+  if (!cropper) return
+  cropper.reset()
+}
+
+/**
+ * The ready event passed to Cropper.js
+ */
+function ready() {
+  // console.log('Cropper is ready.')
+}
+
+function cancel() {
+  emits("close")
+}
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 91 - 0
src/utils/compressUtil.js

@@ -0,0 +1,91 @@
+/**
+ * 图片压缩类
+ * @param minSize
+ * @param maxSize
+ * @constructor
+ */
+export function PhotoCompress(minSize, maxSize) {
+  var nextQ = 0.5; // 压缩比例
+  var maxQ = 1;
+  var minQ = 0;
+
+  /**
+   * 将base64转换为文件
+   * @param base64Codes base64编码
+   * @param fileName 文件名称
+   * @returns {*}
+   */
+  PhotoCompress.prototype.dataUrlToFile = function (base64Codes, fileName = new Date().getTime()) {
+    var arr = base64Codes.split(','),
+      mime = arr[0].match(/:(.*?);/)[1],
+      bStr = atob(arr[1]),
+      n = bStr.length,
+      u8arr = new Uint8Array(n);
+    while (n--) {
+      u8arr[n] = bStr.charCodeAt(n);
+    }
+    return new File([u8arr], fileName, { type: mime });
+  }
+
+  /**
+   * 图片压缩
+   * @param file 文件
+   * @param callback 回调函数
+   */
+  PhotoCompress.prototype.compress = function (file, callback) {
+    var self = this;
+    self.imgBase64(file, function (image, canvas) {
+      var base64Codes = canvas.toDataURL(file.type, nextQ); // y压缩
+      var compressFile = self.dataUrlToFile(base64Codes, file.name.split('.')[0]); // 转成file文件
+      var compressFileSize = compressFile.size; // 压缩后文件大小 k
+      console.log("图片质量:" + nextQ);
+      console.log("压缩后文件大小:" + compressFileSize / 1024);
+      if (compressFileSize > maxSize) { // 压缩后文件大于最大值
+        maxQ = nextQ;
+        nextQ = (nextQ + minQ) / 2; // 质量降低
+        self.compress(file, callback);
+      } else if (compressFileSize < minSize) { // 压缩以后文件小于最小值
+        minQ = nextQ;
+        nextQ = (nextQ + maxQ) / 2; // 质量提高
+        self.compress(file, callback);
+      } else {
+        callback(compressFile);
+      }
+    });
+  }
+
+  /**
+   * 将图片转化为base64
+   * @param file 文件
+   * @param callback 回调函数
+   */
+  PhotoCompress.prototype.imgBase64 = function (file, callback) {
+    // 看支持不支持FileReader
+    if (!file || !window.FileReader) return;
+    var image = new Image();
+    // 绑定 load 事件处理器,加载完成后执行
+    image.onload = function () {
+      var canvas = document.createElement('canvas')
+      var ctx = canvas.getContext('2d')
+      ctx.clearRect(0, 0, canvas.width, canvas.height);
+      canvas.width = image.width * nextQ;
+      canvas.height = image.height * nextQ;
+      ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
+      callback(image, canvas);
+    };
+    if (/^image/.test(file.type)) {
+      // 创建一个reader
+      var reader = new FileReader();
+      // 将图片将转成 base64 格式
+      reader.readAsDataURL(file);
+      // 读取成功后的回调
+      reader.onload = function () {
+        // self.imgUrls.push(this.result);
+        // 设置src属性,浏览器会自动加载。
+        // 记住必须先绑定事件,才能设置src属性,否则会出同步问题。
+        image.src = this.result;
+      }
+    }
+  }
+};
+

+ 46 - 17
src/views/recruit/personal/remuse/components/basicInfo.vue

@@ -34,19 +34,6 @@
             <template v-slot:phone>
               <v-btn variant="text" class="ml-2" color="primary">{{ $t('common.change') }}</v-btn>
             </template>
-            <!-- <template #areaType="{ item }">
-              <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
-                <template v-slot:activator="{  props }">
-                  <textUI
-                    v-model="item[item.nameKey]"
-                    :item="item"
-                    v-bind="props"
-                    style="position: relative;"
-                  ></textUI>
-                </template>
-                <areaType :isIntType="false" :select="[baseInfo?.areaId].filter(Boolean)" @handleAreaClick="handleArea" class="jobTypeCardBox" isSingle></areaType>
-              </v-menu>
-            </template> -->
           </CtForm>
           <div class="text-end">
             <v-btn class="half-button mr-3" variant="tonal" @click="isEdit = false">{{ $t('common.cancel') }}</v-btn>
@@ -112,6 +99,9 @@
       </div>
     </div>
   </div>
+  
+  <Loading :visible="overlay"></Loading>
+  <!-- <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @close="handleHideCopper" :aspectRatio="1 / 1"></ImgCropper> -->
 </template>
 
 <script setup>
@@ -127,12 +117,14 @@ import { getUserAvatar } from '@/utils/avatar'
 import { useI18n } from '@/hooks/web/useI18n'
 import { ref } from 'vue';
 defineOptions({name: 'resume-components-basicInfo'})
+
 const { t } = useI18n()
 const userStore = useUserStore()
 const CtFormRef = ref()
 const isEdit = ref(false)
 const showIcon = ref(false)
 const isExpand = ref(false)
+const overlay = ref(false) // 加载中
 const welfareList = ref(['响应', '改变', '诚信', '进取精神', '信任', '卓越'])
 let baseInfo = ref({})
 const getBasicInfo = () => { // 获取基础信息
@@ -153,19 +145,56 @@ const openFileInput = () => {
 }
 
 // 上传头像
+// const selectPic = ref('')
+// const isShowCopper = ref(false)
+// const handleUploadFile = async (e) => {
+//   const file = e.target.files[0]
+//   const reader = new FileReader()
+//   reader.readAsDataURL(file)
+//   reader.onload = () => {
+//     selectPic.value = String(reader.result)
+//     isShowCopper.value = true
+//   }
+// }
+
 const handleUploadFile = async (e) => {
   const file = e.target.files[0]
   if (!file) return
+  overlay.value = true
   const formData = new FormData()
   formData.append('file', file)
   const { data } = await uploadFile(formData)
-  if (!data) return
-  Snackbar.success(t('common.uploadSucMsg'))
+  if (!data) return overlay.value = false
   await updatePersonAvatar(data)
-  await userStore.getUserBaseInfos(baseInfo.value.userId)
-  getBasicInfo()
+  setTimeout(async () => {
+    await userStore.getUserBaseInfos(baseInfo.value.userId)
+    getBasicInfo()
+    overlay.value = false
+    Snackbar.success(t('common.uploadSucMsg'))
+  }, 1000)
 }
 
+// const handleHideCopper = (data) => {
+//   isShowCopper.value = false
+//   if (data) {
+//     const { file } = data
+//     if (!file) return
+//     const formData = new FormData()
+//     formData.append('file', file)
+//     overlay.value = true
+//     uploadFile(formData).then(async ({data}) => {
+//       if (!data) return
+//       await updatePersonAvatar(data)
+//       setTimeout(async () => {
+//         await userStore.getUserBaseInfos(baseInfo.value.userId)
+//         getBasicInfo()
+//         overlay.value = false
+//         Snackbar.success(t('common.uploadSucMsg'))
+//       }, 1000)
+//     })
+//   }
+// }
+
 const items = ref({
   options: [
     {