diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 8c1957b..e66cf19 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -6,6 +6,7 @@ // biome-ignore lint: disable export {} declare global { + const ElLoading: typeof import('element-plus/es').ElLoading const ElMessage: typeof import('element-plus/es').ElMessage const ElMessageBox: typeof import('element-plus/es').ElMessageBox } diff --git a/components.d.ts b/components.d.ts index e233fe8..6b640a2 100644 --- a/components.d.ts +++ b/components.d.ts @@ -42,4 +42,7 @@ declare module 'vue' { SettingsDialog: typeof import('./src/components/SettingsDialog.vue')['default'] SideNav: typeof import('./src/components/SideNav.vue')['default'] } + export interface GlobalDirectives { + vLoading: typeof import('element-plus/es')['ElLoadingDirective'] + } } diff --git a/package.json b/package.json index dfa730b..a67b6aa 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@element-plus/icons-vue": "^2.3.2", "axios": "^1.13.6", "element-plus": "^2.13.3", + "html2pdf.js": "^0.14.0", "sass": "^1.97.3", "vue": "^3.5.25", "vue-router": "^4.6.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17c8b4c..3a7aae4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: element-plus: specifier: ^2.13.3 version: 2.13.3(vue@3.5.29(typescript@5.9.3)) + html2pdf.js: + specifier: ^0.14.0 + version: 0.14.0 sass: specifier: ^1.97.3 version: 1.97.3 @@ -83,6 +86,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} @@ -532,6 +539,15 @@ packages: '@types/node@24.11.0': resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==} + '@types/pako@2.0.4': + resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==} + + '@types/raf@3.4.3': + resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} @@ -717,6 +733,10 @@ packages: bare-url@2.3.2: resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + basic-ftp@5.2.0: resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} engines: {node: '>=10.0.0'} @@ -748,6 +768,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + canvg@3.0.11: + resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==} + engines: {node: '>=10.0.0'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -797,6 +821,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + core-js@3.49.0: + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + cosmiconfig@9.0.1: resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} engines: {node: '>=14'} @@ -806,6 +833,9 @@ packages: typescript: optional: true + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -856,6 +886,9 @@ packages: devtools-protocol@0.0.1566079: resolution: {integrity: sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==} + dompurify@3.3.3: + resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -973,6 +1006,9 @@ packages: fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-png@6.4.0: + resolution: {integrity: sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -988,6 +1024,9 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1061,6 +1100,13 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + + html2pdf.js@0.14.0: + resolution: {integrity: sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -1100,6 +1146,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + iobuffer@5.4.0: + resolution: {integrity: sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==} + ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -1147,6 +1196,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + jspdf@4.2.1: + resolution: {integrity: sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -1265,6 +1317,9 @@ packages: resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} engines: {node: '>= 14'} + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -1289,6 +1344,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1351,6 +1409,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -1367,6 +1428,9 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -1382,6 +1446,10 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + rgbcolor@1.0.1: + resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} + engines: {node: '>= 0.8.15'} + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1461,6 +1529,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + stackblur-canvas@2.7.0: + resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==} + engines: {node: '>=0.1.14'} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -1483,6 +1555,10 @@ packages: strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + svg-pathdata@6.0.3: + resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==} + engines: {node: '>=12.0.0'} + tar-fs@3.1.1: resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==} @@ -1495,6 +1571,9 @@ packages: text-decoder@1.2.7: resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -1574,6 +1653,9 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -1715,6 +1797,8 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/runtime@7.29.2': {} + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -2045,6 +2129,14 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/pako@2.0.4': {} + + '@types/raf@3.4.3': + optional: true + + '@types/trusted-types@2.0.7': + optional: true + '@types/web-bluetooth@0.0.20': {} '@types/yauzl@2.10.3': @@ -2250,6 +2342,8 @@ snapshots: dependencies: bare-path: 3.0.0 + base64-arraybuffer@1.0.2: {} + basic-ftp@5.2.0: {} body-parser@1.20.4: @@ -2290,6 +2384,18 @@ snapshots: callsites@3.1.0: {} + canvg@3.0.11: + dependencies: + '@babel/runtime': 7.29.2 + '@types/raf': 3.4.3 + core-js: 3.49.0 + raf: 3.4.1 + regenerator-runtime: 0.13.11 + rgbcolor: 1.0.1 + stackblur-canvas: 2.7.0 + svg-pathdata: 6.0.3 + optional: true + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -2334,6 +2440,9 @@ snapshots: cookie@0.7.2: {} + core-js@3.49.0: + optional: true + cosmiconfig@9.0.1(typescript@5.9.3): dependencies: env-paths: 2.2.1 @@ -2343,6 +2452,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + csstype@3.2.3: {} data-uri-to-buffer@6.0.2: {} @@ -2374,6 +2487,10 @@ snapshots: devtools-protocol@0.0.1566079: {} + dompurify@3.3.3: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2551,6 +2668,12 @@ snapshots: fast-fifo@1.3.2: {} + fast-png@6.4.0: + dependencies: + '@types/pako': 2.0.4 + iobuffer: 5.4.0 + pako: 2.1.0 + fast-uri@3.1.0: {} fd-slicer@1.1.0: @@ -2561,6 +2684,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.8.2: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2641,6 +2766,17 @@ snapshots: dependencies: function-bind: 1.1.2 + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + + html2pdf.js@0.14.0: + dependencies: + dompurify: 3.3.3 + html2canvas: 1.4.1 + jspdf: 4.2.1 + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -2696,6 +2832,8 @@ snapshots: inherits@2.0.4: {} + iobuffer@5.4.0: {} + ip-address@10.1.0: {} ipaddr.js@1.9.1: {} @@ -2730,6 +2868,17 @@ snapshots: json-schema-traverse@1.0.0: {} + jspdf@4.2.1: + dependencies: + '@babel/runtime': 7.29.2 + fast-png: 6.4.0 + fflate: 0.8.2 + optionalDependencies: + canvg: 3.0.11 + core-js: 3.49.0 + dompurify: 3.3.3 + html2canvas: 1.4.1 + lines-and-columns@1.2.4: {} local-pkg@1.1.2: @@ -2834,6 +2983,8 @@ snapshots: degenerator: 5.0.1 netmask: 2.0.2 + pako@2.1.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -2855,6 +3006,9 @@ snapshots: pend@1.2.0: {} + performance-now@2.1.0: + optional: true + picocolors@1.1.1: {} picomatch@2.3.1: @@ -2956,6 +3110,11 @@ snapshots: quansync@0.2.11: {} + raf@3.4.1: + dependencies: + performance-now: 2.1.0 + optional: true + range-parser@1.2.1: {} raw-body@2.5.3: @@ -2969,6 +3128,9 @@ snapshots: readdirp@5.0.0: {} + regenerator-runtime@0.13.11: + optional: true + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -2978,6 +3140,9 @@ snapshots: resolve-from@4.0.0: {} + rgbcolor@1.0.1: + optional: true + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -3116,6 +3281,9 @@ snapshots: source-map@0.6.1: optional: true + stackblur-canvas@2.7.0: + optional: true + statuses@2.0.2: {} stoppable@1.1.0: {} @@ -3143,6 +3311,9 @@ snapshots: dependencies: js-tokens: 9.0.1 + svg-pathdata@6.0.3: + optional: true + tar-fs@3.1.1: dependencies: pump: 3.0.4 @@ -3179,6 +3350,10 @@ snapshots: transitivePeerDependencies: - react-native-b4a + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -3265,6 +3440,10 @@ snapshots: utils-merge@1.0.1: {} + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + vary@1.1.2: {} vite@7.3.1(@types/node@24.11.0)(sass@1.97.3): diff --git a/src/api/resume.ts b/src/api/resume.ts new file mode 100644 index 0000000..29e7027 --- /dev/null +++ b/src/api/resume.ts @@ -0,0 +1,372 @@ +import request from '@/utils/request' +import type { ApiResult } from '@/api/auth' + +// ==================== 简历列表相关 ==================== + +/** 时间戳结构(Instant) */ +export interface InstantTime { + /** 秒级时间戳 */ + seconds?: number + /** 纳秒部分 */ + nanos?: number +} + +/** 简历列表项 */ +export interface ResumeListItem { + /** 简历 ID(字符串,避免大整数精度丢失) */ + id?: string + /** 简历名称 */ + resumeName?: string + /** 目标岗位 */ + targetPosition?: string + /** 是否默认简历 0=否 1=是 */ + isDefault?: number + /** 简历修改时间 */ + updateTime?: InstantTime + /** 简历创建时间 */ + createTime?: InstantTime +} + +/** + * 获取简历列表 + * GET /resume/list + */ +export function fetchResumeList() { + return request.get>('/resume/list') +} + +// ==================== 描述段落(通用) ==================== + +/** 描述段落 */ +export interface DescriptionParagraph { + /** 段落标识,前端生成的短ID */ + id?: string + /** 段落文本内容 */ + text?: string +} + +// ==================== 简历主表 ==================== + +/** 简历主表数据 */ +export interface ResumeMainData { + /** 简历 ID(字符串,避免大整数精度丢失) */ + id?: string + /** 简历名称 */ + resumeName?: string + /** 目标岗位 */ + targetPosition?: string + /** 是否默认简历 0=否 1=是 */ + isDefault?: number + /** 头像URL */ + avatarUrl?: string + /** 真实姓名 */ + name?: string + /** 邮箱 */ + email?: string + /** 手机号码 */ + mobileNumber?: string + /** 所在城市 */ + city?: string + /** 微信号 */ + wechatNumber?: string + /** 作品集链接 */ + portfolioUrl?: string + /** 技能标签列表 */ + skills?: string[] + /** 证书标签列表 */ + certificates?: string[] + /** 个人概述 */ + summary?: string +} + +/** + * 查询简历主表 + * GET /resume?resumeId=xxx + */ +export function fetchResumeMain(resumeId: string) { + return request.get>('/resume', { + params: { resumeId }, + }) +} + +// ==================== 教育经历 ==================== + +/** 教育经历项 */ +export interface ResumeEducation { + /** ID(字符串,避免大整数精度丢失) */ + id?: string + /** 学校名称 */ + school?: string + /** 专业 */ + major?: string + /** 学历:大专/本科/硕士/博士 */ + degree?: string + /** 学习形式:全日制/非全日制 */ + studyType?: string + /** 开始时间,格式:2023.09 */ + startDate?: string + /** 结束时间,格式:2024.06 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 查询简历的教育经历列表 + * GET /resume/education?resumeId=xxx + */ +export function fetchResumeEducation(resumeId: string) { + return request.get>('/resume/education', { + params: { resumeId }, + }) +} + +// ==================== 工作经历 ==================== + +/** 工作经历项 */ +export interface ResumeWork { + /** ID(字符串,避免大整数精度丢失) */ + id?: string + /** 公司名称 */ + companyName?: string + /** 职位 */ + position?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 查询简历的工作经历列表 + * GET /resume/work?resumeId=xxx + */ +export function fetchResumeWork(resumeId: string) { + return request.get>('/resume/work', { + params: { resumeId }, + }) +} + +// ==================== 实习经历 ==================== + +/** 实习经历项 */ +export interface ResumeInternship { + /** ID(字符串,避免大整数精度丢失) */ + id?: string + /** 公司名称 */ + companyName?: string + /** 职位 */ + position?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 查询简历的实习经历列表 + * GET /resume/internship?resumeId=xxx + */ +export function fetchResumeInternship(resumeId: string) { + return request.get>('/resume/internship', { + params: { resumeId }, + }) +} + +// ==================== 项目经历 ==================== + +/** 项目经历项 */ +export interface ResumeProject { + /** ID(字符串,避免大整数精度丢失) */ + id?: string + /** 所属公司 */ + companyName?: string + /** 项目名称 */ + projectName?: string + /** 担任角色 */ + role?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 查询简历的项目经历列表 + * GET /resume/project?resumeId=xxx + */ +export function fetchResumeProject(resumeId: string) { + return request.get>('/resume/project', { + params: { resumeId }, + }) +} + +// ==================== 竞赛经历 ==================== + +/** 竞赛经历项 */ +export interface ResumeCompetition { + /** ID(字符串,避免大整数精度丢失) */ + id?: string + /** 竞赛名称 */ + competitionName?: string + /** 获奖情况 */ + award?: string + /** 获奖时间,格式:2023.07 */ + awardDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 查询简历的竞赛经历列表 + * GET /resume/competition?resumeId=xxx + */ +export function fetchResumeCompetition(resumeId: string) { + return request.get>('/resume/competition', { + params: { resumeId }, + }) +} + +// ==================== 简历保存相关 ==================== + +/** 保存简历主表参数 */ +export interface SaveResumeMainParams { + /** 简历 ID */ + resumeId: string + /** 简历名称 */ + resumeName?: string + /** 目标岗位 */ + targetPosition?: string + /** 真实姓名 */ + name?: string + /** 邮箱 */ + email?: string + /** 手机号码 */ + mobileNumber?: string + /** 所在城市 */ + city?: string + /** 微信号 */ + wechatNumber?: string + /** 作品集链接 */ + portfolioUrl?: string + /** 技能标签列表 */ + skills?: string[] + /** 证书标签列表 */ + certificates?: string[] + /** 个人概述 */ + summary?: string +} + +/** + * 保存/更新简历主表信息 + * POST /resume + */ +export function saveResumeMain(data: SaveResumeMainParams) { + return request.post('/resume', data) +} + +/** 保存简历教育经历参数(单条) */ +export interface SaveResumeEducationItem { + /** 学校名称 */ + school?: string + /** 专业 */ + major?: string + /** 学历 */ + degree?: string + /** 学习形式 */ + studyType?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 保存简历教育经历(全量覆盖) + * POST /resume/education + */ +export function saveResumeEducation(resumeId: string, data: SaveResumeEducationItem[]) { + return request.post('/resume/education', { resumeId, items: data }) +} + +/** 保存简历工作经历参数(单条) */ +export interface SaveResumeWorkItem { + /** 公司名称 */ + companyName?: string + /** 职位 */ + position?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 保存简历工作经历(全量覆盖) + * POST /resume/work + */ +export function saveResumeWork(resumeId: string, data: SaveResumeWorkItem[]) { + return request.post('/resume/work', { resumeId, items: data }) +} + +/** + * 保存简历实习经历(全量覆盖) + * POST /resume/internship + */ +export function saveResumeInternship(resumeId: string, data: SaveResumeWorkItem[]) { + return request.post('/resume/internship', { resumeId, items: data }) +} + +/** 保存简历项目经历参数(单条) */ +export interface SaveResumeProjectItem { + /** 项目名称 */ + projectName?: string + /** 所属公司 */ + companyName?: string + /** 担任角色 */ + role?: string + /** 开始时间 */ + startDate?: string + /** 结束时间 */ + endDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 保存简历项目经历(全量覆盖) + * POST /resume/project + */ +export function saveResumeProject(resumeId: string, data: SaveResumeProjectItem[]) { + return request.post('/resume/project', { resumeId, items: data }) +} + +/** 保存简历竞赛经历参数(单条) */ +export interface SaveResumeCompetitionItem { + /** 竞赛名称 */ + competitionName?: string + /** 获奖情况 */ + award?: string + /** 获奖时间 */ + awardDate?: string + /** 描述段落 */ + description?: DescriptionParagraph[] +} + +/** + * 保存简历竞赛经历(全量覆盖) + * POST /resume/competition + */ +export function saveResumeCompetition(resumeId: string, data: SaveResumeCompetitionItem[]) { + return request.post('/resume/competition', { resumeId, items: data }) +} diff --git a/src/assets/images/home/avatar-temporary.png b/src/assets/images/home/avatar-temporary.png new file mode 100644 index 0000000..c6e4b19 Binary files /dev/null and b/src/assets/images/home/avatar-temporary.png differ diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000..943119c Binary files /dev/null and b/src/assets/images/logo.png differ diff --git a/src/assets/styles/components/job-resume-custom-dialog.scss b/src/assets/styles/components/job-resume-custom-dialog.scss index e8b287d..756f4ea 100644 --- a/src/assets/styles/components/job-resume-custom-dialog.scss +++ b/src/assets/styles/components/job-resume-custom-dialog.scss @@ -11,6 +11,7 @@ display: flex; align-items: center; justify-content: center; + font-size: 0.14rem; // 遮罩层 &__overlay { diff --git a/src/assets/styles/components/side-nav.scss b/src/assets/styles/components/side-nav.scss index 1c6bdf1..13758c1 100644 --- a/src/assets/styles/components/side-nav.scss +++ b/src/assets/styles/components/side-nav.scss @@ -38,6 +38,13 @@ color: #fff; } + // Logo 图片 + &__logo-img { + width: 1.32rem; + height: 0.34rem; + display: block; + } + &__menu { display: flex; flex-direction: column; diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index 6d4484a..62422b4 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -55,3 +55,10 @@ font-size: 14px !important; } } + +// ==================== Element Plus Loading 品牌色覆盖 ==================== +.el-loading-spinner { + .circular .path { + stroke: #4FC2C9 !important; + } +} diff --git a/src/assets/styles/pages/home.scss b/src/assets/styles/pages/home.scss index 758ddd5..cf5db4e 100644 --- a/src/assets/styles/pages/home.scss +++ b/src/assets/styles/pages/home.scss @@ -20,15 +20,17 @@ position: sticky; top: 0; z-index: 50; - background: rgba(255, 255, 255, 0.7); - backdrop-filter: blur(100px); - border-bottom: 1px solid rgba(243, 244, 246, 0.5); // 导航内容容器 — 居中 12rem 宽 &__inner { width: 12rem; margin: 0 auto; - height: 0.8rem; + height: 0.68rem; + padding: 0.2rem; + box-sizing: border-box; + border-radius: 40px; + background: #FFFFFF; + border: 0.01rem solid #FFFFFF; display: flex; align-items: center; justify-content: space-between; @@ -49,11 +51,18 @@ letter-spacing: -0.01rem; } + // Logo 图片 + &__logo-img { + width: 1.32rem; + height: 0.34rem; + display: block; + } + // 导航右侧 CTA 按钮 &__btn { padding: 0.1rem 0.32rem; border-radius: 9999px; - background: $accent; + background: #111; color: #fff; font-size: 0.14rem; line-height: 0.2rem; @@ -61,7 +70,7 @@ border: none; cursor: pointer; transition: background 0.2s; - &:hover { background: $accent-hover; } + &:hover { } } } @@ -70,9 +79,41 @@ background: #fff; position: relative; overflow: hidden; + font-size: 0.14rem; + background: radial-gradient(49.26% 24.58% at 46% 0%, #CEF0F2 0%, #FFFFFF 100%); + // 背景色块 — 顶部 + &__orb { + position: absolute; + border-radius: 3.5rem; + filter: blur(1.2rem); + pointer-events: none; + z-index: 0; + background: rgba(82, 202, 209, 0.3); + + &--top { + left: 40%; + transform: translateX(-50%); + top: -1.2rem; + width: 8rem; + height: 2.2rem; + background: rgba(82, 202, 209, 0.2); + opacity: 0; + } + + // 背景色块 — 底部 + &--bottom { + left: 40%; + transform: translateX(-50%); + bottom: -2.4rem; + width: 4rem; + height: 2rem; + } + } // Hero 内容容器 — 左右两栏布局 &__inner { + position: relative; + z-index: 1; width: 12rem; margin: 0 auto; display: flex; @@ -251,6 +292,9 @@ &__inner { width: 12rem; margin: 0 auto; + display: flex; + align-items: center; + justify-content: space-between; } // 标题区 @@ -282,7 +326,6 @@ // 单个统计卡片 .stat-card { - flex: 1; background: #fff; border-radius: 0.48rem; padding: 0.5rem; @@ -311,6 +354,7 @@ .home-jobs-showcase { padding: 1rem 0; background: rgba(82, 202, 209, 0.05); + background: linear-gradient(to top, rgba(82, 202, 209, 0.05), rgba(82, 202, 209, 0.0001)); // 内容容器 &__inner { @@ -388,6 +432,7 @@ border-radius: 0.12rem; padding: 0.2rem 0.24rem; min-width: 2.8rem; + text-align: left; // 公司名 + 时间 &__company { @@ -451,7 +496,7 @@ gap: 0.12rem; padding: 0.2rem 0.4rem; border-radius: 9999px; - background: $btn-dark; + background: #111111; color: #fff; font-size: 0.18rem; line-height: 0.28rem; @@ -459,8 +504,14 @@ border: none; cursor: pointer; transition: background 0.2s; - &:hover { background: $btn-dark-hover; } - svg { flex-shrink: 0; } + &:hover { + //background: $btn-dark-hover; + //background: $btn-dark; + } + svg { + flex-shrink: 0; + color: $btn-dark; + } } // 视觉展示区(右侧/左侧) @@ -470,7 +521,7 @@ display: flex; align-items: center; justify-content: center; - background: rgba(82, 202, 209, 0.05); + background: linear-gradient(157.19deg, #E8F8F9 0%, #FBFDF7 100%); border-radius: 0.48rem; padding: 0.48rem; min-height: 3.87rem; @@ -584,7 +635,7 @@ padding: 0.32rem; width: 100%; max-width: 5.6rem; - transform: rotate(-2deg); + //transform: rotate(-2deg); // 顶部状态栏 &__header { @@ -659,7 +710,7 @@ &__title { font-size: 0.14rem; line-height: 0.2rem; - font-weight: 600; + //font-weight: 600; color: #111; margin: 0; } @@ -686,6 +737,13 @@ justify-content: center; // 简历文档 + &__doc--border { + background: #F9FDFD; + padding: 0.2rem; + border-radius: 0.25rem; + border: 0.62px solid #FFFFFF; + box-shadow: 0 0.15rem 0.31rem -0.074rem rgba(0, 0, 0, 0.3); + } &__doc { width: 2rem; aspect-ratio: 3/4; @@ -693,17 +751,18 @@ border: 1.2px dashed #e5e7eb; border-radius: 0.16rem; padding: 0.16rem; + } // ATS 优化徽章 &__badge { position: absolute; bottom: -0.1rem; - left: -0.1rem; - background: $btn-dark; + left: -0.12rem; + background: #111111; color: #fff; - padding: 0.12rem 0.2rem; - border-radius: 0.24rem; + padding: 0.16rem 0.2rem; + border-radius: 0.15rem; display: flex; align-items: center; gap: 0.08rem; @@ -712,7 +771,6 @@ span { font-size: 0.09rem; line-height: 0.09rem; - font-weight: 900; letter-spacing: 0.01rem; } } @@ -774,6 +832,7 @@ backdrop-filter: blur(56px); border-radius: 0.32rem; padding: 0.2rem; + font-size: 0.14rem; // 奇数列偏移效果 &--offset { @@ -890,10 +949,44 @@ // ==================== 用户评价区 ==================== .home-testimonials { - padding: 1.2rem 0; - background: rgba(82, 202, 209, 0.03); - backdrop-filter: blur(100px); - overflow: hidden; + position: relative; + + // 底层背景色块 — 左上角 + &__orb { + position: absolute; + border-radius: 3.5rem; + filter: blur(1.2rem); + pointer-events: none; + z-index: 0; + + &--left { + left: -0.11rem; + top: 0.6rem; + width: 5.95rem; + height: 5.95rem; + background: #52CAD1; + } + + // 底层背景色块 — 右下角 + &--right { + right: -0.5rem; + bottom: 0.5rem; + width: 6rem; + height: 6rem; + background: #6DE5B6; + } + } + + // 内容层白色容器 — 半透明白底透出色块 + &__container { + position: relative; + z-index: 1; + background: rgba(255, 255, 255, 0.8); + border-radius: 1rem; + backdrop-filter: blur(100px); + padding: 0.8rem 1.2rem 0.6rem; + box-shadow: inset 0 0 0.9rem rgba(255, 255, 255, 0.5); + } // 标题区 &__header { @@ -905,6 +998,7 @@ line-height: 0.48rem; font-weight: 600; letter-spacing: -0.012rem; + color: #111; } p { @@ -913,56 +1007,84 @@ color: #9ca3af; letter-spacing: 0.016rem; text-transform: uppercase; - margin-top: 0.12rem; + margin-top: 0.24rem; } } - // 创始人引言卡片 + // 评价卡片横向排列 + &__cards { + display: flex; + gap: 0.24rem; + justify-content: center; + margin-bottom: 0.48rem; + } + + // 创始人引言深色卡片 &__founder { width: 12rem; - margin: 0 auto 0.8rem; - background: $btn-dark; - color: #fff; + background: #1a1a2e; border-radius: 0.48rem; - padding: 0.48rem; + padding: 0.48rem 0.6rem; + display: flex; + align-items: center; + position: relative; + overflow: hidden; + margin: 0 auto; + } + + // 创始人装饰圆环 SVG + &__founder-decor { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + width: 4.55rem; + height: 4.55rem; + pointer-events: none; + } + + // 创始人内容区 + &__founder-content { display: flex; align-items: center; gap: 0.48rem; position: relative; - overflow: hidden; + z-index: 1; + width: 100%; + } - // 创始人头像 - &-img { - width: 1.28rem; - height: 1.28rem; - border-radius: 0.24rem; - background: rgba(255, 255, 255, 0.1); - display: flex; - align-items: center; - justify-content: center; - font-size: 0.6rem; - line-height: 1; - flex-shrink: 0; - } + // 创始人头像 + &__founder-img { + width: 1.28rem; + height: 1.28rem; + border-radius: 0.24rem; + object-fit: cover; + flex-shrink: 0; + } + + // 创始人文字区 + &__founder-text { + flex: 1; - // 引言文字 blockquote { font-size: 0.24rem; line-height: 0.39rem; + color: #fff; margin: 0; } - // 署名 cite { display: block; margin-top: 0.24rem; font-style: normal; line-height: 0.28rem; + .cite-name { font-size: 0.2rem; color: $accent; font-weight: 600; } + .cite-role { font-size: 0.14rem; color: #999; @@ -971,21 +1093,35 @@ } } - // 评价卡片横向滚动容器 - &__scroll { + // 分页小圆点 + &__dots { display: flex; - gap: 0.24rem; - padding: 0 1.2rem; - overflow-x: auto; - &::-webkit-scrollbar { display: none; } + justify-content: center; + gap: 0.1rem; + margin-top: 0.4rem; + } + + &__dot { + width: 0.12rem; + height: 0.12rem; + border-radius: 50%; + background: #111; + opacity: 0.3; + cursor: pointer; + transition: opacity 0.3s; + + &--active { + opacity: 1; + } } } // 单个评价卡片 .testimonial-card { - flex-shrink: 0; - min-width: 3.71rem; - background: rgba(255, 255, 255, 0.9); + flex: 1; + min-width: 0; + max-width: 3.71rem; + background: #fff; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.05); border-radius: 0.4rem; padding: 0.3rem; @@ -995,7 +1131,7 @@ &__quote { position: absolute; right: 0.3rem; - top: 0.3rem; + bottom: 0.3rem; opacity: 0.3; } @@ -1020,9 +1156,15 @@ width: 0.48rem; height: 0.48rem; border-radius: 50%; - background: linear-gradient(135deg, $accent, #a7f3d0); + object-fit: cover; border: 2px solid #fff; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px -2px rgba(0, 0, 0, 0.1), 0 4px 6px -1px rgba(0, 0, 0, 0.1); + flex-shrink: 0; + } + + // 作者信息容器 + &__info { + min-width: 0; } // 作者姓名 @@ -1217,15 +1359,18 @@ // ==================== 底部 CTA 行动号召 ==================== .home-cta { padding: 0 1.2rem 1.2rem; + width: 12rem; + margin: 0 auto; // CTA 内容区 &__inner { - background: $accent; + background: linear-gradient(223.56deg, #52CAD1 0%, #53D9C8 100%); border-radius: 0.6rem; - padding: 1.28rem 0; + padding: 1.0rem 0; text-align: center; position: relative; overflow: hidden; + line-height: 1; h2 { font-size: 0.52rem; @@ -1239,10 +1384,10 @@ // CTA 按钮 &__btn { - margin-top: 0.4rem; + margin-top: 0.2rem; padding: 0.24rem 0.64rem; border-radius: 9999px; - background: $btn-dark; + background: #111; color: #fff; font-size: 0.2rem; line-height: 0.28rem; @@ -1253,15 +1398,16 @@ z-index: 1; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); transition: background 0.2s; - &:hover { background: $btn-dark-hover; } + &:hover { background: #111; } } } // ==================== 页脚 ==================== .home-footer { - background: #fff; + background: #111; border-top: 1px solid #f3f4f6; padding: 0.8rem 0 0.4rem; + line-height: 1; // 页脚内容 — 四列横向排列 &__inner { @@ -1282,7 +1428,7 @@ font-size: 0.16rem; line-height: 0.24rem; font-weight: 500; - color: #111; + color: #fff; margin: 0 0 0.32rem; } @@ -1309,9 +1455,17 @@ // Logo &__logo { margin-bottom: 0.16rem; + font-size: 0.14rem; } - // Logo 文字 + // Logo 图片 + &__logo-img { + width: 1.43rem; + height: 0.355rem; + display: block; + } + + // Logo 文字(保留备用) &__logo-text { font-size: 0.22rem; line-height: 0.34rem; @@ -1331,7 +1485,6 @@ width: 12rem; margin: 0.6rem auto 0; text-align: center; - border-top: 1px solid #f3f4f6; padding-top: 0.4rem; p { diff --git a/src/assets/styles/pages/jobs.scss b/src/assets/styles/pages/jobs.scss index 1fe902b..a906841 100644 --- a/src/assets/styles/pages/jobs.scss +++ b/src/assets/styles/pages/jobs.scss @@ -615,6 +615,14 @@ } } + // 暂无数据提示 + &__empty { + text-align: center; + padding: 1rem 0; + font-size: 0.14rem; + color: $text-light; + } + // 加载更多提示 &__loading-more { text-align: center; diff --git a/src/assets/styles/pages/resume-detail.scss b/src/assets/styles/pages/resume-detail.scss index 939ee73..a35ab07 100644 --- a/src/assets/styles/pages/resume-detail.scss +++ b/src/assets/styles/pages/resume-detail.scss @@ -12,6 +12,7 @@ background: $bg-main; display: flex; flex-direction: column; + font-size: 0.14rem; } &__page-title { @@ -130,10 +131,11 @@ display: flex; align-items: center; justify-content: center; - color: $bg-white; + color: $text-dark; font-size: 0.18rem; font-weight: 700; flex-shrink: 0; + background: $bg-main; } &__score-badge { @@ -317,12 +319,37 @@ margin-bottom: 0; } + // ---- 编辑按钮(与 ProfilePageContent 一致) ---- + &__edit-btn { + background: none; + border: none; + color: $text-light; + cursor: pointer; + padding: 0.04rem; + border-radius: 0.04rem; + display: flex; + align-items: center; + transition: all 0.2s; + flex-shrink: 0; + + &:hover { + color: $accent; + background: $theme-color; + } + } + + &__edit-icon { + width: 0.15rem; + height: 0.15rem; + } + // ---- 教育背景 ---- &__edu-item { - padding: 0.1rem 0; - + margin-top: 0.16rem; + padding-left: 0.1rem; + border-left: 2px solid $border-color; &:not(:last-child) { - border-bottom: 1px solid $border-color; + } } @@ -339,12 +366,15 @@ line-height: 1.6; } - // ---- 工作经验 ---- + // ---- 工作经历 ---- &__exp-item { - padding: 0.1rem 0; + margin-top: 0.16rem; + margin-left: 0.1rem; + padding-left: 0.1rem; + border-left: 2px solid $border-color; &:not(:last-child) { - border-bottom: 1px solid $border-color; + //border-bottom: 1px solid $border-color; } } @@ -390,4 +420,47 @@ background: $theme-color; } } + + // ---- 卡片底部问题操作区 ---- + &__card-issue { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 0.1rem; + margin-top: 0.18rem; + padding-top: 0.14rem; + } + + // 问题类型按钮组(三选一) + &__issue-type-group { + display: flex; + align-items: center; + gap: 0.06rem; + } + + // 问题类型按钮 + &__issue-type-btn { + font-size: 0.12rem; + border-radius: 0.16rem; + padding: 0.05rem 0.14rem; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; + border: 1px solid $border-color; + background: $bg-main; + color: $text-light; + + &:hover { + border-color: $accent; + color: $accent; + background: $theme-color; + } + + // 选中态 + &--active { + background: $theme-color; + color: $accent; + border-color: $accent; + } + } } diff --git a/src/assets/styles/pages/resume.scss b/src/assets/styles/pages/resume.scss index 34a7081..2a2e0ec 100644 --- a/src/assets/styles/pages/resume.scss +++ b/src/assets/styles/pages/resume.scss @@ -188,3 +188,16 @@ } } } + +// ==================== 上传简历加载提示 ==================== +.resume-upload-loading { + .el-loading-spinner { + .circular .path { + stroke: $accent; + } + + .el-loading-text { + color: $accent; + } + } +} diff --git a/src/components/JobResumeCustomDialog.vue b/src/components/JobResumeCustomDialog.vue index 5303fb1..1724487 100644 --- a/src/components/JobResumeCustomDialog.vue +++ b/src/components/JobResumeCustomDialog.vue @@ -304,6 +304,7 @@ diff --git a/src/views/Jobs.vue b/src/views/Jobs.vue index 287b14f..70b0631 100644 --- a/src/views/Jobs.vue +++ b/src/views/Jobs.vue @@ -100,7 +100,7 @@ -
+
+ +
暂无数据
加载中...
没有更多了
diff --git a/src/views/Resume.vue b/src/views/Resume.vue index d240c99..1b74130 100644 --- a/src/views/Resume.vue +++ b/src/views/Resume.vue @@ -77,15 +77,22 @@ diff --git a/vite.config.ts b/vite.config.ts index b817698..f3f7f21 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -40,6 +40,11 @@ export default defineConfig({ target: 'http://127.0.0.1:8080', changeOrigin: true, }, + '/ai-api': { + target: 'http://192.168.31.133:8000', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/ai-api/, ''), + }, }, }, css: {