作者 202304001

0820提交

<!DOCTYPE html>
<!doctype html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link
href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css"
rel="stylesheet"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<script type="text/javascript" src="/config.js"></script>
</body>
</html>
... ...
... ... @@ -8,6 +8,7 @@
"name": "aigeo",
"version": "0.0.0",
"dependencies": {
"axios": "^1.11.0",
"chart.js": "^4.5.0",
"pinia": "^3.0.3",
"sass": "^1.90.0",
... ... @@ -2009,6 +2010,12 @@
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
... ... @@ -2047,6 +2054,17 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.11.0",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
... ... @@ -2144,6 +2162,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
... ... @@ -2240,6 +2271,18 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
... ... @@ -2364,6 +2407,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
... ... @@ -2389,6 +2441,20 @@
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"license": "MIT"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
... ... @@ -2430,6 +2496,51 @@
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.25.8",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
... ... @@ -2595,6 +2706,26 @@
"node": ">=8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
... ... @@ -2611,6 +2742,22 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fraction.js": {
"version": "4.3.7",
"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
... ... @@ -2658,6 +2805,43 @@
"node": ">=6.9.0"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-stream": {
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-9.0.1.tgz",
... ... @@ -2707,6 +2891,45 @@
"node": ">=10.13.0"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
... ... @@ -3016,6 +3239,15 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
... ... @@ -3050,6 +3282,27 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
... ... @@ -3623,6 +3876,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
... ...
... ... @@ -13,6 +13,7 @@
"format": "prettier --write src/"
},
"dependencies": {
"axios": "^1.11.0",
"chart.js": "^4.5.0",
"pinia": "^3.0.3",
"sass": "^1.90.0",
... ...
window.PLATFROM_CONFIG = {
DEV_BASE_URL: 'http://erpapitest.yiwaixiao.net:80', // 开发环境
PRO_BASE_URL: 'http://erpapi.yiwaixiao.net', // 正式环境
}
document.cookie = 'token=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNzU1ODg4NDM3MDUiLCJzdWIiOiJ5aXdhaXhpYW8ubmV0IiwidXNlcl9pZCI6Mzc4LCJsb2dpbmlkIjoiaW5mbyIsImRvbWFpbiI6IjIxZ21haWwuY29tIiwiY29tcGFueV9pZCI6NywicGFyYW1fbGV2ZWwiOjAsImxvZ3BhdGgiOiJlOlxcbG9nc1xcMjFnbWFpbC5jb21cXGluZm9cXGVycm9yXFwiLCJpYXQiOjE3NTU0ODg1MjIsImV4cCI6MTc1NTQ5NTcyMn0.B7FNn8Zk-t2SvGUwHRUURLn_2QsJNX2lJX-w8pGJhRw';
... ...
import request from '@/utils/request'
export const uploadFile = (data) => {
return request({
url: '/ai/upload',
method: 'post',
data
})
}
export const saveHtml = (data) => {
return request({
url: '/ai/saveHtml',
method: 'post',
data
})
}
export const saveDocx = (data) => {
return request({
url: '/ai/generateFile',
method: 'post',
data
})
}
export const uploadFileToDataset = (data) => {
return request({
url: '/ai/uploadFileToDataset',
method: 'post',
data
})
}
//<!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>
\ No newline at end of file
... ...
... ... @@ -86,4 +86,18 @@
<script setup>
</script>
<style scoped></style>
<style scoped>
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@layer utilities {
.content-auto {
content-visibility: auto;
}
.sidebar-item-active {
@apply bg-primary/10 text-primary border-l-4 border-primary;
}
}
</style>
... ...
import './assets/main.css'
import './utils/config.js'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
... ...
... ... @@ -13,6 +13,11 @@ const router = createRouter({
name: 'about',
component: () => import('../views/test.vue'),
},
{
path: '/test1',
name: 'test1',
component: () => import('../views/test1.vue'),
},
],
})
... ...
export const MAX_SIZE = 1024 * 1024 * 5;
export const INVALID_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
export const getCookie = (name) => {
const cookieArr = document.cookie.split('; ');
for (let item of cookieArr) {
const [key, value] = item.split('=');
if (key === name) {
return decodeURIComponent(value);
}
}
return null;
}
... ...
import axios from 'axios'
// 根据环境获取基础URL
// 从全局window对象获取配置
const config = window.PLATFROM_CONFIG || {}
// 添加调试信息
const BASE_URL = import.meta.env.DEV ? config.DEV_BASE_URL : config.PRO_BASE_URL;
// const API_BASE_URL="http://webdemo.yiwaixiao.net/"
// 创建axios实例
const request = axios.create({
baseURL: BASE_URL,
timeout: 60000,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'token': 'eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNzU1ODg4NDM3MDUiLCJzdWIiOiJ5aXdhaXhpYW8ubmV0IiwidXNlcl9pZCI6Mzc4LCJsb2dpbmlkIjoiaW5mbyIsImRvbWFpbiI6IjIxZ21haWwuY29tIiwiY29tcGFueV9pZCI6NywicGFyYW1fbGV2ZWwiOjAsImxvZ3BhdGgiOiJkOlxcbG9nc1xcMjFnbWFpbC5jb20iLCJpYXQiOjE3NTUwNTQ5NTksImV4cCI6MTc1Njc4Mjk1OX0.WfXb6Ol7ka8n3MHgnndor2la6VzmtBHpjZngsNjwHn0'
}
})
// 请求拦截器
request.interceptors.request.use(
config => {
console.log("config:", config)
console.log(BASE_URL)
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
request.interceptors.response.use(
response => {
return response.data
},
error => {
// if (error.response?.status == '800') {
// // token过期处理
// const userStore = useUserStore()
// userStore.clearToken()
// window.location.href = '/login'
// }
return Promise.reject(error)
}
)
export default request
... ...
... ... @@ -74,7 +74,7 @@
<div class="mb-6">
<h2 class="text-[clamp(1.5rem,3vw,2rem)] font-bold">控制台</h2>
<p class="text-gray-500 mt-1">欢迎回来,今天是 <span class="font-medium" id="current-date">{{ currentDate
}}</span>
}}</span>
</p>
</div>
... ... @@ -541,6 +541,7 @@
import siderbar from '@/components/siderbar.vue';
import { ref, onMounted } from 'vue';
import { Chart, registerables } from 'chart.js';
import { getCookie } from '@/utils/config';
Chart.register(...registerables);
// 状态管理
... ... @@ -963,6 +964,8 @@ onMounted(() => {
userDropdownOpen.value = false;
}
});
const token = getCookie('token')
console.log(token)
});
tailwind.config = {
... ... @@ -1033,8 +1036,4 @@ tailwind.config = {
@apply opacity-100 translate-y-0;
}
}
.btn-primary {
background-color: rgb(0, 119, 255);
}
</style>
... ...
<script setup>
import { saveDocx, saveHtml, uploadFile, uploadFileToDataset } from '@/api/aiapi';
import { ref, onMounted, computed } from 'vue';
import { MAX_SIZE, INVALID_TYPES } from '@/utils/config.js';
const messages = ref([]);
const openaiMessages = ref([]);
const inputMessage = ref('');
const isStreaming = ref(false);
const ws = ref(null);
const isConnected = ref(false);
const currentMode = ref('Dify');
const modelOptions = ref(['gpt-4o-mini', 'o3-mini', 'gpt-5-mini', 'dall-e-3', 'gpt-3.5-turbo']);
const selectedModel = ref('gpt-4o-mini');
const fileUrl = ref('');
const showFile = ref(false);
const triggerMode = () => {
if (currentMode.value === 'Dify') {
currentMode.value = 'OpenAI';
} else {
currentMode.value = 'Dify';
}
};
const getCurrentMessages = () => {
return currentMode.value === 'Dify' ? messages.value : openaiMessages.value;
};
onMounted(() => {
// 初始化WebSocket连接
ws.value = new WebSocket('ws://192.168.0.24/ws/chat/378');
ws.value.onopen = () => {
isConnected.value = true;
};
ws.value.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log("*-**-*--------*", response)
if (response.code === 200) {
// 渲染AI回复
let aiMessage;
if (response.task_id !== null) {
aiMessage = messages.value.find(m => m.id === response.id && m.role === 'ai');
} else {
openaiMessages.value.map(item => {
console.log(item.id, response.id, item.id === response.id)
if (item.id === response.id) {
aiMessage = item
}
})
aiMessage = openaiMessages.value.find(m => m.id === response.id);
}
if (!aiMessage) {
aiMessage = {
role: response.task_id !== null ? 'ai' : 'assistant',
type: 'text',
content: '',
id: response.id,
};
if (currentMode.value === 'OpenAI') {
openaiMessages.value.push(aiMessage);
} else {
aiMessage.conversation_id = response.conversation_id
messages.value.push(aiMessage);
}
}
// 追加流式内容
aiMessage.content += response.answer;
} else if (response.code === 203) {
// 回复结束
isStreaming.value = false;
} else if (response.code === 500) {
// 消息失败处理
if (currentMode.value === 'OpenAI') {
openaiMessages.value.push({
role: 'system',
type: 'text',
content: '消息发送失败,请重试'
});
} else {
messages.value.push({
role: 'system',
type: 'text',
content: '消息发送失败,请重试'
});
}
isStreaming.value = false;
} else if (response.code >= 300) {
messages.value.push({
role: 'system',
type: 'text',
content: response.message
});
}
};
ws.value.onclose = () => {
isConnected.value = false;
};
});
const sendMessage = () => {
if (!inputMessage.value.trim() || !isConnected.value) return;
let data;
if (currentMode.value === 'Dify') {
data = difySendMessage()
} else {
openaiSendMessage()
return
}
console.log("data:", data)
ws.value.send(JSON.stringify({
method: currentMode.value,
hasFile: true,
dto: data
}));
fileUrl.value = '';
showFile.value = false;
inputMessage.value = '';
isStreaming.value = true;
};
const difySendMessage = () => {
// 添加用户消息
messages.value.push({
role: 'user',
type: 'text',
content: inputMessage.value
});
let data = {
query: inputMessage.value
}
const aiMessage = messages.value.find(m => m.role === 'ai');
if (aiMessage && aiMessage.conversation_id) {
data.conversation_id = aiMessage.conversation_id;
}
return data
}
const openaiSendMessage = () => {
let data = {
model: selectedModel.value,
messages: [],
}
const newMessage = {
role: 'user',
type: 'text',
content: inputMessage.value,
};
// 如果有上传图片,添加到新消息
if (fileType.value !== null && fileType.value === 'image') {
newMessage.image_url = uploadedImageUrl.value;
} else if (fileType.value !== null && fileType.value === 'file') {
data.fileContent = fileUrl.value;
}
openaiMessages.value.push(newMessage);
openaiMessages.value.map(item => {
if (item.hasOwnProperty('image_url')) {
data.messages.push({
role: item.role,
content: [{
type: 'text',
text: item.content
}, {
type: 'image_url',
image_url: {
url: item.image_url
}
}]
})
} else {
data.messages.push({
role: item.role,
content: item.content
})
}
})
console.log("11data:", data)
ws.value.send(JSON.stringify({
method: currentMode.value,
hasFile: true,
dto: data
}));
fileUrl.value = '';
showFile.value = false;
inputMessage.value = '';
isStreaming.value = true;
}
const triggerFileUpload = () => {
document.getElementById('file-upload').click();
};
const handleFileUpload = async (event) => {
try {
const file = event.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
const res = await uploadFileToDataset(formData);
// if (file.size > MAX_SIZE) {
// alert('文件大小不能超过5MB');
// return;
// }
// const res = await uploadFile(formData);
// console.log("res:", res);
// fileUrl.value = res.data; // 保存返回的URL
// showFile.value = true;
event.target.value = '';
} catch (err) {
console.log("err:", err);
alert('文件上传失败,请重试');
}
};
const cancel = () => {
fileUrl.value = '';
showFile.value = false;
}
const fileType = computed(() => {
if (!fileUrl.value) return null;
const url = fileUrl.value;
const extension = url.split('.').pop().toLowerCase();
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
return imageExtensions.includes(extension) ? 'image' : 'file';
});
const extractHtmlFromString = (str) => {
const htmlStart = str.indexOf('```html') + '```html'.length;
const htmlEnd = str.indexOf('```', htmlStart);
if (htmlStart === -1 || htmlEnd === -1) {
return null; // 没有找到HTML代码块
}
return str.substring(htmlStart, htmlEnd).trim();
}
const saveToHtml = async (content) => {
const html = extractHtmlFromString(content)
const res = await saveHtml({
html: html
})
console.log("res:", res)
if (res.code == '200' && res.data) {
alert("保存成功")
} else {
alert("保存失败")
}
}
function test1(input) {
let processed = input;
const hasSeparator = input.includes('---');
if (hasSeparator) {
// 按分隔符分割并过滤空内容​
const parts = input.split('---')
.map(part => part.trim())
.filter(part => part.length > 0);
// 取中间部分或首个有效内容​
processed = parts.length > 1 ? parts.slice(1, -1).join('\n\n') || parts[1] : parts[0];
}
const lines = processed.split('\n').map(line => line.trim());
let startIndex = -1;
let endIndex = lines.length;
// 找到第一个#标题的位置​
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#')) {
startIndex = i;
break;
}
}
if (startIndex === -1) {
return lines.filter(line => line).join('\n');
}
// 找到文章结束位置(连续空行或内容结束)​
for (let i = lines.length - 1; i >= startIndex; i--) {
if (lines[i].length > 0) {
endIndex = i + 1;
break;
}
}
return lines.slice(startIndex, endIndex).join('\n');
}
const saveFile = async (content, type) => {
const str = test1(content)
const res = await saveDocx({
content: str,
type
})
console.log("res", res)
}
const test = async () => {
// const file = event.target.files[0];
// const formData = new FormData();
// formData.append('file', file);
// if (file.size > MAX_SIZE) {
// alert('文件大小不能超过5MB');
// return;
// }
// const res = await uploadFile(formData);
// console.log("res:", res);
}
</script>
<template>
<div class="chat-container">
<div class="chat-messages">
<div v-for="(message, index) in getCurrentMessages()" :key="index" class="message">
<div :class="['message-content', message.role]">
<img v-if="message.role === 'user'" src="@/assets/user-icon.png" class="avatar" />
<img v-else src="@/assets/robot.png" class="avatar" />
<div class="message-body">
<div v-if="message.type === 'text'">
{{ message.content }}
<button v-if="message.content.includes('```html') && message.content.includes('```')"
@click="saveToHtml(message.content)" class="save-btn">
保存HTML
</button>
<button @click="saveFile(message.content, 'docx')" class="save-btn">
生成DOCX
</button>
<button @click="saveFile(message.content, 'pdf')" class="save-btn">
生成PDF
</button>
</div>
<img v-else-if="message.type === 'image'" :src="message.content" class="uploaded-image" />
<div v-else-if="message.type === 'file'">
<a :href="message.content" target="_blank">下载文件</a>
</div>
</div>
</div>
</div>
<div v-if="isStreaming" class="streaming-indicator">
<div class="typing-dots">
<span></span><span></span><span></span>
</div>
</div>
</div>
<div v-if="showFile" class="image-preview">
<img v-if="fileType === 'image'" :src="fileUrl" alt="预览图片" />
<span v-else>已上传文件:{{ fileUrl.split('/').pop() }}</span>
<button class="cancel-btn" @click="cancel">取消</button>
</div>
<div class="chat-input">
<div class="upload-buttons">
<button class="small-button" @click="triggerMode">当前模式:{{ currentMode }}</button>
<select v-if="currentMode === 'OpenAI'" v-model="selectedModel" class="model-select">
<option v-for="(model, index) in modelOptions" :value="model" :key="index">{{ model }}</option>
</select>
<div class="vertical-buttons" v-if="currentMode === 'OpenAI'">
<input type="file" id="file-upload" @change="handleFileUpload" style="display: none" />
<button class="small-button" @click="triggerFileUpload">上传文件</button>
</div>
</div>
<textarea v-model="inputMessage" @keyup.enter="sendMessage" placeholder="输入消息..."></textarea>
<button @click="sendMessage">发送</button>
<button @click="saveFile">测试</button>
<button @click="test">测试1</button>
</div>
</div>
</template>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100vh;
max-width: 800px;
margin: 0 auto;
border: 1px solid #ddd;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.message {
margin-bottom: 15px;
}
.message-content {
display: flex;
align-items: flex-start;
gap: 10px;
}
.message-content.user {
flex-direction: row-reverse;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.message-body {
max-width: 70%;
padding: 10px 15px;
border-radius: 18px;
background: #f0f0f0;
}
.message-content.user .message-body {
background: #007bff;
color: white;
}
.uploaded-image {
max-width: 200px;
max-height: 200px;
border-radius: 8px;
}
.chat-input {
display: flex;
padding: 10px;
border-top: 1px solid #ddd;
background: white;
gap: 10px;
}
.upload-buttons {
display: flex;
flex-direction: column;
gap: 5px;
min-width: 120px;
}
.vertical-buttons {
display: flex;
flex-direction: column;
gap: 5px;
}
/* 图片预览样式 */
.image-preview {
padding: 10px;
text-align: center;
background: #f5f5f5;
border-bottom: 1px solid #ddd;
}
.image-preview img {
width: 80px;
height: 80px;
object-fit: cover;
margin-bottom: 5px;
}
.cancel-btn {
padding: 3px 8px;
font-size: 12px;
background: #ff4d4f;
}
textarea {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
height: 60px;
}
button {
padding: 10px 15px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.streaming-indicator {
padding: 10px;
}
.typing-dots span {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: #ccc;
margin: 0 2px;
animation: bounce 1.4s infinite ease-in-out;
}
.typing-dots span:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dots span:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes bounce {
0%,
60%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-5px);
}
}
.small-button {
padding: 5px 10px;
font-size: 12px;
height: 30px;
}
.model-select {
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
margin-right: 10px;
height: 30px;
}
.image-preview {
padding: 10px;
border-bottom: 1px solid #ddd;
text-align: center;
}
.image-preview img {
max-width: 200px;
max-height: 200px;
margin-bottom: 10px;
}
</style>
\ No newline at end of file
... ...
... ... @@ -15,4 +15,9 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
host: '0.0.0.0',
port: 5189,
allowedHosts: ['erpwebtest.yiwaixiao.net', 'webdemo.yiwaixiao.net']
},
})
... ...