作者 202304001

0820提交

1 -<!DOCTYPE html> 1 +<!doctype html>
2 <html lang=""> 2 <html lang="">
3 <head> 3 <head>
4 - <meta charset="UTF-8">  
5 - <link rel="icon" href="/favicon.ico"> 4 + <meta charset="UTF-8" />
  5 + <link rel="icon" href="/favicon.ico" />
6 <script src="https://cdn.tailwindcss.com"></script> 6 <script src="https://cdn.tailwindcss.com"></script>
7 - <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">  
8 - <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 + <link
  8 + href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css"
  9 + rel="stylesheet"
  10 + />
  11 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9 <title>Vite App</title> 12 <title>Vite App</title>
10 </head> 13 </head>
11 <body> 14 <body>
12 <div id="app"></div> 15 <div id="app"></div>
13 <script type="module" src="/src/main.js"></script> 16 <script type="module" src="/src/main.js"></script>
  17 + <script type="text/javascript" src="/config.js"></script>
14 </body> 18 </body>
15 </html> 19 </html>
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 "name": "aigeo", 8 "name": "aigeo",
9 "version": "0.0.0", 9 "version": "0.0.0",
10 "dependencies": { 10 "dependencies": {
  11 + "axios": "^1.11.0",
11 "chart.js": "^4.5.0", 12 "chart.js": "^4.5.0",
12 "pinia": "^3.0.3", 13 "pinia": "^3.0.3",
13 "sass": "^1.90.0", 14 "sass": "^1.90.0",
@@ -2009,6 +2010,12 @@ @@ -2009,6 +2010,12 @@
2009 "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 2010 "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
2010 "license": "MIT" 2011 "license": "MIT"
2011 }, 2012 },
  2013 + "node_modules/asynckit": {
  2014 + "version": "0.4.0",
  2015 + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
  2016 + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
  2017 + "license": "MIT"
  2018 + },
2012 "node_modules/autoprefixer": { 2019 "node_modules/autoprefixer": {
2013 "version": "10.4.21", 2020 "version": "10.4.21",
2014 "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", 2021 "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
@@ -2047,6 +2054,17 @@ @@ -2047,6 +2054,17 @@
2047 "postcss": "^8.1.0" 2054 "postcss": "^8.1.0"
2048 } 2055 }
2049 }, 2056 },
  2057 + "node_modules/axios": {
  2058 + "version": "1.11.0",
  2059 + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz",
  2060 + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
  2061 + "license": "MIT",
  2062 + "dependencies": {
  2063 + "follow-redirects": "^1.15.6",
  2064 + "form-data": "^4.0.4",
  2065 + "proxy-from-env": "^1.1.0"
  2066 + }
  2067 + },
2050 "node_modules/balanced-match": { 2068 "node_modules/balanced-match": {
2051 "version": "1.0.2", 2069 "version": "1.0.2",
2052 "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", 2070 "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -2144,6 +2162,19 @@ @@ -2144,6 +2162,19 @@
2144 "url": "https://github.com/sponsors/sindresorhus" 2162 "url": "https://github.com/sponsors/sindresorhus"
2145 } 2163 }
2146 }, 2164 },
  2165 + "node_modules/call-bind-apply-helpers": {
  2166 + "version": "1.0.2",
  2167 + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
  2168 + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
  2169 + "license": "MIT",
  2170 + "dependencies": {
  2171 + "es-errors": "^1.3.0",
  2172 + "function-bind": "^1.1.2"
  2173 + },
  2174 + "engines": {
  2175 + "node": ">= 0.4"
  2176 + }
  2177 + },
2147 "node_modules/camelcase-css": { 2178 "node_modules/camelcase-css": {
2148 "version": "2.0.1", 2179 "version": "2.0.1",
2149 "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", 2180 "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -2240,6 +2271,18 @@ @@ -2240,6 +2271,18 @@
2240 "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 2271 "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
2241 "license": "MIT" 2272 "license": "MIT"
2242 }, 2273 },
  2274 + "node_modules/combined-stream": {
  2275 + "version": "1.0.8",
  2276 + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
  2277 + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
  2278 + "license": "MIT",
  2279 + "dependencies": {
  2280 + "delayed-stream": "~1.0.0"
  2281 + },
  2282 + "engines": {
  2283 + "node": ">= 0.8"
  2284 + }
  2285 + },
2243 "node_modules/commander": { 2286 "node_modules/commander": {
2244 "version": "4.1.1", 2287 "version": "4.1.1",
2245 "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", 2288 "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
@@ -2364,6 +2407,15 @@ @@ -2364,6 +2407,15 @@
2364 "url": "https://github.com/sponsors/sindresorhus" 2407 "url": "https://github.com/sponsors/sindresorhus"
2365 } 2408 }
2366 }, 2409 },
  2410 + "node_modules/delayed-stream": {
  2411 + "version": "1.0.0",
  2412 + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
  2413 + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
  2414 + "license": "MIT",
  2415 + "engines": {
  2416 + "node": ">=0.4.0"
  2417 + }
  2418 + },
2367 "node_modules/detect-libc": { 2419 "node_modules/detect-libc": {
2368 "version": "1.0.3", 2420 "version": "1.0.3",
2369 "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz", 2421 "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
@@ -2389,6 +2441,20 @@ @@ -2389,6 +2441,20 @@
2389 "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 2441 "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
2390 "license": "MIT" 2442 "license": "MIT"
2391 }, 2443 },
  2444 + "node_modules/dunder-proto": {
  2445 + "version": "1.0.1",
  2446 + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
  2447 + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
  2448 + "license": "MIT",
  2449 + "dependencies": {
  2450 + "call-bind-apply-helpers": "^1.0.1",
  2451 + "es-errors": "^1.3.0",
  2452 + "gopd": "^1.2.0"
  2453 + },
  2454 + "engines": {
  2455 + "node": ">= 0.4"
  2456 + }
  2457 + },
2392 "node_modules/eastasianwidth": { 2458 "node_modules/eastasianwidth": {
2393 "version": "0.2.0", 2459 "version": "0.2.0",
2394 "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 2460 "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2430,6 +2496,51 @@ @@ -2430,6 +2496,51 @@
2430 "url": "https://github.com/sponsors/antfu" 2496 "url": "https://github.com/sponsors/antfu"
2431 } 2497 }
2432 }, 2498 },
  2499 + "node_modules/es-define-property": {
  2500 + "version": "1.0.1",
  2501 + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
  2502 + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
  2503 + "license": "MIT",
  2504 + "engines": {
  2505 + "node": ">= 0.4"
  2506 + }
  2507 + },
  2508 + "node_modules/es-errors": {
  2509 + "version": "1.3.0",
  2510 + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
  2511 + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
  2512 + "license": "MIT",
  2513 + "engines": {
  2514 + "node": ">= 0.4"
  2515 + }
  2516 + },
  2517 + "node_modules/es-object-atoms": {
  2518 + "version": "1.1.1",
  2519 + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
  2520 + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
  2521 + "license": "MIT",
  2522 + "dependencies": {
  2523 + "es-errors": "^1.3.0"
  2524 + },
  2525 + "engines": {
  2526 + "node": ">= 0.4"
  2527 + }
  2528 + },
  2529 + "node_modules/es-set-tostringtag": {
  2530 + "version": "2.1.0",
  2531 + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
  2532 + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
  2533 + "license": "MIT",
  2534 + "dependencies": {
  2535 + "es-errors": "^1.3.0",
  2536 + "get-intrinsic": "^1.2.6",
  2537 + "has-tostringtag": "^1.0.2",
  2538 + "hasown": "^2.0.2"
  2539 + },
  2540 + "engines": {
  2541 + "node": ">= 0.4"
  2542 + }
  2543 + },
2433 "node_modules/esbuild": { 2544 "node_modules/esbuild": {
2434 "version": "0.25.8", 2545 "version": "0.25.8",
2435 "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz", 2546 "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
@@ -2595,6 +2706,26 @@ @@ -2595,6 +2706,26 @@
2595 "node": ">=8" 2706 "node": ">=8"
2596 } 2707 }
2597 }, 2708 },
  2709 + "node_modules/follow-redirects": {
  2710 + "version": "1.15.11",
  2711 + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
  2712 + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
  2713 + "funding": [
  2714 + {
  2715 + "type": "individual",
  2716 + "url": "https://github.com/sponsors/RubenVerborgh"
  2717 + }
  2718 + ],
  2719 + "license": "MIT",
  2720 + "engines": {
  2721 + "node": ">=4.0"
  2722 + },
  2723 + "peerDependenciesMeta": {
  2724 + "debug": {
  2725 + "optional": true
  2726 + }
  2727 + }
  2728 + },
2598 "node_modules/foreground-child": { 2729 "node_modules/foreground-child": {
2599 "version": "3.3.1", 2730 "version": "3.3.1",
2600 "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", 2731 "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -2611,6 +2742,22 @@ @@ -2611,6 +2742,22 @@
2611 "url": "https://github.com/sponsors/isaacs" 2742 "url": "https://github.com/sponsors/isaacs"
2612 } 2743 }
2613 }, 2744 },
  2745 + "node_modules/form-data": {
  2746 + "version": "4.0.4",
  2747 + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
  2748 + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
  2749 + "license": "MIT",
  2750 + "dependencies": {
  2751 + "asynckit": "^0.4.0",
  2752 + "combined-stream": "^1.0.8",
  2753 + "es-set-tostringtag": "^2.1.0",
  2754 + "hasown": "^2.0.2",
  2755 + "mime-types": "^2.1.12"
  2756 + },
  2757 + "engines": {
  2758 + "node": ">= 6"
  2759 + }
  2760 + },
2614 "node_modules/fraction.js": { 2761 "node_modules/fraction.js": {
2615 "version": "4.3.7", 2762 "version": "4.3.7",
2616 "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", 2763 "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
@@ -2658,6 +2805,43 @@ @@ -2658,6 +2805,43 @@
2658 "node": ">=6.9.0" 2805 "node": ">=6.9.0"
2659 } 2806 }
2660 }, 2807 },
  2808 + "node_modules/get-intrinsic": {
  2809 + "version": "1.3.0",
  2810 + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
  2811 + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
  2812 + "license": "MIT",
  2813 + "dependencies": {
  2814 + "call-bind-apply-helpers": "^1.0.2",
  2815 + "es-define-property": "^1.0.1",
  2816 + "es-errors": "^1.3.0",
  2817 + "es-object-atoms": "^1.1.1",
  2818 + "function-bind": "^1.1.2",
  2819 + "get-proto": "^1.0.1",
  2820 + "gopd": "^1.2.0",
  2821 + "has-symbols": "^1.1.0",
  2822 + "hasown": "^2.0.2",
  2823 + "math-intrinsics": "^1.1.0"
  2824 + },
  2825 + "engines": {
  2826 + "node": ">= 0.4"
  2827 + },
  2828 + "funding": {
  2829 + "url": "https://github.com/sponsors/ljharb"
  2830 + }
  2831 + },
  2832 + "node_modules/get-proto": {
  2833 + "version": "1.0.1",
  2834 + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
  2835 + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
  2836 + "license": "MIT",
  2837 + "dependencies": {
  2838 + "dunder-proto": "^1.0.1",
  2839 + "es-object-atoms": "^1.0.0"
  2840 + },
  2841 + "engines": {
  2842 + "node": ">= 0.4"
  2843 + }
  2844 + },
2661 "node_modules/get-stream": { 2845 "node_modules/get-stream": {
2662 "version": "9.0.1", 2846 "version": "9.0.1",
2663 "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz", 2847 "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz",
@@ -2707,6 +2891,45 @@ @@ -2707,6 +2891,45 @@
2707 "node": ">=10.13.0" 2891 "node": ">=10.13.0"
2708 } 2892 }
2709 }, 2893 },
  2894 + "node_modules/gopd": {
  2895 + "version": "1.2.0",
  2896 + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
  2897 + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
  2898 + "license": "MIT",
  2899 + "engines": {
  2900 + "node": ">= 0.4"
  2901 + },
  2902 + "funding": {
  2903 + "url": "https://github.com/sponsors/ljharb"
  2904 + }
  2905 + },
  2906 + "node_modules/has-symbols": {
  2907 + "version": "1.1.0",
  2908 + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
  2909 + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
  2910 + "license": "MIT",
  2911 + "engines": {
  2912 + "node": ">= 0.4"
  2913 + },
  2914 + "funding": {
  2915 + "url": "https://github.com/sponsors/ljharb"
  2916 + }
  2917 + },
  2918 + "node_modules/has-tostringtag": {
  2919 + "version": "1.0.2",
  2920 + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
  2921 + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
  2922 + "license": "MIT",
  2923 + "dependencies": {
  2924 + "has-symbols": "^1.0.3"
  2925 + },
  2926 + "engines": {
  2927 + "node": ">= 0.4"
  2928 + },
  2929 + "funding": {
  2930 + "url": "https://github.com/sponsors/ljharb"
  2931 + }
  2932 + },
2710 "node_modules/hasown": { 2933 "node_modules/hasown": {
2711 "version": "2.0.2", 2934 "version": "2.0.2",
2712 "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", 2935 "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
@@ -3016,6 +3239,15 @@ @@ -3016,6 +3239,15 @@
3016 "@jridgewell/sourcemap-codec": "^1.5.0" 3239 "@jridgewell/sourcemap-codec": "^1.5.0"
3017 } 3240 }
3018 }, 3241 },
  3242 + "node_modules/math-intrinsics": {
  3243 + "version": "1.1.0",
  3244 + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
  3245 + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
  3246 + "license": "MIT",
  3247 + "engines": {
  3248 + "node": ">= 0.4"
  3249 + }
  3250 + },
3019 "node_modules/merge2": { 3251 "node_modules/merge2": {
3020 "version": "1.4.1", 3252 "version": "1.4.1",
3021 "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", 3253 "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
@@ -3050,6 +3282,27 @@ @@ -3050,6 +3282,27 @@
3050 "url": "https://github.com/sponsors/jonschlinkert" 3282 "url": "https://github.com/sponsors/jonschlinkert"
3051 } 3283 }
3052 }, 3284 },
  3285 + "node_modules/mime-db": {
  3286 + "version": "1.52.0",
  3287 + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
  3288 + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
  3289 + "license": "MIT",
  3290 + "engines": {
  3291 + "node": ">= 0.6"
  3292 + }
  3293 + },
  3294 + "node_modules/mime-types": {
  3295 + "version": "2.1.35",
  3296 + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
  3297 + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
  3298 + "license": "MIT",
  3299 + "dependencies": {
  3300 + "mime-db": "1.52.0"
  3301 + },
  3302 + "engines": {
  3303 + "node": ">= 0.6"
  3304 + }
  3305 + },
3053 "node_modules/minimatch": { 3306 "node_modules/minimatch": {
3054 "version": "9.0.5", 3307 "version": "9.0.5",
3055 "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", 3308 "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
@@ -3623,6 +3876,12 @@ @@ -3623,6 +3876,12 @@
3623 "url": "https://github.com/sponsors/sindresorhus" 3876 "url": "https://github.com/sponsors/sindresorhus"
3624 } 3877 }
3625 }, 3878 },
  3879 + "node_modules/proxy-from-env": {
  3880 + "version": "1.1.0",
  3881 + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
  3882 + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
  3883 + "license": "MIT"
  3884 + },
3626 "node_modules/queue-microtask": { 3885 "node_modules/queue-microtask": {
3627 "version": "1.2.3", 3886 "version": "1.2.3",
3628 "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", 3887 "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 "format": "prettier --write src/" 13 "format": "prettier --write src/"
14 }, 14 },
15 "dependencies": { 15 "dependencies": {
  16 + "axios": "^1.11.0",
16 "chart.js": "^4.5.0", 17 "chart.js": "^4.5.0",
17 "pinia": "^3.0.3", 18 "pinia": "^3.0.3",
18 "sass": "^1.90.0", 19 "sass": "^1.90.0",
  1 +window.PLATFROM_CONFIG = {
  2 + DEV_BASE_URL: 'http://erpapitest.yiwaixiao.net:80', // 开发环境
  3 + PRO_BASE_URL: 'http://erpapi.yiwaixiao.net', // 正式环境
  4 +}
  5 +
  6 +
  7 +document.cookie = 'token=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNzU1ODg4NDM3MDUiLCJzdWIiOiJ5aXdhaXhpYW8ubmV0IiwidXNlcl9pZCI6Mzc4LCJsb2dpbmlkIjoiaW5mbyIsImRvbWFpbiI6IjIxZ21haWwuY29tIiwiY29tcGFueV9pZCI6NywicGFyYW1fbGV2ZWwiOjAsImxvZ3BhdGgiOiJlOlxcbG9nc1xcMjFnbWFpbC5jb21cXGluZm9cXGVycm9yXFwiLCJpYXQiOjE3NTU0ODg1MjIsImV4cCI6MTc1NTQ5NTcyMn0.B7FNn8Zk-t2SvGUwHRUURLn_2QsJNX2lJX-w8pGJhRw';
  1 +import request from '@/utils/request'
  2 +
  3 +export const uploadFile = (data) => {
  4 + return request({
  5 + url: '/ai/upload',
  6 + method: 'post',
  7 + data
  8 + })
  9 +}
  10 +
  11 +export const saveHtml = (data) => {
  12 + return request({
  13 + url: '/ai/saveHtml',
  14 + method: 'post',
  15 + data
  16 + })
  17 +}
  18 +
  19 +export const saveDocx = (data) => {
  20 + return request({
  21 + url: '/ai/generateFile',
  22 + method: 'post',
  23 + data
  24 + })
  25 +}
  26 +
  27 +export const uploadFileToDataset = (data) => {
  28 + return request({
  29 + url: '/ai/uploadFileToDataset',
  30 + method: 'post',
  31 + data
  32 + })
  33 +}
  34 +
  35 +
  36 +//<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>登录页面</title> <style> body { font-family: Arial, sans-serif; background-color: #f4f4f4; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .login-container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } h2 { margin-bottom: 20px; } input[type="text"], input[type="password"] { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ccc; border-radius: 3px; } input[type="submit"] { background-color: #4CAF50; color: white; border: none; padding: 10px; cursor: pointer; border-radius: 3px; width: 100%; } input[type="submit"]:hover { background-color: #45a049; } </style> </head> <body> <div class="login-container"> <h2>用户登录</h2> <form action="/login" method="POST"> <label for="username">用户名:</label> <input type="text" id="username" name="username" required> <label for="password">密码:</label> <input type="password" id="password" name="password" required> <input type="submit" value="登录"> </form> </div> </body> </html>
@@ -86,4 +86,18 @@ @@ -86,4 +86,18 @@
86 <script setup> 86 <script setup>
87 87
88 </script> 88 </script>
89 -<style scoped></style> 89 +<style scoped>
  90 +@import 'tailwindcss/base';
  91 +@import 'tailwindcss/components';
  92 +@import 'tailwindcss/utilities';
  93 +
  94 +@layer utilities {
  95 + .content-auto {
  96 + content-visibility: auto;
  97 + }
  98 +
  99 + .sidebar-item-active {
  100 + @apply bg-primary/10 text-primary border-l-4 border-primary;
  101 + }
  102 +}
  103 +</style>
1 import './assets/main.css' 1 import './assets/main.css'
  2 +import './utils/config.js'
2 3
3 import { createApp } from 'vue' 4 import { createApp } from 'vue'
4 import { createPinia } from 'pinia' 5 import { createPinia } from 'pinia'
@@ -13,6 +13,11 @@ const router = createRouter({ @@ -13,6 +13,11 @@ const router = createRouter({
13 name: 'about', 13 name: 'about',
14 component: () => import('../views/test.vue'), 14 component: () => import('../views/test.vue'),
15 }, 15 },
  16 + {
  17 + path: '/test1',
  18 + name: 'test1',
  19 + component: () => import('../views/test1.vue'),
  20 + },
16 ], 21 ],
17 }) 22 })
18 23
  1 +export const MAX_SIZE = 1024 * 1024 * 5;
  2 +export const INVALID_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
  3 +
  4 +export const getCookie = (name) => {
  5 + const cookieArr = document.cookie.split('; ');
  6 + for (let item of cookieArr) {
  7 + const [key, value] = item.split('=');
  8 + if (key === name) {
  9 + return decodeURIComponent(value);
  10 + }
  11 + }
  12 + return null;
  13 +}
  1 +import axios from 'axios'
  2 +
  3 +// 根据环境获取基础URL
  4 +// 从全局window对象获取配置
  5 +const config = window.PLATFROM_CONFIG || {}
  6 +// 添加调试信息
  7 +const BASE_URL = import.meta.env.DEV ? config.DEV_BASE_URL : config.PRO_BASE_URL;
  8 +// const API_BASE_URL="http://webdemo.yiwaixiao.net/"
  9 +// 创建axios实例
  10 +const request = axios.create({
  11 + baseURL: BASE_URL,
  12 + timeout: 60000,
  13 + headers: {
  14 + 'Content-Type': 'application/x-www-form-urlencoded',
  15 + 'token': 'eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNzU1ODg4NDM3MDUiLCJzdWIiOiJ5aXdhaXhpYW8ubmV0IiwidXNlcl9pZCI6Mzc4LCJsb2dpbmlkIjoiaW5mbyIsImRvbWFpbiI6IjIxZ21haWwuY29tIiwiY29tcGFueV9pZCI6NywicGFyYW1fbGV2ZWwiOjAsImxvZ3BhdGgiOiJkOlxcbG9nc1xcMjFnbWFpbC5jb20iLCJpYXQiOjE3NTUwNTQ5NTksImV4cCI6MTc1Njc4Mjk1OX0.WfXb6Ol7ka8n3MHgnndor2la6VzmtBHpjZngsNjwHn0'
  16 + }
  17 +})
  18 +
  19 +// 请求拦截器
  20 +request.interceptors.request.use(
  21 + config => {
  22 + console.log("config:", config)
  23 + console.log(BASE_URL)
  24 + return config
  25 + },
  26 + error => {
  27 + return Promise.reject(error)
  28 + }
  29 +)
  30 +
  31 +// 响应拦截器
  32 +request.interceptors.response.use(
  33 + response => {
  34 + return response.data
  35 + },
  36 + error => {
  37 + // if (error.response?.status == '800') {
  38 + // // token过期处理
  39 + // const userStore = useUserStore()
  40 + // userStore.clearToken()
  41 + // window.location.href = '/login'
  42 + // }
  43 + return Promise.reject(error)
  44 + }
  45 +)
  46 +
  47 +export default request
@@ -74,7 +74,7 @@ @@ -74,7 +74,7 @@
74 <div class="mb-6"> 74 <div class="mb-6">
75 <h2 class="text-[clamp(1.5rem,3vw,2rem)] font-bold">控制台</h2> 75 <h2 class="text-[clamp(1.5rem,3vw,2rem)] font-bold">控制台</h2>
76 <p class="text-gray-500 mt-1">欢迎回来,今天是 <span class="font-medium" id="current-date">{{ currentDate 76 <p class="text-gray-500 mt-1">欢迎回来,今天是 <span class="font-medium" id="current-date">{{ currentDate
77 - }}</span> 77 + }}</span>
78 </p> 78 </p>
79 </div> 79 </div>
80 80
@@ -541,6 +541,7 @@ @@ -541,6 +541,7 @@
541 import siderbar from '@/components/siderbar.vue'; 541 import siderbar from '@/components/siderbar.vue';
542 import { ref, onMounted } from 'vue'; 542 import { ref, onMounted } from 'vue';
543 import { Chart, registerables } from 'chart.js'; 543 import { Chart, registerables } from 'chart.js';
  544 +import { getCookie } from '@/utils/config';
544 Chart.register(...registerables); 545 Chart.register(...registerables);
545 546
546 // 状态管理 547 // 状态管理
@@ -963,6 +964,8 @@ onMounted(() => { @@ -963,6 +964,8 @@ onMounted(() => {
963 userDropdownOpen.value = false; 964 userDropdownOpen.value = false;
964 } 965 }
965 }); 966 });
  967 + const token = getCookie('token')
  968 + console.log(token)
966 }); 969 });
967 970
968 tailwind.config = { 971 tailwind.config = {
@@ -1033,8 +1036,4 @@ tailwind.config = { @@ -1033,8 +1036,4 @@ tailwind.config = {
1033 @apply opacity-100 translate-y-0; 1036 @apply opacity-100 translate-y-0;
1034 } 1037 }
1035 } 1038 }
1036 -  
1037 -.btn-primary {  
1038 - background-color: rgb(0, 119, 255);  
1039 -}  
1040 </style> 1039 </style>
  1 +<script setup>
  2 +import { saveDocx, saveHtml, uploadFile, uploadFileToDataset } from '@/api/aiapi';
  3 +import { ref, onMounted, computed } from 'vue';
  4 +import { MAX_SIZE, INVALID_TYPES } from '@/utils/config.js';
  5 +const messages = ref([]);
  6 +const openaiMessages = ref([]);
  7 +const inputMessage = ref('');
  8 +const isStreaming = ref(false);
  9 +const ws = ref(null);
  10 +const isConnected = ref(false);
  11 +const currentMode = ref('Dify');
  12 +const modelOptions = ref(['gpt-4o-mini', 'o3-mini', 'gpt-5-mini', 'dall-e-3', 'gpt-3.5-turbo']);
  13 +const selectedModel = ref('gpt-4o-mini');
  14 +const fileUrl = ref('');
  15 +const showFile = ref(false);
  16 +
  17 +const triggerMode = () => {
  18 + if (currentMode.value === 'Dify') {
  19 + currentMode.value = 'OpenAI';
  20 + } else {
  21 + currentMode.value = 'Dify';
  22 + }
  23 +};
  24 +
  25 +const getCurrentMessages = () => {
  26 + return currentMode.value === 'Dify' ? messages.value : openaiMessages.value;
  27 +};
  28 +
  29 +onMounted(() => {
  30 + // 初始化WebSocket连接
  31 + ws.value = new WebSocket('ws://192.168.0.24/ws/chat/378');
  32 +
  33 + ws.value.onopen = () => {
  34 + isConnected.value = true;
  35 + };
  36 +
  37 + ws.value.onmessage = (event) => {
  38 + const response = JSON.parse(event.data);
  39 + console.log("*-**-*--------*", response)
  40 + if (response.code === 200) {
  41 + // 渲染AI回复
  42 + let aiMessage;
  43 + if (response.task_id !== null) {
  44 + aiMessage = messages.value.find(m => m.id === response.id && m.role === 'ai');
  45 + } else {
  46 + openaiMessages.value.map(item => {
  47 + console.log(item.id, response.id, item.id === response.id)
  48 + if (item.id === response.id) {
  49 + aiMessage = item
  50 + }
  51 + })
  52 + aiMessage = openaiMessages.value.find(m => m.id === response.id);
  53 + }
  54 + if (!aiMessage) {
  55 + aiMessage = {
  56 + role: response.task_id !== null ? 'ai' : 'assistant',
  57 + type: 'text',
  58 + content: '',
  59 + id: response.id,
  60 + };
  61 + if (currentMode.value === 'OpenAI') {
  62 + openaiMessages.value.push(aiMessage);
  63 + } else {
  64 + aiMessage.conversation_id = response.conversation_id
  65 + messages.value.push(aiMessage);
  66 + }
  67 + }
  68 + // 追加流式内容
  69 + aiMessage.content += response.answer;
  70 + } else if (response.code === 203) {
  71 + // 回复结束
  72 + isStreaming.value = false;
  73 + } else if (response.code === 500) {
  74 + // 消息失败处理
  75 + if (currentMode.value === 'OpenAI') {
  76 + openaiMessages.value.push({
  77 + role: 'system',
  78 + type: 'text',
  79 + content: '消息发送失败,请重试'
  80 + });
  81 + } else {
  82 + messages.value.push({
  83 + role: 'system',
  84 + type: 'text',
  85 + content: '消息发送失败,请重试'
  86 + });
  87 + }
  88 + isStreaming.value = false;
  89 + } else if (response.code >= 300) {
  90 + messages.value.push({
  91 + role: 'system',
  92 + type: 'text',
  93 + content: response.message
  94 + });
  95 + }
  96 + };
  97 +
  98 + ws.value.onclose = () => {
  99 + isConnected.value = false;
  100 + };
  101 +});
  102 +
  103 +const sendMessage = () => {
  104 + if (!inputMessage.value.trim() || !isConnected.value) return;
  105 + let data;
  106 + if (currentMode.value === 'Dify') {
  107 + data = difySendMessage()
  108 + } else {
  109 + openaiSendMessage()
  110 + return
  111 + }
  112 + console.log("data:", data)
  113 +
  114 + ws.value.send(JSON.stringify({
  115 + method: currentMode.value,
  116 + hasFile: true,
  117 + dto: data
  118 + }));
  119 + fileUrl.value = '';
  120 + showFile.value = false;
  121 + inputMessage.value = '';
  122 + isStreaming.value = true;
  123 +};
  124 +
  125 +const difySendMessage = () => {
  126 + // 添加用户消息
  127 + messages.value.push({
  128 + role: 'user',
  129 + type: 'text',
  130 + content: inputMessage.value
  131 + });
  132 + let data = {
  133 + query: inputMessage.value
  134 + }
  135 + const aiMessage = messages.value.find(m => m.role === 'ai');
  136 + if (aiMessage && aiMessage.conversation_id) {
  137 + data.conversation_id = aiMessage.conversation_id;
  138 + }
  139 + return data
  140 +}
  141 +const openaiSendMessage = () => {
  142 + let data = {
  143 + model: selectedModel.value,
  144 + messages: [],
  145 + }
  146 + const newMessage = {
  147 + role: 'user',
  148 + type: 'text',
  149 + content: inputMessage.value,
  150 + };
  151 + // 如果有上传图片,添加到新消息
  152 + if (fileType.value !== null && fileType.value === 'image') {
  153 + newMessage.image_url = uploadedImageUrl.value;
  154 + } else if (fileType.value !== null && fileType.value === 'file') {
  155 + data.fileContent = fileUrl.value;
  156 + }
  157 + openaiMessages.value.push(newMessage);
  158 + openaiMessages.value.map(item => {
  159 + if (item.hasOwnProperty('image_url')) {
  160 + data.messages.push({
  161 + role: item.role,
  162 + content: [{
  163 + type: 'text',
  164 + text: item.content
  165 + }, {
  166 + type: 'image_url',
  167 + image_url: {
  168 + url: item.image_url
  169 + }
  170 + }]
  171 + })
  172 + } else {
  173 + data.messages.push({
  174 + role: item.role,
  175 + content: item.content
  176 + })
  177 + }
  178 + })
  179 + console.log("11data:", data)
  180 + ws.value.send(JSON.stringify({
  181 + method: currentMode.value,
  182 + hasFile: true,
  183 + dto: data
  184 + }));
  185 + fileUrl.value = '';
  186 + showFile.value = false;
  187 + inputMessage.value = '';
  188 + isStreaming.value = true;
  189 +}
  190 +
  191 +const triggerFileUpload = () => {
  192 + document.getElementById('file-upload').click();
  193 +};
  194 +
  195 +const handleFileUpload = async (event) => {
  196 + try {
  197 + const file = event.target.files[0];
  198 + if (!file) return;
  199 + const formData = new FormData();
  200 + formData.append('file', file);
  201 + const res = await uploadFileToDataset(formData);
  202 +
  203 + // if (file.size > MAX_SIZE) {
  204 + // alert('文件大小不能超过5MB');
  205 + // return;
  206 + // }
  207 + // const res = await uploadFile(formData);
  208 + // console.log("res:", res);
  209 + // fileUrl.value = res.data; // 保存返回的URL
  210 + // showFile.value = true;
  211 + event.target.value = '';
  212 + } catch (err) {
  213 + console.log("err:", err);
  214 + alert('文件上传失败,请重试');
  215 + }
  216 +};
  217 +
  218 +const cancel = () => {
  219 + fileUrl.value = '';
  220 + showFile.value = false;
  221 +}
  222 +
  223 +const fileType = computed(() => {
  224 + if (!fileUrl.value) return null;
  225 +
  226 + const url = fileUrl.value;
  227 + const extension = url.split('.').pop().toLowerCase();
  228 +
  229 + const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
  230 +
  231 + return imageExtensions.includes(extension) ? 'image' : 'file';
  232 +});
  233 +
  234 +const extractHtmlFromString = (str) => {
  235 + const htmlStart = str.indexOf('```html') + '```html'.length;
  236 + const htmlEnd = str.indexOf('```', htmlStart);
  237 +
  238 + if (htmlStart === -1 || htmlEnd === -1) {
  239 + return null; // 没有找到HTML代码块
  240 + }
  241 +
  242 + return str.substring(htmlStart, htmlEnd).trim();
  243 +}
  244 +
  245 +const saveToHtml = async (content) => {
  246 + const html = extractHtmlFromString(content)
  247 + const res = await saveHtml({
  248 + html: html
  249 + })
  250 + console.log("res:", res)
  251 + if (res.code == '200' && res.data) {
  252 + alert("保存成功")
  253 + } else {
  254 + alert("保存失败")
  255 + }
  256 +}
  257 +function test1(input) {
  258 + let processed = input;
  259 + const hasSeparator = input.includes('---');
  260 + if (hasSeparator) {
  261 + // 按分隔符分割并过滤空内容​
  262 + const parts = input.split('---')
  263 + .map(part => part.trim())
  264 + .filter(part => part.length > 0);
  265 + // 取中间部分或首个有效内容​
  266 + processed = parts.length > 1 ? parts.slice(1, -1).join('\n\n') || parts[1] : parts[0];
  267 + }
  268 + const lines = processed.split('\n').map(line => line.trim());
  269 + let startIndex = -1;
  270 + let endIndex = lines.length;
  271 + // 找到第一个#标题的位置​
  272 + for (let i = 0; i < lines.length; i++) {
  273 + if (lines[i].startsWith('#')) {
  274 + startIndex = i;
  275 + break;
  276 + }
  277 + }
  278 + if (startIndex === -1) {
  279 + return lines.filter(line => line).join('\n');
  280 + }
  281 + // 找到文章结束位置(连续空行或内容结束)​
  282 + for (let i = lines.length - 1; i >= startIndex; i--) {
  283 + if (lines[i].length > 0) {
  284 + endIndex = i + 1;
  285 + break;
  286 + }
  287 + }
  288 + return lines.slice(startIndex, endIndex).join('\n');
  289 +}
  290 +
  291 +const saveFile = async (content, type) => {
  292 + const str = test1(content)
  293 + const res = await saveDocx({
  294 + content: str,
  295 + type
  296 + })
  297 + console.log("res", res)
  298 +}
  299 +
  300 +const test = async () => {
  301 + // const file = event.target.files[0];
  302 + // const formData = new FormData();
  303 + // formData.append('file', file);
  304 + // if (file.size > MAX_SIZE) {
  305 + // alert('文件大小不能超过5MB');
  306 + // return;
  307 + // }
  308 + // const res = await uploadFile(formData);
  309 + // console.log("res:", res);
  310 +
  311 +}
  312 +
  313 +</script>
  314 +<template>
  315 + <div class="chat-container">
  316 + <div class="chat-messages">
  317 + <div v-for="(message, index) in getCurrentMessages()" :key="index" class="message">
  318 + <div :class="['message-content', message.role]">
  319 + <img v-if="message.role === 'user'" src="@/assets/user-icon.png" class="avatar" />
  320 + <img v-else src="@/assets/robot.png" class="avatar" />
  321 + <div class="message-body">
  322 + <div v-if="message.type === 'text'">
  323 + {{ message.content }}
  324 + <button v-if="message.content.includes('```html') && message.content.includes('```')"
  325 + @click="saveToHtml(message.content)" class="save-btn">
  326 + 保存HTML
  327 + </button>
  328 + <button @click="saveFile(message.content, 'docx')" class="save-btn">
  329 + 生成DOCX
  330 + </button>
  331 + <button @click="saveFile(message.content, 'pdf')" class="save-btn">
  332 + 生成PDF
  333 + </button>
  334 + </div>
  335 + <img v-else-if="message.type === 'image'" :src="message.content" class="uploaded-image" />
  336 + <div v-else-if="message.type === 'file'">
  337 + <a :href="message.content" target="_blank">下载文件</a>
  338 + </div>
  339 + </div>
  340 + </div>
  341 + </div>
  342 + <div v-if="isStreaming" class="streaming-indicator">
  343 + <div class="typing-dots">
  344 + <span></span><span></span><span></span>
  345 + </div>
  346 + </div>
  347 + </div>
  348 + <div v-if="showFile" class="image-preview">
  349 + <img v-if="fileType === 'image'" :src="fileUrl" alt="预览图片" />
  350 + <span v-else>已上传文件:{{ fileUrl.split('/').pop() }}</span>
  351 +
  352 + <button class="cancel-btn" @click="cancel">取消</button>
  353 + </div>
  354 +
  355 + <div class="chat-input">
  356 + <div class="upload-buttons">
  357 + <button class="small-button" @click="triggerMode">当前模式:{{ currentMode }}</button>
  358 + <select v-if="currentMode === 'OpenAI'" v-model="selectedModel" class="model-select">
  359 + <option v-for="(model, index) in modelOptions" :value="model" :key="index">{{ model }}</option>
  360 + </select>
  361 + <div class="vertical-buttons" v-if="currentMode === 'OpenAI'">
  362 + <input type="file" id="file-upload" @change="handleFileUpload" style="display: none" />
  363 + <button class="small-button" @click="triggerFileUpload">上传文件</button>
  364 + </div>
  365 + </div>
  366 + <textarea v-model="inputMessage" @keyup.enter="sendMessage" placeholder="输入消息..."></textarea>
  367 + <button @click="sendMessage">发送</button>
  368 + <button @click="saveFile">测试</button>
  369 + <button @click="test">测试1</button>
  370 + </div>
  371 + </div>
  372 +</template>
  373 +
  374 +
  375 +
  376 +<style scoped>
  377 +.chat-container {
  378 + display: flex;
  379 + flex-direction: column;
  380 + height: 100vh;
  381 + max-width: 800px;
  382 + margin: 0 auto;
  383 + border: 1px solid #ddd;
  384 +}
  385 +
  386 +.chat-messages {
  387 + flex: 1;
  388 + overflow-y: auto;
  389 + padding: 20px;
  390 +}
  391 +
  392 +.message {
  393 + margin-bottom: 15px;
  394 +}
  395 +
  396 +.message-content {
  397 + display: flex;
  398 + align-items: flex-start;
  399 + gap: 10px;
  400 +}
  401 +
  402 +.message-content.user {
  403 + flex-direction: row-reverse;
  404 +}
  405 +
  406 +.avatar {
  407 + width: 40px;
  408 + height: 40px;
  409 + border-radius: 50%;
  410 + object-fit: cover;
  411 +}
  412 +
  413 +.message-body {
  414 + max-width: 70%;
  415 + padding: 10px 15px;
  416 + border-radius: 18px;
  417 + background: #f0f0f0;
  418 +}
  419 +
  420 +.message-content.user .message-body {
  421 + background: #007bff;
  422 + color: white;
  423 +}
  424 +
  425 +.uploaded-image {
  426 + max-width: 200px;
  427 + max-height: 200px;
  428 + border-radius: 8px;
  429 +}
  430 +
  431 +.chat-input {
  432 + display: flex;
  433 + padding: 10px;
  434 + border-top: 1px solid #ddd;
  435 + background: white;
  436 + gap: 10px;
  437 +}
  438 +
  439 +.upload-buttons {
  440 + display: flex;
  441 + flex-direction: column;
  442 + gap: 5px;
  443 + min-width: 120px;
  444 +}
  445 +
  446 +.vertical-buttons {
  447 + display: flex;
  448 + flex-direction: column;
  449 + gap: 5px;
  450 +}
  451 +
  452 +/* 图片预览样式 */
  453 +.image-preview {
  454 + padding: 10px;
  455 + text-align: center;
  456 + background: #f5f5f5;
  457 + border-bottom: 1px solid #ddd;
  458 +}
  459 +
  460 +.image-preview img {
  461 + width: 80px;
  462 + height: 80px;
  463 + object-fit: cover;
  464 + margin-bottom: 5px;
  465 +}
  466 +
  467 +.cancel-btn {
  468 + padding: 3px 8px;
  469 + font-size: 12px;
  470 + background: #ff4d4f;
  471 +}
  472 +
  473 +textarea {
  474 + flex: 1;
  475 + padding: 10px;
  476 + border: 1px solid #ddd;
  477 + border-radius: 4px;
  478 + resize: none;
  479 + height: 60px;
  480 +}
  481 +
  482 +button {
  483 + padding: 10px 15px;
  484 + background: #007bff;
  485 + color: white;
  486 + border: none;
  487 + border-radius: 4px;
  488 + cursor: pointer;
  489 +}
  490 +
  491 +.streaming-indicator {
  492 + padding: 10px;
  493 +}
  494 +
  495 +.typing-dots span {
  496 + display: inline-block;
  497 + width: 8px;
  498 + height: 8px;
  499 + border-radius: 50%;
  500 + background: #ccc;
  501 + margin: 0 2px;
  502 + animation: bounce 1.4s infinite ease-in-out;
  503 +}
  504 +
  505 +.typing-dots span:nth-child(2) {
  506 + animation-delay: 0.2s;
  507 +}
  508 +
  509 +.typing-dots span:nth-child(3) {
  510 + animation-delay: 0.4s;
  511 +}
  512 +
  513 +@keyframes bounce {
  514 +
  515 + 0%,
  516 + 60%,
  517 + 100% {
  518 + transform: translateY(0);
  519 + }
  520 +
  521 + 30% {
  522 + transform: translateY(-5px);
  523 + }
  524 +}
  525 +
  526 +.small-button {
  527 + padding: 5px 10px;
  528 + font-size: 12px;
  529 + height: 30px;
  530 +}
  531 +
  532 +.model-select {
  533 + padding: 5px;
  534 + border: 1px solid #ddd;
  535 + border-radius: 4px;
  536 + margin-right: 10px;
  537 + height: 30px;
  538 +}
  539 +
  540 +.image-preview {
  541 + padding: 10px;
  542 + border-bottom: 1px solid #ddd;
  543 + text-align: center;
  544 +}
  545 +
  546 +.image-preview img {
  547 + max-width: 200px;
  548 + max-height: 200px;
  549 + margin-bottom: 10px;
  550 +}
  551 +</style>
@@ -15,4 +15,9 @@ export default defineConfig({ @@ -15,4 +15,9 @@ export default defineConfig({
15 '@': fileURLToPath(new URL('./src', import.meta.url)) 15 '@': fileURLToPath(new URL('./src', import.meta.url))
16 }, 16 },
17 }, 17 },
  18 + server: {
  19 + host: '0.0.0.0',
  20 + port: 5189,
  21 + allowedHosts: ['erpwebtest.yiwaixiao.net', 'webdemo.yiwaixiao.net']
  22 + },
18 }) 23 })