mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-10-23 10:23:42 +08:00
引入tailwind css,调整样式
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
## v4.1.8
|
||||
|
||||
- 功能优化:**UI 全新改版,支持主题切换**。 :rocket: :rocket: :rocket:
|
||||
- Bug 修复:修复音 Luma API 更新导致任务响应解析失败的错误
|
||||
- Bug修复:修复音 Luma API 更新导致任务响应解析失败的错误
|
||||
- 功能优化:支持 Suno v4.0 模型支持
|
||||
- Bug 修复:修复 Suno 已完成任务删除失败的 错误
|
||||
- Bug修复:修复 Suno 已完成任务删除失败的 错误
|
||||
- 功能新增:支持 OpenAI 实时语音通话功能,目前已经支持按次收费,支持管理员设置每次实时语音通话的算力消耗
|
||||
|
||||
## v4.1.7
|
||||
|
588
web/package-lock.json
generated
588
web/package-lock.json
generated
@@ -45,10 +45,13 @@
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"postcss": "^8.4.49",
|
||||
"stylus": "^0.58.1",
|
||||
"stylus-loader": "^7.0.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"webpack": "^5.90.3"
|
||||
}
|
||||
},
|
||||
@@ -66,6 +69,18 @@
|
||||
"node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
@@ -1850,6 +1865,102 @@
|
||||
"deprecated": "Use @eslint/object-schema instead",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
@@ -2027,6 +2138,16 @@
|
||||
"ws": "^8.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.28",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
|
||||
@@ -3723,6 +3844,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
@@ -3805,7 +3932,7 @@
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
@@ -4232,6 +4359,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-api": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
|
||||
@@ -5722,6 +5858,12 @@
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
@@ -5739,6 +5881,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dlv": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dns-packet": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
|
||||
@@ -5862,6 +6010,12 @@
|
||||
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/easy-stack": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz",
|
||||
@@ -6941,6 +7095,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child/node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
@@ -7851,6 +8033,21 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz",
|
||||
@@ -7886,6 +8083,15 @@
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz",
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.13.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
|
||||
@@ -9219,6 +9425,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz",
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
|
||||
@@ -9445,6 +9660,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
@@ -9555,6 +9776,37 @@
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
@@ -9587,6 +9839,15 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.6.tgz",
|
||||
@@ -9637,6 +9898,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pirates": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz",
|
||||
"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
@@ -9682,7 +9952,7 @@
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.49",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.49.tgz",
|
||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||
"funding": [
|
||||
{
|
||||
@@ -9802,6 +10072,101 @@
|
||||
"postcss": "^8.2.15"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"postcss-value-parser": "^4.0.0",
|
||||
"read-cache": "^1.0.0",
|
||||
"resolve": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-js": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"camelcase-css": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12 || ^14 || >= 16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4.21"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
|
||||
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"lilconfig": "^3.0.0",
|
||||
"yaml": "^2.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": ">=8.0.9",
|
||||
"ts-node": ">=9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"postcss": {
|
||||
"optional": true
|
||||
},
|
||||
"ts-node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antonk52"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.6.1.tgz",
|
||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-loader": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz",
|
||||
@@ -10019,6 +10384,31 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nested": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-normalize-charset": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz",
|
||||
@@ -10599,6 +10989,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
|
||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pify": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
|
||||
@@ -11504,6 +11903,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
@@ -11515,6 +11929,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-eof": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
|
||||
@@ -11619,6 +12046,90 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.0.tgz",
|
||||
"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"commander": "^4.0.0",
|
||||
"glob": "^10.3.10",
|
||||
"lines-and-columns": "^1.1.6",
|
||||
"mz": "^2.7.0",
|
||||
"pirates": "^4.0.1",
|
||||
"ts-interface-checker": "^0.1.9"
|
||||
},
|
||||
"bin": {
|
||||
"sucrase": "bin/sucrase",
|
||||
"sucrase-node": "bin/sucrase-node"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/commander": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
|
||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@@ -11717,6 +12228,55 @@
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.17",
|
||||
"resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.17.tgz",
|
||||
"integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.6",
|
||||
"lilconfig": "^3.1.3",
|
||||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.35.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss/node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antonk52"
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
@@ -11961,6 +12521,12 @@
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-interface-checker": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||
@@ -12925,6 +13491,24 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
@@ -45,10 +45,13 @@
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"postcss": "^8.4.49",
|
||||
"stylus": "^0.58.1",
|
||||
"stylus-loader": "^7.0.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"webpack": "^5.90.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
6
web/postcss.config.js
Normal file
6
web/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
115
web/src/App.vue
115
web/src/App.vue
@@ -1,29 +1,29 @@
|
||||
<template>
|
||||
<el-config-provider>
|
||||
<router-view/>
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ElConfigProvider} from 'element-plus';
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {isChrome, isMobile} from "@/utils/libs";
|
||||
import {showMessageInfo} from "@/utils/dialog";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import {getUserToken} from "@/store/session";
|
||||
import { ElConfigProvider } from "element-plus";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { checkSession, getClientId, getSystemInfo } from "@/store/cache";
|
||||
import { isChrome, isMobile } from "@/utils/libs";
|
||||
import { showMessageInfo } from "@/utils/dialog";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import { getUserToken } from "@/store/session";
|
||||
|
||||
const debounce = (fn, delay) => {
|
||||
let timer
|
||||
let timer;
|
||||
return (...args) => {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
fn(...args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
fn(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const _ResizeObserver = window.ResizeObserver;
|
||||
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||
@@ -31,63 +31,69 @@ window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||
callback = debounce(callback, 200);
|
||||
super(callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const store = useSharedStore()
|
||||
const store = useSharedStore();
|
||||
onMounted(() => {
|
||||
// 获取系统参数
|
||||
getSystemInfo().then((res) => {
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'shortcut icon'
|
||||
link.href = res.data.logo
|
||||
document.head.appendChild(link)
|
||||
})
|
||||
const link = document.createElement("link");
|
||||
link.rel = "shortcut icon";
|
||||
link.href = res.data.logo;
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
if (!isChrome() && !isMobile()) {
|
||||
showMessageInfo("建议使用 Chrome 浏览器以获得最佳体验。")
|
||||
showMessageInfo("建议使用 Chrome 浏览器以获得最佳体验。");
|
||||
}
|
||||
|
||||
checkSession().then(() => {
|
||||
store.setIsLogin(true)
|
||||
}).catch(()=>{})
|
||||
})
|
||||
checkSession()
|
||||
.then(() => {
|
||||
store.setIsLogin(true);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
watch(() => store.isLogin, (val) => {
|
||||
if (val) {
|
||||
connect()
|
||||
}
|
||||
})
|
||||
// 设置主题
|
||||
document.documentElement.setAttribute("data-theme", store.theme);
|
||||
});
|
||||
|
||||
const handler = ref(0)
|
||||
// 初始化 websocket 连接
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
watch(
|
||||
() => store.isLogin,
|
||||
(val) => {
|
||||
if (val) {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
const clientId = getClientId()
|
||||
const _socket = new WebSocket(host + `/api/ws?client_id=${clientId}`,["token",getUserToken()]);
|
||||
_socket.addEventListener('open', () => {
|
||||
console.log('WebSocket 已连接')
|
||||
);
|
||||
|
||||
const handler = ref(0);
|
||||
// 初始化 websocket 连接
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST;
|
||||
if (host === "") {
|
||||
if (location.protocol === "https:") {
|
||||
host = "wss://" + location.host;
|
||||
} else {
|
||||
host = "ws://" + location.host;
|
||||
}
|
||||
}
|
||||
const clientId = getClientId();
|
||||
const _socket = new WebSocket(host + `/api/ws?client_id=${clientId}`, ["token", getUserToken()]);
|
||||
_socket.addEventListener("open", () => {
|
||||
console.log("WebSocket 已连接");
|
||||
handler.value = setInterval(() => {
|
||||
if (_socket.readyState === WebSocket.OPEN) {
|
||||
_socket.send(JSON.stringify({"type":"ping"}))
|
||||
_socket.send(JSON.stringify({ type: "ping" }));
|
||||
}
|
||||
},5000)
|
||||
})
|
||||
_socket.addEventListener('close', () => {
|
||||
clearInterval(handler.value)
|
||||
connect()
|
||||
}, 5000);
|
||||
});
|
||||
store.setSocket(_socket)
|
||||
}
|
||||
|
||||
_socket.addEventListener("close", () => {
|
||||
clearInterval(handler.value);
|
||||
connect();
|
||||
});
|
||||
store.setSocket(_socket);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="stylus">
|
||||
html, body {
|
||||
margin: 0;
|
||||
@@ -137,4 +143,5 @@ html, body {
|
||||
color #07C160
|
||||
}
|
||||
|
||||
@import '@/assets/iconfont/iconfont.css'
|
||||
</style>
|
||||
|
@@ -16,29 +16,18 @@
|
||||
height: 100%;
|
||||
}
|
||||
.el-aside {
|
||||
//background-color: $sideBgColor;
|
||||
padding 10px
|
||||
width var(--el-aside-width, 320px)
|
||||
|
||||
.media-page {
|
||||
display: flex
|
||||
flex-flow: column
|
||||
//background-color: $sideBgColor
|
||||
border-radius 10px
|
||||
padding 10px 0
|
||||
|
||||
.search-box {
|
||||
flex-wrap: wrap
|
||||
margin-bottom: 10px
|
||||
// padding: 10px 0;
|
||||
|
||||
// .search-input {
|
||||
// --el-input-bg-color: #363535
|
||||
// --el-input-border-color: #464545
|
||||
// --el-input-focus-border-color:#b0a0f8
|
||||
// --el-input-hover-border-color: #2DA39A
|
||||
// box-shadow: none
|
||||
// }
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
@@ -58,16 +47,14 @@
|
||||
width: 100%
|
||||
justify-content: flex-start
|
||||
padding: 8px 12px
|
||||
//border-bottom: 1px solid #3c3c3c
|
||||
//border: 1px solid #3c3c3c
|
||||
cursor: pointer
|
||||
// border: 1px solid #3c3c3c
|
||||
border: 1px solid var(--theme-bg-color)
|
||||
margin-bottom 6px
|
||||
border-radius 5px
|
||||
|
||||
&:hover {
|
||||
// background-color :rgba(239, 241, 246, 0.64);
|
||||
border: 1px solid var(--border-active);
|
||||
|
||||
border: 1px solid var(--border-active);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
@@ -115,10 +102,9 @@
|
||||
}
|
||||
|
||||
.chat-list-item.active {
|
||||
background-color :var(--theme-bg);
|
||||
box-shadow: 0px 3px 9px rgba(112,144,176,0.12);
|
||||
|
||||
border: 1px solid var(--shadow-color);
|
||||
background-color :var(--theme-bg);
|
||||
box-shadow: 0 3px 9px rgba(112, 144, 176, 0.12);
|
||||
border: 1px solid var(--border-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +134,7 @@
|
||||
color var(--el-text-color-primary)
|
||||
|
||||
.chat-config {
|
||||
height 30px
|
||||
height 50px
|
||||
padding 10px 30px
|
||||
display flex
|
||||
justify-content center
|
||||
|
@@ -3,7 +3,7 @@
|
||||
--text-secondary: #8a939d;
|
||||
--el-color-primary: rgb(107, 80, 225);
|
||||
--theme-textcolor-normal:#b0a0f8;
|
||||
--el-border-radius-base: 8px;
|
||||
--el-border-radius-base: 5px;
|
||||
--el-color-primary-light-5:rgb(107, 85, 255);
|
||||
--el-color-primary-light-3:rgb(78, 51, 254);
|
||||
--theme-btn-color:rgba(117, 81, 255, 1)
|
||||
@@ -35,7 +35,8 @@
|
||||
// #e7e7e8
|
||||
}
|
||||
.el-dialog{
|
||||
--el-border-radius-base: 20px;
|
||||
//--el-border-radius-base: calc(var(--el-component-size) / 2);
|
||||
--el-dialog-border-radius: 10px
|
||||
}
|
||||
.login-box{
|
||||
--el-component-size: 48px;
|
||||
@@ -90,7 +91,11 @@
|
||||
line-height: 28px;
|
||||
}
|
||||
.el-button--primary{
|
||||
border-radius: 8px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
height auto
|
||||
}
|
||||
/* 设置滚动条的宽度 */
|
||||
::-webkit-scrollbar {
|
||||
@@ -117,10 +122,10 @@
|
||||
}
|
||||
//.el-message-box
|
||||
.el-message-box{
|
||||
--el-messagebox-border-radius:18px
|
||||
--el-messagebox-border-radius: 10px
|
||||
}
|
||||
.el-message-box__container{
|
||||
border-top: 1px solid #dbd3f4;
|
||||
//border-top: 1px solid #dbd3f4;
|
||||
padding-top: 7px;
|
||||
.el-message-box__message{
|
||||
--text-color:var(--theme-text-color-primary)
|
||||
|
@@ -43,6 +43,20 @@
|
||||
display flex
|
||||
align-items center
|
||||
flex-direction column
|
||||
|
||||
.icon-expand {
|
||||
font-size 24px
|
||||
margin-bottom 10px
|
||||
cursor pointer
|
||||
color var(--text-color)
|
||||
}
|
||||
|
||||
.icon-colspan {
|
||||
font-size 18px
|
||||
margin-left 3px
|
||||
cursor pointer
|
||||
color var(--text-color)
|
||||
}
|
||||
}
|
||||
.menu-list-collapse{
|
||||
.flex-center-col{
|
||||
@@ -63,14 +77,9 @@
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
.el-icon{
|
||||
margin: 0 4px;
|
||||
width 26px !important;
|
||||
height: 26px !important;
|
||||
}
|
||||
.menu-title{
|
||||
font-size: 15px !important;
|
||||
margin-bottom: 0px !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,11 +126,15 @@
|
||||
|
||||
}
|
||||
&.active{
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
filter: none !important;
|
||||
.el-icon{
|
||||
background: rgba(79, 89, 102, .122);
|
||||
}
|
||||
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.bot{
|
||||
|
@@ -82,7 +82,6 @@
|
||||
.task-list-box {
|
||||
background: var(--chat-bg);
|
||||
width 100%
|
||||
padding 10px
|
||||
color var(--text-theme-color)
|
||||
overflow-x hidden
|
||||
|
||||
|
@@ -195,7 +195,7 @@
|
||||
.task-list-box {
|
||||
background: var(--chat-bg);
|
||||
width 100%
|
||||
padding 0 10px 10px 10px
|
||||
//padding 0 10px 10px 10px
|
||||
color var(--text-theme-color)
|
||||
overflow-x hidden
|
||||
|
||||
|
@@ -84,7 +84,6 @@
|
||||
.task-list-box {
|
||||
background: var(--chat-bg);
|
||||
width 100%
|
||||
padding 0 10px 10px 10px
|
||||
color: var(--text-theme-color)
|
||||
overflow-x hidden
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
.index-page {
|
||||
margin: 0
|
||||
overflow hidden
|
||||
@@ -7,9 +5,8 @@
|
||||
display flex
|
||||
justify-content center
|
||||
align-items baseline
|
||||
padding-top 158px
|
||||
min-height: 75vh
|
||||
background: var(--theme-bg) !important
|
||||
height: 100vh
|
||||
|
||||
|
||||
|
||||
|
@@ -251,7 +251,6 @@
|
||||
|
||||
.text {
|
||||
margin-right 10px
|
||||
color #000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +351,8 @@
|
||||
border-radius 5px
|
||||
padding 5px 10px
|
||||
cursor pointer
|
||||
color #000
|
||||
color: var(--theme-text-color-primary)
|
||||
background-color var(--btn-bg)
|
||||
|
||||
&:hover {
|
||||
opacity: 0.7
|
||||
|
@@ -170,6 +170,14 @@ body {
|
||||
.content-collapse {
|
||||
left: 65px;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
width: 100%;
|
||||
|
||||
.el-table__body-header {
|
||||
height 40px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
|
@@ -146,7 +146,7 @@
|
||||
font-size 12px
|
||||
padding 2px 5px
|
||||
background-color var(--sm-btn-bg)
|
||||
color: #fff
|
||||
color: #fff
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
|
||||
|
||||
.list-box {
|
||||
padding 0 0 0 20px
|
||||
padding 20px
|
||||
.item {
|
||||
display flex
|
||||
flex-flow row
|
||||
@@ -301,11 +301,13 @@
|
||||
.right {
|
||||
min-width 350px;
|
||||
font-size 14px
|
||||
padding 0 15px
|
||||
padding 0 0 0 15px
|
||||
display flex
|
||||
justify-content right
|
||||
|
||||
.tools {
|
||||
display flex
|
||||
justify-content left
|
||||
justify-content right
|
||||
align-items center
|
||||
flex-flow row
|
||||
height 90px
|
||||
@@ -313,9 +315,9 @@
|
||||
.btn-publish {
|
||||
padding 2px 10px
|
||||
|
||||
.text {
|
||||
margin-right 10px
|
||||
}
|
||||
// .text {
|
||||
// margin-right 10px
|
||||
// }
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
@@ -395,11 +397,12 @@
|
||||
|
||||
.btn {
|
||||
margin-right 10px
|
||||
color: #000
|
||||
color: var((--theme-text-color-primary))
|
||||
border none
|
||||
border-radius 5px
|
||||
padding 5px 10px
|
||||
cursor pointer
|
||||
background: var(--btn-bg)
|
||||
|
||||
|
||||
&:hover {
|
||||
|
3
web/src/assets/css/tailwind.css
Normal file
3
web/src/assets/css/tailwind.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
@@ -4,7 +4,7 @@
|
||||
--text-fb:#fff;
|
||||
--text-color: rgba(255, 255, 255, 1) !important; // 主要的文本颜色
|
||||
--normal-color: rgba(163, 174, 208, 1); // 普通颜色
|
||||
--el-text-color-primary: #fff;
|
||||
--el-text-color-primary: #fff;
|
||||
p, h1, h2, h3, h4, h5, h6, article {
|
||||
// color: var(--text-color) !important;
|
||||
font-family: $font-regular;
|
||||
@@ -25,6 +25,7 @@
|
||||
--card-bg:#252d58;
|
||||
--card-bg-table: rgba(17, 28, 68, 1);
|
||||
--theme-bg:rgb(13, 20, 53);
|
||||
--theme-bg-color: rgb(13, 20, 53);
|
||||
--theme-bg-all:rgb(13, 20, 53);
|
||||
--sign-bg: rgba(27, 37, 75, 1);
|
||||
--text-theme-color: #fff;
|
||||
@@ -40,8 +41,8 @@
|
||||
--el-bg-color:#141a36;
|
||||
--el-fill-color-blank: rgba(17, 28, 68, 1);
|
||||
--el-fill-color-light: rgba(86, 86, 95, .2);
|
||||
--el-color-primary-light-9:rgba(86, 86, 95, .2);
|
||||
--chat-wel-bg:#2d2f388a;
|
||||
--el-color-primary-light-9:rgba(86, 86, 95, .2);
|
||||
--chat-wel-bg:#2d2f388a;
|
||||
--theme-text-color-secondary: #a3aed0;
|
||||
// --el-pagination-button-bg-color: rgba(86,86,95,0.2);
|
||||
|
||||
@@ -67,4 +68,16 @@
|
||||
--chat-content-bg-list:rgba(86, 86, 95, .2);
|
||||
--hover-deep-color:#30323c;
|
||||
// --theme-text-tertiary: #e1e1e1;
|
||||
}
|
||||
|
||||
// 操作按钮
|
||||
--btn-bg: rgba(86, 86, 95, .5);
|
||||
|
||||
.el-table {
|
||||
// 表格表头背景
|
||||
--el-fill-color-darker: rgba(100, 100, 100, .5);
|
||||
--el-border-color-darker: #73767a;
|
||||
--el-table-border-color: rgba(100, 100, 100, .5);
|
||||
--el-table-row-hover-bg-color: rgba(16, 21, 43, .8);
|
||||
--el-table-current-row-bg-color: rgba(16, 21, 43, .8);
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@
|
||||
--card-bg:#fff;
|
||||
--theme-bg:linear-gradient(88deg, #fff3f3 1.44%, #e7e8ff);
|
||||
--theme-bg-all:#f5f7fd;
|
||||
--theme-bg-color: #f5f7fd;
|
||||
--sign-bg: rgba(244, 247, 254, 1);
|
||||
--text-theme-color: rgba(43, 54, 116, 1)
|
||||
--text-color-primary: rgba(67, 24, 255, 1);
|
||||
@@ -43,10 +44,13 @@
|
||||
|
||||
--chat-content-bg:#f5f7fc;
|
||||
--chat-list-bg: #0302020a;
|
||||
--chat-content-bg-list:#fff;
|
||||
--chat-wel-bg:rgba(247, 247, 248, 1);
|
||||
--el-pagination-button-bg-color: rgba(86,86,95,0.2);
|
||||
--hover-deep-color:#fff;
|
||||
--chat-content-bg-list: #fff;
|
||||
--chat-wel-bg: rgba(247, 247, 248, 1);
|
||||
--el-pagination-button-bg-color: rgba(86, 86, 95, 0.2);
|
||||
--hover-deep-color: #fff;
|
||||
|
||||
// 操作按钮
|
||||
--btn-bg: rgba(100, 100, 100, .1);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,8 @@
|
||||
|
||||
/* 在线链接服务仅供平台体验和调试使用,平台不承诺服务的稳定性,企业客户需下载字体包自行发布使用并做好备份。 */
|
||||
@font-face {
|
||||
font-family: 'iconfont'; /* Project id 4125778 */
|
||||
src: url('//at.alicdn.com/t/c/font_4125778_t6hhjiqu67.woff2?t=1733221702650') format('woff2'),
|
||||
url('//at.alicdn.com/t/c/font_4125778_t6hhjiqu67.woff?t=1733221702650') format('woff'),
|
||||
url('//at.alicdn.com/t/c/font_4125778_t6hhjiqu67.ttf?t=1733221702650') format('truetype');
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1734934068681') format('woff2'),
|
||||
url('iconfont.woff?t=1734934068681') format('woff'),
|
||||
url('iconfont.ttf?t=1734934068681') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -15,6 +13,154 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-redeem:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.icon-login:before {
|
||||
content: "\e636";
|
||||
}
|
||||
|
||||
.icon-present:before {
|
||||
content: "\e648";
|
||||
}
|
||||
|
||||
.icon-icon-warning:before {
|
||||
content: "\e671";
|
||||
}
|
||||
|
||||
.icon-help:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.icon-success:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.icon-error:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.icon-house:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.icon-vip4:before {
|
||||
content: "\e684";
|
||||
}
|
||||
|
||||
.icon-vip1:before {
|
||||
content: "\f90b";
|
||||
}
|
||||
|
||||
.icon-vip2:before {
|
||||
content: "\fabb";
|
||||
}
|
||||
|
||||
.icon-vip3:before {
|
||||
content: "\10135";
|
||||
}
|
||||
|
||||
.icon-conversation:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.icon-arrow-down:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.icon-arrow-up:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.icon-refresh:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
|
||||
.icon-refresh-bold:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-copy:before {
|
||||
content: "\e720";
|
||||
}
|
||||
|
||||
.icon-new-chat:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.icon-expand:before {
|
||||
content: "\e7a0";
|
||||
}
|
||||
|
||||
.icon-colspan:before {
|
||||
content: "\e79e";
|
||||
}
|
||||
|
||||
.icon-question:before {
|
||||
content: "\e8e9";
|
||||
}
|
||||
|
||||
.icon-AIduihua_jihuo:before {
|
||||
content: "\e6bb";
|
||||
}
|
||||
|
||||
.icon-MidJourney:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-stable-diffusion:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.icon-info:before {
|
||||
content: "\e6a0";
|
||||
}
|
||||
|
||||
.icon-more-horizontal:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.icon-xinghao:before {
|
||||
content: "\e8d6";
|
||||
}
|
||||
|
||||
.icon-plus:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.icon-plus-circle:before {
|
||||
content: "\e822";
|
||||
}
|
||||
|
||||
.icon-taiyang:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
||||
.icon-yueliang:before {
|
||||
content: "\e679";
|
||||
}
|
||||
|
||||
.icon-prev-page:before {
|
||||
content: "\e8ef";
|
||||
}
|
||||
|
||||
.icon-next-page:before {
|
||||
content: "\e8f0";
|
||||
}
|
||||
|
||||
.icon-search:before {
|
||||
content: "\e618";
|
||||
}
|
||||
|
||||
.icon-sub_menu:before {
|
||||
content: "\e75e";
|
||||
}
|
||||
|
||||
.icon-google:before {
|
||||
content: "\ea0c";
|
||||
}
|
||||
|
||||
.icon-linggan:before {
|
||||
content: "\e641";
|
||||
}
|
||||
@@ -419,6 +565,3 @@
|
||||
content: "\e66f";
|
||||
}
|
||||
|
||||
.icon-house:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,265 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "3624396",
|
||||
"name": "兑换码",
|
||||
"font_class": "redeem",
|
||||
"unicode": "e61a",
|
||||
"unicode_decimal": 58906
|
||||
},
|
||||
{
|
||||
"icon_id": "8760110",
|
||||
"name": "login",
|
||||
"font_class": "login",
|
||||
"unicode": "e636",
|
||||
"unicode_decimal": 58934
|
||||
},
|
||||
{
|
||||
"icon_id": "29565277",
|
||||
"name": "礼物",
|
||||
"font_class": "present",
|
||||
"unicode": "e648",
|
||||
"unicode_decimal": 58952
|
||||
},
|
||||
{
|
||||
"icon_id": "13519527",
|
||||
"name": "警告",
|
||||
"font_class": "icon-warning",
|
||||
"unicode": "e671",
|
||||
"unicode_decimal": 58993
|
||||
},
|
||||
{
|
||||
"icon_id": "145466",
|
||||
"name": "帮助",
|
||||
"font_class": "help",
|
||||
"unicode": "e64a",
|
||||
"unicode_decimal": 58954
|
||||
},
|
||||
{
|
||||
"icon_id": "1951950",
|
||||
"name": "成功",
|
||||
"font_class": "success",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "6204756",
|
||||
"name": "失败",
|
||||
"font_class": "error",
|
||||
"unicode": "e64e",
|
||||
"unicode_decimal": 58958
|
||||
},
|
||||
{
|
||||
"icon_id": "3916695",
|
||||
"name": "首页",
|
||||
"font_class": "house",
|
||||
"unicode": "e619",
|
||||
"unicode_decimal": 58905
|
||||
},
|
||||
{
|
||||
"icon_id": "10583159",
|
||||
"name": "会员",
|
||||
"font_class": "vip4",
|
||||
"unicode": "e684",
|
||||
"unicode_decimal": 59012
|
||||
},
|
||||
{
|
||||
"icon_id": "23942994",
|
||||
"name": "会员",
|
||||
"font_class": "vip1",
|
||||
"unicode": "f90b",
|
||||
"unicode_decimal": 63755
|
||||
},
|
||||
{
|
||||
"icon_id": "24111538",
|
||||
"name": "会员",
|
||||
"font_class": "vip2",
|
||||
"unicode": "fabb",
|
||||
"unicode_decimal": 64187
|
||||
},
|
||||
{
|
||||
"icon_id": "37305926",
|
||||
"name": "会员VIP",
|
||||
"font_class": "vip3",
|
||||
"unicode": "10135",
|
||||
"unicode_decimal": 65845
|
||||
},
|
||||
{
|
||||
"icon_id": "41134022",
|
||||
"name": "新会话",
|
||||
"font_class": "conversation",
|
||||
"unicode": "e617",
|
||||
"unicode_decimal": 58903
|
||||
},
|
||||
{
|
||||
"icon_id": "17411805",
|
||||
"name": "Arrow Down",
|
||||
"font_class": "arrow-down",
|
||||
"unicode": "e615",
|
||||
"unicode_decimal": 58901
|
||||
},
|
||||
{
|
||||
"icon_id": "17411857",
|
||||
"name": "Arrow Up",
|
||||
"font_class": "arrow-up",
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "7736305",
|
||||
"name": "refresh",
|
||||
"font_class": "refresh",
|
||||
"unicode": "e90c",
|
||||
"unicode_decimal": 59660
|
||||
},
|
||||
{
|
||||
"icon_id": "1391302",
|
||||
"name": "Refresh",
|
||||
"font_class": "refresh-bold",
|
||||
"unicode": "e614",
|
||||
"unicode_decimal": 58900
|
||||
},
|
||||
{
|
||||
"icon_id": "19418384",
|
||||
"name": "copy",
|
||||
"font_class": "copy",
|
||||
"unicode": "e720",
|
||||
"unicode_decimal": 59168
|
||||
},
|
||||
{
|
||||
"icon_id": "20584689",
|
||||
"name": "New Chat",
|
||||
"font_class": "new-chat",
|
||||
"unicode": "e613",
|
||||
"unicode_decimal": 58899
|
||||
},
|
||||
{
|
||||
"icon_id": "23995596",
|
||||
"name": "收起展开-展开",
|
||||
"font_class": "expand",
|
||||
"unicode": "e7a0",
|
||||
"unicode_decimal": 59296
|
||||
},
|
||||
{
|
||||
"icon_id": "23995626",
|
||||
"name": "收起展开-收起",
|
||||
"font_class": "colspan",
|
||||
"unicode": "e79e",
|
||||
"unicode_decimal": 59294
|
||||
},
|
||||
{
|
||||
"icon_id": "1727527",
|
||||
"name": "306问号-线性圆框",
|
||||
"font_class": "question",
|
||||
"unicode": "e8e9",
|
||||
"unicode_decimal": 59625
|
||||
},
|
||||
{
|
||||
"icon_id": "35446270",
|
||||
"name": "AI对话_激活",
|
||||
"font_class": "AIduihua_jihuo",
|
||||
"unicode": "e6bb",
|
||||
"unicode_decimal": 59067
|
||||
},
|
||||
{
|
||||
"icon_id": "39584617",
|
||||
"name": "MidJourney-copy",
|
||||
"font_class": "MidJourney",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "42109955",
|
||||
"name": "stable-diffusion",
|
||||
"font_class": "stable-diffusion",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "1227734",
|
||||
"name": "info",
|
||||
"font_class": "info",
|
||||
"unicode": "e6a0",
|
||||
"unicode_decimal": 59040
|
||||
},
|
||||
{
|
||||
"icon_id": "159969",
|
||||
"name": "more",
|
||||
"font_class": "more-horizontal",
|
||||
"unicode": "e60d",
|
||||
"unicode_decimal": 58893
|
||||
},
|
||||
{
|
||||
"icon_id": "8434022",
|
||||
"name": "星号",
|
||||
"font_class": "xinghao",
|
||||
"unicode": "e8d6",
|
||||
"unicode_decimal": 59606
|
||||
},
|
||||
{
|
||||
"icon_id": "831577",
|
||||
"name": "plus",
|
||||
"font_class": "plus",
|
||||
"unicode": "e61f",
|
||||
"unicode_decimal": 58911
|
||||
},
|
||||
{
|
||||
"icon_id": "6151285",
|
||||
"name": "plus-circle",
|
||||
"font_class": "plus-circle",
|
||||
"unicode": "e822",
|
||||
"unicode_decimal": 59426
|
||||
},
|
||||
{
|
||||
"icon_id": "15056491",
|
||||
"name": "太阳",
|
||||
"font_class": "taiyang",
|
||||
"unicode": "e60b",
|
||||
"unicode_decimal": 58891
|
||||
},
|
||||
{
|
||||
"icon_id": "40094190",
|
||||
"name": "月亮-copy",
|
||||
"font_class": "yueliang",
|
||||
"unicode": "e679",
|
||||
"unicode_decimal": 59001
|
||||
},
|
||||
{
|
||||
"icon_id": "1727538",
|
||||
"name": "上一页",
|
||||
"font_class": "prev-page",
|
||||
"unicode": "e8ef",
|
||||
"unicode_decimal": 59631
|
||||
},
|
||||
{
|
||||
"icon_id": "1727540",
|
||||
"name": "下一页",
|
||||
"font_class": "next-page",
|
||||
"unicode": "e8f0",
|
||||
"unicode_decimal": 59632
|
||||
},
|
||||
{
|
||||
"icon_id": "2488134",
|
||||
"name": "搜索",
|
||||
"font_class": "search",
|
||||
"unicode": "e618",
|
||||
"unicode_decimal": 58904
|
||||
},
|
||||
{
|
||||
"icon_id": "9845558",
|
||||
"name": "sub_menu",
|
||||
"font_class": "sub_menu",
|
||||
"unicode": "e75e",
|
||||
"unicode_decimal": 59230
|
||||
},
|
||||
{
|
||||
"icon_id": "11983544",
|
||||
"name": "google",
|
||||
"font_class": "google",
|
||||
"unicode": "ea0c",
|
||||
"unicode_decimal": 59916
|
||||
},
|
||||
{
|
||||
"icon_id": "15330210",
|
||||
"name": "创意灵感",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,27 +1,22 @@
|
||||
<template>
|
||||
<div class="theme-box" @click="toggleTheme">
|
||||
<span class="iconfont icon-yueliang">{{
|
||||
themePage === "light" ? "" : ""
|
||||
}}</span>
|
||||
<span class="iconfont icon-yueliang">{{ themePage === "light" ? "" : "" }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
|
||||
// 定义主题状态,初始值从 localStorage 获取
|
||||
const themePage = ref(localStorage.getItem("theme") || "light");
|
||||
const store = useSharedStore();
|
||||
const themePage = ref(store.theme || "light");
|
||||
|
||||
// 切换主题函数
|
||||
const toggleTheme = () => {
|
||||
themePage.value = themePage.value === "light" ? "dark" : "light";
|
||||
document.documentElement.setAttribute("data-theme", themePage.value); // 设置 HTML 的 data-theme 属性
|
||||
localStorage.setItem("theme", themePage.value); // 保存主题到 localStorage
|
||||
store.setTheme(themePage.value); // 保存主题
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.documentElement.setAttribute("data-theme", themePage.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@@ -30,7 +25,7 @@ onMounted(() => {
|
||||
z-index :111
|
||||
position: fixed;
|
||||
right: 40px;
|
||||
bottom: 262px;
|
||||
bottom: 150px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
|
@@ -1,23 +1,11 @@
|
||||
<template>
|
||||
<div
|
||||
class="user-bill"
|
||||
v-loading="loading"
|
||||
element-loading-background="rgba(255,255,255,.3)"
|
||||
>
|
||||
<div class="user-bill" v-loading="loading" element-loading-background="rgba(255,255,255,.3)">
|
||||
<el-row v-if="items.length > 0">
|
||||
<el-table
|
||||
:data="items"
|
||||
:row-key="(row) => row.id"
|
||||
table-layout="auto"
|
||||
border
|
||||
>
|
||||
<el-table :data="items" :row-key="(row) => row.id" table-layout="auto" border>
|
||||
<el-table-column prop="order_no" label="订单号">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.order_no }}</span>
|
||||
<el-icon
|
||||
class="copy-order-no"
|
||||
:data-clipboard-text="scope.row.order_no"
|
||||
>
|
||||
<el-icon class="copy-order-no" :data-clipboard-text="scope.row.order_no">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</template>
|
||||
@@ -33,16 +21,14 @@
|
||||
<el-table-column prop="pay_name" label="支付名称" />
|
||||
<el-table-column label="支付时间">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row['pay_time']">{{
|
||||
dateFormat(scope.row["pay_time"])
|
||||
}}</span>
|
||||
<span v-if="scope.row['pay_time']">{{ dateFormat(scope.row["pay_time"]) }}</span>
|
||||
<el-tag v-else>未支付</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-row>
|
||||
<el-empty :image-size="100" v-else :image="nodata" description="暂无数据" />
|
||||
<div class="pagination">
|
||||
<div class="pagination pb-5">
|
||||
<el-pagination
|
||||
v-if="total > 0"
|
||||
background
|
||||
|
@@ -12,22 +12,14 @@
|
||||
|
||||
<div class="breadcrumb">
|
||||
<el-breadcrumb :separator-icon="ArrowRight">
|
||||
<el-breadcrumb-item v-for="item in breadcrumb">{{
|
||||
item.title
|
||||
}}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="item in breadcrumb" :key="item.title">{{ item.title }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
<!-- 切换主题 -->
|
||||
<el-switch
|
||||
style="margin-right: 10px"
|
||||
v-model="dark"
|
||||
inline-prompt
|
||||
:active-action-icon="Moon"
|
||||
:inactive-action-icon="Sunny"
|
||||
@change="changeTheme"
|
||||
/>
|
||||
<el-switch style="margin-right: 10px" v-model="dark" inline-prompt :active-action-icon="Moon"
|
||||
:inactive-action-icon="Sunny" @change="changeTheme"/>
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" :hide-on-click="true" trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
@@ -38,9 +30,7 @@
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-version"></i> 当前版本:{{ version }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item><i class="iconfont icon-version"></i> 当前版本:{{ version }}</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="logout">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span>退出登录</span>
|
||||
@@ -53,21 +43,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { getMenuItems, useSidebarStore } from "@/store/sidebar";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
ArrowDown,
|
||||
ArrowRight,
|
||||
Expand,
|
||||
Fold,
|
||||
Moon,
|
||||
Sunny
|
||||
} from "@element-plus/icons-vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { removeAdminToken } from "@/store/session";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {getMenuItems, useSidebarStore} from "@/store/sidebar";
|
||||
import {useRouter} from "vue-router";
|
||||
import {ArrowDown, ArrowRight, Expand, Fold, Moon, Sunny} from "@element-plus/icons-vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {removeAdminToken} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const version = ref(process.env.VUE_APP_VERSION);
|
||||
const avatar = ref("/images/user-info.jpg");
|
||||
@@ -76,17 +59,17 @@ const router = useRouter();
|
||||
const breadcrumb = ref([]);
|
||||
|
||||
const store = useSharedStore();
|
||||
const dark = ref(store.adminTheme === "dark");
|
||||
const theme = ref(store.adminTheme);
|
||||
const dark = ref(store.theme === "dark");
|
||||
const theme = ref(store.theme);
|
||||
watch(
|
||||
() => store.adminTheme,
|
||||
() => store.theme,
|
||||
(val) => {
|
||||
theme.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
const changeTheme = () => {
|
||||
store.setAdminTheme(dark.value ? "dark" : "light");
|
||||
store.setTheme(dark.value ? "dark" : "light");
|
||||
};
|
||||
|
||||
router.afterEach((to) => {
|
||||
@@ -107,7 +90,7 @@ const initBreadCrumb = (path) => {
|
||||
if (items[i].index === path) {
|
||||
breadcrumb.value.push({
|
||||
title: items[i].title,
|
||||
path: items[i].index
|
||||
path: items[i].index,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -121,11 +104,11 @@ const initBreadCrumb = (path) => {
|
||||
if (subs[j].index === path) {
|
||||
breadcrumb.value.push({
|
||||
title: items[i].title,
|
||||
path: items[i].index
|
||||
path: items[i].index,
|
||||
});
|
||||
breadcrumb.value.push({
|
||||
title: subs[j].title,
|
||||
path: subs[j].index
|
||||
path: subs[j].index,
|
||||
});
|
||||
bk = true;
|
||||
break;
|
||||
|
@@ -70,8 +70,8 @@ httpGet('/api/admin/config/get?key=system').then(res => {
|
||||
ElMessage.error("加载系统配置失败: " + e.message)
|
||||
})
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.adminTheme)
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
const theme = ref(store.theme)
|
||||
watch(() => store.theme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
const items = [
|
||||
|
@@ -42,8 +42,8 @@ import {useSharedStore} from "@/store/sharedata";
|
||||
import {ref, watch} from "vue";
|
||||
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.adminTheme)
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
const theme = ref(store.theme)
|
||||
watch(() => store.theme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
const router = useRouter();
|
||||
|
@@ -14,6 +14,8 @@ import App from "./App.vue";
|
||||
import { useThemeStore } from "@/store/theme";
|
||||
import { createPinia } from "pinia";
|
||||
import "animate.css/animate.min.css";
|
||||
import "@/assets/css/tailwind.css";
|
||||
|
||||
import {
|
||||
ActionSheet,
|
||||
Badge,
|
||||
@@ -59,7 +61,7 @@ import {
|
||||
Tabs,
|
||||
Tag,
|
||||
TextEllipsis,
|
||||
Uploader
|
||||
Uploader,
|
||||
} from "vant";
|
||||
import { router } from "@/router";
|
||||
import "v3-waterfall/dist/style.css";
|
||||
|
@@ -1,77 +1,73 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import Storage from 'good-storage'
|
||||
import { defineStore } from "pinia";
|
||||
import Storage from "good-storage";
|
||||
|
||||
export const useSharedStore = defineStore('shared', {
|
||||
state: () => ({
|
||||
showLoginDialog: false,
|
||||
chatListStyle: Storage.get("chat_list_style","chat"),
|
||||
chatStream: Storage.get("chat_stream",true),
|
||||
socket: {conn:null, handlers:{}},
|
||||
mobileTheme: Storage.get("mobile_theme", "light"),
|
||||
adminTheme: Storage.get("admin_theme", "light"),
|
||||
isLogin: false
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setShowLoginDialog(value) {
|
||||
this.showLoginDialog = value;
|
||||
},
|
||||
setChatListStyle(value) {
|
||||
this.chatListStyle = value;
|
||||
Storage.set("chat_list_style", value);
|
||||
},
|
||||
setChatStream(value) {
|
||||
this.chatStream = value;
|
||||
Storage.set("chat_stream", value);
|
||||
},
|
||||
setSocket(value) {
|
||||
for (const key in this.socket.handlers) {
|
||||
this.setMessageHandler(value, this.socket.handlers[key])
|
||||
}
|
||||
this.socket.conn = value
|
||||
},
|
||||
addMessageHandler(key, callback) {
|
||||
if (!this.socket.handlers[key]) {
|
||||
this.socket.handlers[key] = callback;
|
||||
}
|
||||
this.setMessageHandler(this.socket.conn, callback)
|
||||
},
|
||||
|
||||
setMessageHandler(conn, callback) {
|
||||
if (!conn) {
|
||||
return
|
||||
}
|
||||
conn.addEventListener('message', (event) => {
|
||||
try {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
callback(JSON.parse(String(reader.result)))
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removeMessageHandler(key) {
|
||||
if (this.socket.conn && this.socket.conn.readyState === WebSocket.OPEN) {
|
||||
this.socket.conn.removeEventListener('message', this.socket.handlers[key])
|
||||
}
|
||||
delete this.socket.handlers[key]
|
||||
},
|
||||
setMobileTheme(theme) {
|
||||
this.mobileTheme = theme
|
||||
Storage.set("mobile_theme", theme)
|
||||
},
|
||||
setAdminTheme(theme) {
|
||||
this.adminTheme = theme
|
||||
Storage.set("admin_theme", theme)
|
||||
},
|
||||
setIsLogin(value) {
|
||||
this.isLogin = value
|
||||
}
|
||||
export const useSharedStore = defineStore("shared", {
|
||||
state: () => ({
|
||||
showLoginDialog: false,
|
||||
chatListStyle: Storage.get("chat_list_style", "chat"),
|
||||
chatStream: Storage.get("chat_stream", true),
|
||||
socket: { conn: null, handlers: {} },
|
||||
theme: Storage.get("theme", "light"),
|
||||
isLogin: false,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setShowLoginDialog(value) {
|
||||
this.showLoginDialog = value;
|
||||
},
|
||||
setChatListStyle(value) {
|
||||
this.chatListStyle = value;
|
||||
Storage.set("chat_list_style", value);
|
||||
},
|
||||
setChatStream(value) {
|
||||
this.chatStream = value;
|
||||
Storage.set("chat_stream", value);
|
||||
},
|
||||
setSocket(value) {
|
||||
for (const key in this.socket.handlers) {
|
||||
this.setMessageHandler(value, this.socket.handlers[key]);
|
||||
}
|
||||
this.socket.conn = value;
|
||||
},
|
||||
addMessageHandler(key, callback) {
|
||||
if (!this.socket.handlers[key]) {
|
||||
this.socket.handlers[key] = callback;
|
||||
}
|
||||
this.setMessageHandler(this.socket.conn, callback);
|
||||
},
|
||||
|
||||
setMessageHandler(conn, callback) {
|
||||
if (!conn) {
|
||||
return;
|
||||
}
|
||||
conn.addEventListener("message", (event) => {
|
||||
try {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
callback(JSON.parse(String(reader.result)));
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeMessageHandler(key) {
|
||||
if (this.socket.conn && this.socket.conn.readyState === WebSocket.OPEN) {
|
||||
this.socket.conn.removeEventListener("message", this.socket.handlers[key]);
|
||||
}
|
||||
delete this.socket.handlers[key];
|
||||
},
|
||||
setTheme(theme) {
|
||||
this.theme = theme;
|
||||
document.documentElement.setAttribute("data-theme", theme); // 设置 HTML 的 data-theme 属性
|
||||
Storage.set("theme", theme);
|
||||
},
|
||||
setIsLogin(value) {
|
||||
this.isLogin = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@@ -11,13 +11,7 @@
|
||||
</el-button>
|
||||
|
||||
<div class="search-box">
|
||||
<el-input
|
||||
v-model="chatName"
|
||||
placeholder="搜索会话"
|
||||
@keyup="searchChat($event)"
|
||||
style=""
|
||||
class="search-input"
|
||||
>
|
||||
<el-input v-model="chatName" placeholder="搜索会话" @keyup="searchChat($event)" style="" class="search-input">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon">
|
||||
<Search />
|
||||
@@ -25,96 +19,62 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div class="content" :style="{ height: leftBoxHeight + 'px' }">
|
||||
<el-row v-for="chat in chatList" :key="chat.chat_id">
|
||||
<div
|
||||
:class="
|
||||
chat.chat_id === chatId
|
||||
? 'chat-list-item active'
|
||||
: 'chat-list-item'
|
||||
"
|
||||
@click="loadChat(chat)"
|
||||
>
|
||||
<el-image :src="chat.icon" class="avatar" />
|
||||
<span class="chat-title-input" v-if="chat.edit">
|
||||
<el-input
|
||||
v-model="tmpChatTitle"
|
||||
size="small"
|
||||
@keydown="titleKeydown($event, chat)"
|
||||
:id="'chat-' + chat.chat_id"
|
||||
@blur="editConfirm(chat)"
|
||||
@click="stopPropagation($event)"
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</span>
|
||||
<span v-else class="chat-title">{{ chat.title }}</span>
|
||||
|
||||
<span class="chat-opt">
|
||||
<el-dropdown trigger="click">
|
||||
<span
|
||||
class="el-dropdown-link"
|
||||
<el-scrollbar :height="{ height: +'px' }">
|
||||
<div class="content">
|
||||
<el-row v-for="chat in chatList" :key="chat.chat_id">
|
||||
<div :class="chat.chat_id === chatId ? 'chat-list-item active' : 'chat-list-item'" @click="loadChat(chat)">
|
||||
<el-image :src="chat.icon" class="avatar" />
|
||||
<span class="chat-title-input" v-if="chat.edit">
|
||||
<el-input
|
||||
v-model="tmpChatTitle"
|
||||
size="small"
|
||||
@keydown="titleKeydown($event, chat)"
|
||||
:id="'chat-' + chat.chat_id"
|
||||
@blur="editConfirm(chat)"
|
||||
@click="stopPropagation($event)"
|
||||
>
|
||||
<el-icon><More /></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
:icon="Edit"
|
||||
@click="editChatTitle(chat)"
|
||||
>重命名</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
:icon="Delete"
|
||||
style="
|
||||
--el-text-color-regular: var(--el-color-danger);
|
||||
--el-dropdown-menuItem-hover-fill: #f8e1de;
|
||||
--el-dropdown-menuItem-hover-color: var(
|
||||
--el-color-danger
|
||||
);
|
||||
"
|
||||
@click="removeChat(chat)"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item :icon="Share" @click="shareChat(chat)"
|
||||
>分享</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
placeholder="请输入标题"
|
||||
/>
|
||||
</span>
|
||||
<span v-else class="chat-title">{{ chat.title }}</span>
|
||||
|
||||
<span class="chat-opt">
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link" @click="stopPropagation($event)">
|
||||
<el-icon><More /></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :icon="Edit" @click="editChatTitle(chat)">重命名</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:icon="Delete"
|
||||
style="
|
||||
--el-text-color-regular: var(--el-color-danger);
|
||||
--el-dropdown-menuItem-hover-fill: #f8e1de;
|
||||
--el-dropdown-menuItem-hover-color: var(--el-color-danger);
|
||||
"
|
||||
@click="removeChat(chat)"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item :icon="Share" @click="shareChat(chat)">分享</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="tool-box">
|
||||
<el-button type="primary" size="small" @click="clearAllChats">
|
||||
<i class="iconfont icon-clear"></i> 清除所有对话
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" @click="clearAllChats"> <i class="iconfont icon-clear"></i> 清除所有对话 </el-button>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main
|
||||
v-loading="loading"
|
||||
element-loading-background="rgba(122, 122, 122, 0.3)"
|
||||
>
|
||||
<el-main v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.3)">
|
||||
<div class="chat-container">
|
||||
<div class="chat-config">
|
||||
<el-select
|
||||
v-model="roleId"
|
||||
filterable
|
||||
placeholder="角色"
|
||||
@change="_newChat"
|
||||
class="role-select"
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<el-select v-model="roleId" filterable placeholder="角色" @change="_newChat" class="role-select" style="width: 150px">
|
||||
<el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id">
|
||||
<div class="role-option">
|
||||
<el-image :src="item.icon"></el-image>
|
||||
<span>{{ item.name }}</span>
|
||||
@@ -122,43 +82,21 @@
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select
|
||||
v-model="modelID"
|
||||
filterable
|
||||
placeholder="模型"
|
||||
@change="_newChat"
|
||||
:disabled="disableModel"
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
>
|
||||
<el-select v-model="modelID" filterable placeholder="模型" @change="_newChat" :disabled="disableModel" style="width: 150px">
|
||||
<el-option v-for="item in models" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span>{{ item.name }}</span>
|
||||
<el-tag
|
||||
style="margin-left: 5px; position: relative; top: -2px"
|
||||
type="info"
|
||||
size="small"
|
||||
>{{ item.power }}算力
|
||||
</el-tag>
|
||||
<el-tag style="margin-left: 5px; position: relative; top: -2px" type="info" size="small">{{ item.power }}算力 </el-tag>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div class="flex-center">
|
||||
<el-dropdown :hide-on-click="false" trigger="click">
|
||||
<span class="setting"
|
||||
><i class="iconfont icon-plugin"></i
|
||||
></span>
|
||||
<span class="setting"><i class="iconfont icon-plugin"></i></span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="tools-dropdown">
|
||||
<el-checkbox-group v-model="toolSelected">
|
||||
<el-dropdown-item v-for="item in tools" :key="item.id">
|
||||
<el-checkbox :value="item.id" :label="item.label" />
|
||||
<el-tooltip
|
||||
:content="item.description"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip :content="item.description" placement="right">
|
||||
<el-icon><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</el-dropdown-item>
|
||||
@@ -175,27 +113,13 @@
|
||||
|
||||
<div>
|
||||
<div id="container" :style="{ height: mainWinHeight + 'px' }">
|
||||
<div
|
||||
class="chat-box"
|
||||
id="chat-box"
|
||||
:style="{ height: chatBoxHeight + 'px' }"
|
||||
>
|
||||
<div class="chat-box" id="chat-box" :style="{ height: chatBoxHeight + 'px' }">
|
||||
<div v-if="showHello">
|
||||
<welcome @send="autofillPrompt" />
|
||||
</div>
|
||||
<div v-for="item in chatData" :key="item.id" v-else>
|
||||
<chat-prompt
|
||||
v-if="item.type === 'prompt'"
|
||||
:data="item"
|
||||
:list-style="listStyle"
|
||||
/>
|
||||
<chat-reply
|
||||
v-else-if="item.type === 'reply'"
|
||||
:data="item"
|
||||
@regen="reGenerate"
|
||||
:read-only="false"
|
||||
:list-style="listStyle"
|
||||
/>
|
||||
<chat-prompt v-if="item.type === 'prompt'" :data="item" :list-style="listStyle" />
|
||||
<chat-reply v-else-if="item.type === 'reply'" :data="item" @regen="reGenerate" :read-only="false" :list-style="listStyle" />
|
||||
</div>
|
||||
|
||||
<back-top :right="30" :bottom="155" />
|
||||
@@ -259,56 +183,27 @@
|
||||
</div> -->
|
||||
<div class="flex little-btns">
|
||||
<span class="tool-item-btn" @click="realtimeChat">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="
|
||||
'实时语音对话,每次消耗' +
|
||||
config.advance_voice_power +
|
||||
'算力'
|
||||
"
|
||||
>
|
||||
<el-tooltip class="box-item" effect="dark" :content="'实时语音对话,每次消耗' + config.advance_voice_power + '算力'">
|
||||
<i class="iconfont icon-mic-bold"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
|
||||
<span class="tool-item-btn" v-if="isLogin">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="上传附件"
|
||||
>
|
||||
<file-select
|
||||
v-if="isLogin"
|
||||
:user-id="loginUser.id"
|
||||
@selected="insertFile"
|
||||
/>
|
||||
<el-tooltip class="box-item" effect="dark" content="上传附件">
|
||||
<file-select v-if="isLogin" :user-id="loginUser.id" @selected="insertFile" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex little-btns">
|
||||
<span class="send-btn tool-item-btn">
|
||||
<!-- showStopGenerate -->
|
||||
<el-button
|
||||
type="info"
|
||||
v-if="showStopGenerate"
|
||||
@click="stopGenerate"
|
||||
plain
|
||||
>
|
||||
<el-button type="info" v-if="showStopGenerate" @click="stopGenerate" plain>
|
||||
<el-icon>
|
||||
<VideoPause />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="sendMessage"
|
||||
style="color: #754ff6"
|
||||
v-else
|
||||
>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="发送"
|
||||
>
|
||||
<el-button @click="sendMessage" style="color: #754ff6" v-else>
|
||||
<el-tooltip class="box-item" effect="dark" content="发送">
|
||||
<el-icon><Promotion /></el-icon>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
@@ -328,21 +223,14 @@
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<el-dialog
|
||||
v-model="showNotice"
|
||||
:show-close="true"
|
||||
class="notice-dialog"
|
||||
title="网站公告"
|
||||
>
|
||||
<el-dialog v-model="showNotice" :show-close="true" class="notice-dialog" title="网站公告">
|
||||
<div class="notice">
|
||||
<div v-html="notice"></div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="notShow" type="primary"
|
||||
>我知道了,不再显示</el-button
|
||||
>
|
||||
<el-button @click="notShow" type="primary">我知道了,不再显示</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -361,11 +249,7 @@
|
||||
/>
|
||||
</el-dialog> -->
|
||||
|
||||
<el-dialog
|
||||
v-model="showConversationDialog"
|
||||
title="实时语音通话"
|
||||
:fullscreen="true"
|
||||
>
|
||||
<el-dialog v-model="showConversationDialog" title="实时语音通话" :fullscreen="true">
|
||||
<div v-loading="!frameLoaded">
|
||||
<iframe
|
||||
style="width: 100%; height: calc(100vh - 100px); border: none"
|
||||
@@ -381,17 +265,7 @@
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import ChatPrompt from "@/components/ChatPrompt.vue";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
import {
|
||||
Delete,
|
||||
Edit,
|
||||
InfoFilled,
|
||||
More,
|
||||
Plus,
|
||||
Promotion,
|
||||
Search,
|
||||
Share,
|
||||
VideoPause
|
||||
} from "@element-plus/icons-vue";
|
||||
import { Delete, Edit, InfoFilled, More, Plus, Promotion, Search, Share, VideoPause } from "@element-plus/icons-vue";
|
||||
import "highlight.js/styles/a11y-dark.css";
|
||||
import { isMobile, randString, removeArrayItem, UUID } from "@/utils/libs";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
@@ -492,7 +366,7 @@ const md = require("markdown-it")({
|
||||
breaks: true,
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true
|
||||
typographer: true,
|
||||
});
|
||||
// 获取系统公告
|
||||
httpGet("/api/config/get?key=notice")
|
||||
@@ -561,7 +435,7 @@ onMounted(() => {
|
||||
id: randString(32),
|
||||
icon: chatRole["icon"],
|
||||
prompt: prePrompt,
|
||||
content: data.body
|
||||
content: data.body,
|
||||
});
|
||||
isNewMsg.value = false;
|
||||
lineBuffer.value = data.body;
|
||||
@@ -583,16 +457,14 @@ onMounted(() => {
|
||||
httpPost("/api/chat/tokens", {
|
||||
text: "",
|
||||
model: getModelValue(modelID.value),
|
||||
chat_id: chatId.value
|
||||
chat_id: chatId.value,
|
||||
})
|
||||
.then((res) => {
|
||||
reply["created_at"] = new Date().getTime();
|
||||
reply["tokens"] = res.data;
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById("chat-box")
|
||||
.scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
document.getElementById("chat-box").scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
@@ -606,9 +478,7 @@ onMounted(() => {
|
||||
}
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById("chat-box")
|
||||
.scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
document.getElementById("chat-box").scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
localStorage.setItem("chat_id", chatId.value);
|
||||
});
|
||||
});
|
||||
@@ -736,10 +606,7 @@ const newChat = () => {
|
||||
disableModel.value = true;
|
||||
}
|
||||
// 已有新开的会话
|
||||
if (
|
||||
newChatItem.value !== null &&
|
||||
newChatItem.value["role_id"] === roles.value[0]["role_id"]
|
||||
) {
|
||||
if (newChatItem.value !== null && newChatItem.value["role_id"] === roles.value[0]["role_id"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -757,7 +624,7 @@ const newChat = () => {
|
||||
model_id: modelID.value,
|
||||
title: "",
|
||||
edit: false,
|
||||
removing: false
|
||||
removing: false,
|
||||
};
|
||||
showStopGenerate.value = false;
|
||||
loadChatHistory(chatId.value);
|
||||
@@ -818,7 +685,7 @@ const editConfirm = function (chat) {
|
||||
|
||||
httpPost("/api/chat/update", {
|
||||
chat_id: chat.chat_id,
|
||||
title: tmpChatTitle.value
|
||||
title: tmpChatTitle.value,
|
||||
})
|
||||
.then(() => {
|
||||
chat.title = tmpChatTitle.value;
|
||||
@@ -833,18 +700,14 @@ const removeChat = function (chat) {
|
||||
ElMessageBox.confirm(`该操作会删除"${chat.title}"`, "删除聊天", {
|
||||
confirmButtonText: "删除",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/chat/remove?chat_id=" + chat.chat_id)
|
||||
.then(() => {
|
||||
chatList.value = removeArrayItem(
|
||||
chatList.value,
|
||||
chat,
|
||||
function (e1, e2) {
|
||||
return e1.id === e2.id;
|
||||
}
|
||||
);
|
||||
chatList.value = removeArrayItem(chatList.value, chat, function (e1, e2) {
|
||||
return e1.id === e2.id;
|
||||
});
|
||||
// 重置会话
|
||||
_newChat();
|
||||
})
|
||||
@@ -867,9 +730,7 @@ const enableInput = () => {
|
||||
|
||||
const onInput = (e) => {
|
||||
// 根据输入的内容自动计算输入框的行数
|
||||
const lineHeight = parseFloat(
|
||||
window.getComputedStyle(inputRef.value).lineHeight
|
||||
);
|
||||
const lineHeight = parseFloat(window.getComputedStyle(inputRef.value).lineHeight);
|
||||
textHeightRef.value.style.width = inputRef.value.clientWidth + "px"; // 设定宽度和 textarea 相同
|
||||
const lines = Math.floor(textHeightRef.value.clientHeight / lineHeight);
|
||||
inputRef.value.scrollTo(0, inputRef.value.scrollHeight);
|
||||
@@ -936,13 +797,11 @@ const sendMessage = function () {
|
||||
icon: loginUser.value.avatar,
|
||||
content: content,
|
||||
model: getModelValue(modelID.value),
|
||||
created_at: new Date().getTime() / 1000
|
||||
created_at: new Date().getTime() / 1000,
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById("chat-box")
|
||||
.scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
document.getElementById("chat-box").scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
});
|
||||
|
||||
showHello.value = false;
|
||||
@@ -957,8 +816,8 @@ const sendMessage = function () {
|
||||
chat_id: chatId.value,
|
||||
content: content,
|
||||
tools: toolSelected.value,
|
||||
stream: stream.value
|
||||
}
|
||||
stream: stream.value,
|
||||
},
|
||||
})
|
||||
);
|
||||
tmpChatTitle.value = content;
|
||||
@@ -976,7 +835,7 @@ const clearAllChats = function () {
|
||||
dangerouslyUseHTMLString: true,
|
||||
showClose: true,
|
||||
closeOnClickModal: false,
|
||||
center: false
|
||||
center: false,
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/chat/clear")
|
||||
@@ -1009,7 +868,7 @@ const loadChatHistory = function (chatId) {
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: _role["icon"],
|
||||
content: _role["hello_msg"]
|
||||
content: _role["hello_msg"],
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1022,9 +881,7 @@ const loadChatHistory = function (chatId) {
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
document
|
||||
.getElementById("chat-box")
|
||||
.scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
document.getElementById("chat-box").scrollTo(0, document.getElementById("chat-box").scrollHeight);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -1049,7 +906,7 @@ const reGenerate = function (prompt) {
|
||||
type: "prompt",
|
||||
id: randString(32),
|
||||
icon: loginUser.value.avatar,
|
||||
content: text
|
||||
content: text,
|
||||
});
|
||||
store.socket.conn.send(
|
||||
JSON.stringify({
|
||||
@@ -1061,8 +918,8 @@ const reGenerate = function (prompt) {
|
||||
chat_id: chatId.value,
|
||||
content: text,
|
||||
tools: toolSelected.value,
|
||||
stream: stream.value
|
||||
}
|
||||
stream: stream.value,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -1077,11 +934,7 @@ const searchChat = function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
const items = [];
|
||||
for (let i = 0; i < allChats.value.length; i++) {
|
||||
if (
|
||||
allChats.value[i].title
|
||||
.toLowerCase()
|
||||
.indexOf(chatName.value.toLowerCase()) !== -1
|
||||
) {
|
||||
if (allChats.value[i].title.toLowerCase().indexOf(chatName.value.toLowerCase()) !== -1) {
|
||||
items.push(allChats.value[i]);
|
||||
}
|
||||
}
|
||||
@@ -1095,12 +948,7 @@ const shareChat = (chat) => {
|
||||
return ElMessage.error("请先选中一个会话");
|
||||
}
|
||||
|
||||
const url =
|
||||
location.protocol +
|
||||
"//" +
|
||||
location.host +
|
||||
"/chat/export?chat_id=" +
|
||||
chat.chat_id;
|
||||
const url = location.protocol + "//" + location.host + "/chat/export?chat_id=" + chat.chat_id;
|
||||
window.open(url, "_blank");
|
||||
};
|
||||
|
||||
@@ -1124,11 +972,7 @@ const insertFile = (file) => {
|
||||
files.value.push(file);
|
||||
};
|
||||
const removeFile = (file) => {
|
||||
files.value = removeArrayItem(
|
||||
files.value,
|
||||
file,
|
||||
(v1, v2) => v1.url === v2.url
|
||||
);
|
||||
files.value = removeArrayItem(files.value, file, (v1, v2) => v1.url === v2.url);
|
||||
};
|
||||
|
||||
// 实时语音对话
|
||||
|
@@ -11,13 +11,8 @@
|
||||
<el-form-item label="图片质量">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select v-model="params.quality" style="width: 176px">
|
||||
<el-option
|
||||
v-for="v in qualities"
|
||||
:label="v.name"
|
||||
:value="v.value"
|
||||
:key="v.value"
|
||||
/>
|
||||
<el-select v-model="params.quality" style="width: 150px">
|
||||
<el-option v-for="v in qualities" :label="v.name" :value="v.value" :key="v.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
@@ -28,13 +23,8 @@
|
||||
<el-form-item label="图片尺寸">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select v-model="params.size" style="width: 176px">
|
||||
<el-option
|
||||
v-for="v in sizes"
|
||||
:label="v"
|
||||
:value="v"
|
||||
:key="v"
|
||||
/>
|
||||
<el-select v-model="params.size" style="width: 150px">
|
||||
<el-option v-for="v in sizes" :label="v" :value="v" :key="v" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
@@ -45,19 +35,10 @@
|
||||
<el-form-item label="图片样式">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select v-model="params.style" style="width: 176px">
|
||||
<el-option
|
||||
v-for="v in styles"
|
||||
:label="v.name"
|
||||
:value="v.value"
|
||||
:key="v.value"
|
||||
/>
|
||||
<el-select v-model="params.style" style="width: 150px">
|
||||
<el-option v-for="v in styles" :label="v.name" :value="v.value" :key="v.value" />
|
||||
</el-select>
|
||||
<el-tooltip
|
||||
content="生动使模型倾向于生成超真实和戏剧性的图像"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="生动使模型倾向于生成超真实和戏剧性的图像" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -80,17 +61,8 @@
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
size="small"
|
||||
@click="generatePrompt"
|
||||
color="#5865f2"
|
||||
:disabled="isGenerating"
|
||||
>
|
||||
<i
|
||||
class="iconfont icon-chuangzuo"
|
||||
style="margin-right: 5px"
|
||||
></i>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
@@ -98,12 +70,8 @@
|
||||
<div class="text-info">
|
||||
<el-row :gutter="10">
|
||||
<el-text type="primary"
|
||||
>每次绘图消耗
|
||||
<el-text type="warning"
|
||||
>{{ dallPower }}算力;</el-text
|
||||
></el-text
|
||||
>每次绘图消耗 <el-text type="warning">{{ dallPower }}算力,</el-text></el-text
|
||||
>
|
||||
|
||||
<el-text type="primary"
|
||||
>当前可用
|
||||
<el-text type="warning"> {{ power }}算力</el-text>
|
||||
@@ -113,21 +81,16 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="submit-btn">
|
||||
<el-button type="primary" :dark="false" round @click="generate">
|
||||
立即生成
|
||||
</el-button>
|
||||
<el-button type="primary" :dark="false" round @click="generate"> 立即生成 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-list-box">
|
||||
<div
|
||||
class="task-list-inner"
|
||||
:style="{ height: listBoxHeight + 'px' }"
|
||||
>
|
||||
<div class="task-list-box pl-6 pr-6 pb-4 pt-4">
|
||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||
<div class="job-list-box">
|
||||
<h2>任务列表</h2>
|
||||
<h2 class="text-xl">任务列表</h2>
|
||||
<task-list :list="runningJobs" />
|
||||
<template v-if="finishedJobs.length > 0">
|
||||
<h2>创作记录</h2>
|
||||
<h2 class="text-xl">创作记录</h2>
|
||||
<div class="finish-job-list">
|
||||
<div v-if="finishedJobs.length > 0">
|
||||
<v3-waterfall
|
||||
@@ -170,22 +133,12 @@
|
||||
<div class="err-msg-container">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<el-popover
|
||||
title="错误详情"
|
||||
trigger="click"
|
||||
:width="250"
|
||||
:content="slotProp.item['err_msg']"
|
||||
placement="top"
|
||||
>
|
||||
<el-popover title="错误详情" trigger="click" :width="250" :content="slotProp.item['err_msg']" placement="top">
|
||||
<template #reference>
|
||||
<el-button type="info">详情</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click="removeImage(slotProp.item)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="danger" @click="removeImage(slotProp.item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,43 +156,21 @@
|
||||
|
||||
<div class="remove">
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeImage(slotProp.item)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(slotProp.item)" circle />
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
content="取消分享"
|
||||
placement="top"
|
||||
v-if="slotProp.item.publish"
|
||||
>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="publishImage(slotProp.item, false)"
|
||||
circle
|
||||
>
|
||||
<el-tooltip content="取消分享" placement="top" v-if="slotProp.item.publish">
|
||||
<el-button type="warning" @click="publishImage(slotProp.item, false)" circle>
|
||||
<i class="iconfont icon-cancel-share"></i>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="分享" placement="top" v-else>
|
||||
<el-button
|
||||
type="success"
|
||||
@click="publishImage(slotProp.item, true)"
|
||||
circle
|
||||
>
|
||||
<el-button type="success" @click="publishImage(slotProp.item, true)" circle>
|
||||
<i class="iconfont icon-share-bold"></i>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="复制提示词" placement="top">
|
||||
<el-button
|
||||
type="info"
|
||||
circle
|
||||
class="copy-prompt"
|
||||
:data-clipboard-text="slotProp.item.prompt"
|
||||
>
|
||||
<el-button type="info" circle class="copy-prompt" :data-clipboard-text="slotProp.item.prompt">
|
||||
<i class="iconfont icon-file"></i>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
@@ -255,12 +186,7 @@
|
||||
</template>
|
||||
</v3-waterfall>
|
||||
</div>
|
||||
<el-empty
|
||||
:image-size="100"
|
||||
:image="nodata"
|
||||
description="暂无记录"
|
||||
v-else
|
||||
/>
|
||||
<el-empty :image-size="100" :image="nodata" description="暂无记录" v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -318,19 +244,19 @@ window.onresize = () => {
|
||||
};
|
||||
const qualities = [
|
||||
{ name: "标准", value: "standard" },
|
||||
{ name: "高清", value: "hd" }
|
||||
{ name: "高清", value: "hd" },
|
||||
];
|
||||
const sizes = ["1024x1024", "1792x1024", "1024x1792"];
|
||||
const styles = [
|
||||
{ name: "生动", value: "vivid" },
|
||||
{ name: "自然", value: "natural" }
|
||||
{ name: "自然", value: "natural" },
|
||||
];
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
quality: "standard",
|
||||
size: "1024x1024",
|
||||
style: "vivid",
|
||||
prompt: ""
|
||||
prompt: "",
|
||||
});
|
||||
|
||||
const finishedJobs = ref([]);
|
||||
@@ -417,17 +343,14 @@ const fetchFinishJobs = () => {
|
||||
loading.value = true;
|
||||
page.value = page.value + 1;
|
||||
|
||||
httpGet(
|
||||
`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`
|
||||
)
|
||||
httpGet(`/api/dall/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`)
|
||||
.then((res) => {
|
||||
if (res.data.items.length < pageSize.value) {
|
||||
isOver.value = true;
|
||||
}
|
||||
const imageList = res.data.items;
|
||||
for (let i = 0; i < imageList.length; i++) {
|
||||
imageList[i]["img_thumb"] =
|
||||
imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
}
|
||||
if (page.value === 1) {
|
||||
finishedJobs.value = imageList;
|
||||
@@ -469,7 +392,7 @@ const removeImage = (item) => {
|
||||
ElMessageBox.confirm("此操作将会删除任务和图片,继续操作码?", "删除提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/dall/remove", { id: item.id })
|
||||
|
@@ -3,64 +3,24 @@
|
||||
<div class="tab-box">
|
||||
<div class="flex-center-col big-top-title xxx">
|
||||
<div class="flex-center-col" @click="isCollapse = !isCollapse">
|
||||
<el-icon v-if="isCollapse" class="openicon">
|
||||
<svg
|
||||
t="1733138242826"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1853"
|
||||
id="mx_n_1733138242827"
|
||||
width="200"
|
||||
height="200"
|
||||
>
|
||||
<path
|
||||
d="M715 267c135.31 0 245 109.69 245 245S850.31 757 715 757H309C173.69 757 64 647.31 64 512s109.69-245 245-245h406zM309 367c-80.081 0-145 64.919-145 145s64.919 145 145 145 145-64.919 145-145-64.919-145-145-145z"
|
||||
fill="#754ff6"
|
||||
p-id="1854"
|
||||
></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
<el-tooltip content="展开菜单" placement="right" v-if="isCollapse">
|
||||
<i class="iconfont icon-expand"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex" :class="{ 'top-collapse': !isCollapse }">
|
||||
<div class="top-avatar flex">
|
||||
<span class="title" v-if="!isCollapse">GeekAI</span>
|
||||
<img
|
||||
v-if="loginUser.id"
|
||||
:src="!!loginUser.avatar ? loginUser.avatar : avatarImg"
|
||||
alt=""
|
||||
:class="{ marr: !isCollapse }"
|
||||
/>
|
||||
<img v-if="loginUser.id" :src="!!loginUser.avatar ? loginUser.avatar : avatarImg" alt="" :class="{ marr: !isCollapse }" />
|
||||
</div>
|
||||
<div class="menuIcon xxx" @click="isCollapse = !isCollapse">
|
||||
<el-icon v-if="!isCollapse" class="openicon">
|
||||
<svg
|
||||
t="1733138405307"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2064"
|
||||
width="200"
|
||||
height="200"
|
||||
>
|
||||
<path
|
||||
d="M715 267c135.31 0 245 109.69 245 245S850.31 757 715 757H309C173.69 757 64 647.31 64 512s109.69-245 245-245h406z m0 100c-80.081 0-145 64.919-145 145s64.919 145 145 145 145-64.919 145-145-64.919-145-145-145z"
|
||||
fill="#754ff6"
|
||||
p-id="2065"
|
||||
></path>
|
||||
</svg>
|
||||
</el-icon>
|
||||
<el-tooltip content="关闭菜单" placement="right" v-if="!isCollapse">
|
||||
<i class="iconfont icon-colspan"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="menu-list"
|
||||
:style="{ width: isCollapse ? '65px' : '170px' }"
|
||||
:class="{ 'menu-list-collapse': !isCollapse }"
|
||||
>
|
||||
<div class="menu-list" :style="{ width: isCollapse ? '65px' : '170px' }" :class="{ 'menu-list-collapse': !isCollapse }">
|
||||
<ul>
|
||||
<li
|
||||
class="menu-list-item flex-center-col"
|
||||
@@ -69,11 +29,11 @@
|
||||
@click="changeNav(item)"
|
||||
:class="item.url === curPath ? 'active' : ''"
|
||||
>
|
||||
<el-image :src="item.icon" class="el-icon" />
|
||||
<div
|
||||
class="menu-title"
|
||||
:class="{ 'menu-title-collapse': !isCollapse }"
|
||||
>
|
||||
<span v-if="item.icon.startsWith('icon')" :class="{ 'mr-1 ml-2': !isCollapse }">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" class="el-icon ml-1" v-else />
|
||||
<div class="menu-title" :class="{ 'menu-title-collapse': !isCollapse }">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</li>
|
||||
@@ -91,11 +51,7 @@
|
||||
<div class="bot" :style="{ width: isCollapse ? '65px' : '170px' }">
|
||||
<div class="bot-line"></div>
|
||||
|
||||
<el-popover
|
||||
v-if="moreNavs.length > 0"
|
||||
placement="right-end"
|
||||
trigger="hover"
|
||||
>
|
||||
<el-popover v-if="moreNavs.length > 0" placement="right-end" trigger="hover">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<el-icon><CirclePlus /></el-icon>
|
||||
@@ -110,28 +66,21 @@
|
||||
:class="{
|
||||
active: item.url === curPath,
|
||||
moreTitle: index !== 3 && index !== 4,
|
||||
twoTittle: index === 3 || index === 4
|
||||
twoTittle: index === 3 || index === 4,
|
||||
}"
|
||||
>
|
||||
<a @click="changeNav(item)">
|
||||
<el-image
|
||||
:src="item.icon"
|
||||
style="width: 20px; height: 20px"
|
||||
/>
|
||||
<span
|
||||
:class="item.url === curPath ? 'title active' : 'title'"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
<span v-if="item.icon.startsWith('icon')">
|
||||
<i class="iconfont" :class="item.icon"></i>
|
||||
</span>
|
||||
<el-image :src="item.icon" style="width: 20px; height: 20px" v-else />
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{ item.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
placement="right-end"
|
||||
trigger="hover"
|
||||
v-if="loginUser.id"
|
||||
>
|
||||
<el-popover placement="right-end" trigger="hover" v-if="loginUser.id">
|
||||
<template #reference>
|
||||
<li class="menu-list-item flex-center-col">
|
||||
<el-icon><Setting /></el-icon>
|
||||
@@ -145,9 +94,7 @@
|
||||
<el-icon>
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
<span class="username title">{{
|
||||
loginUser.nickname
|
||||
}}</span>
|
||||
<span class="username title">{{ loginUser.nickname }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
@@ -160,12 +107,7 @@
|
||||
</template>
|
||||
</el-popover>
|
||||
<li class="menu-bot-item">
|
||||
<a
|
||||
:href="gitURL"
|
||||
class="link-button"
|
||||
target="_blank"
|
||||
v-if="!license.de_copy && !isCollapse"
|
||||
>
|
||||
<a :href="gitURL" class="link-button" target="_blank" v-if="!license.de_copy && !isCollapse">
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
|
||||
@@ -189,12 +131,7 @@
|
||||
@click="showNoticeLogin = true"
|
||||
></div>
|
||||
<div class="topheader" v-if="loginUser.id === undefined || !loginUser.id">
|
||||
<el-button
|
||||
@click="router.push('/login')"
|
||||
class="btn-go animate__animated animate__pulse animate__infinite"
|
||||
round
|
||||
>登录</el-button
|
||||
>
|
||||
<el-button @click="router.push('/login')" class="btn-go animate__animated animate__pulse animate__infinite" round>登录</el-button>
|
||||
</div>
|
||||
<!-- <div class="content custom-scroll"> -->
|
||||
<div class="content custom-scroll">
|
||||
@@ -206,35 +143,27 @@
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</el-scrollbar>
|
||||
<config-dialog
|
||||
v-if="loginUser.id"
|
||||
:show="showConfigDialog"
|
||||
@hide="showConfigDialog = false"
|
||||
/>
|
||||
<config-dialog v-if="loginUser.id" :show="showConfigDialog" @hide="showConfigDialog = false" />
|
||||
<el-dialog v-model="showNoticeLogin">
|
||||
<el-result icon="warning" title="未登录" sub-title="登录后解锁功能">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="router.push('/login')">登录</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<el-dialog v-model="showNoticeLogin">
|
||||
<el-result icon="warning" title="未登录" sub-title="登录后解锁功能">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="router.push('/login')"
|
||||
>登录</el-button
|
||||
>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CirclePlus, Setting } from "@element-plus/icons-vue";
|
||||
import { CirclePlus, Setting, UserFilled } from "@element-plus/icons-vue";
|
||||
import ThemeChange from "@/components/ThemeChange.vue";
|
||||
import avatarImg from "@/assets/img/avatar.jpg";
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { UserFilled } from "@element-plus/icons-vue";
|
||||
import { checkSession, getLicenseInfo, getSystemInfo } from "@/store/cache";
|
||||
import { removeUserToken } from "@/store/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
@@ -249,7 +178,6 @@ const curPath = ref();
|
||||
|
||||
const title = ref("");
|
||||
const showNoticeLogin = ref(false);
|
||||
// const mainWinHeight = window.innerHeight - 50;
|
||||
|
||||
/**
|
||||
* 从路径名中提取第一个路径段
|
||||
@@ -276,15 +204,9 @@ const getFirstPathSegment = (url) => {
|
||||
}
|
||||
};
|
||||
const loginUser = ref({});
|
||||
const mainWinHeight = loginUser.value.id
|
||||
? window.innerHeight
|
||||
: window.innerHeight;
|
||||
|
||||
const version = ref(process.env.VUE_APP_VERSION);
|
||||
const routerViewKey = ref(0);
|
||||
const showConfigDialog = ref(false);
|
||||
const license = ref({ de_copy: true });
|
||||
const docsURL = ref(process.env.VUE_APP_DOCS_URL);
|
||||
const gitURL = ref(process.env.VUE_APP_GIT_URL);
|
||||
|
||||
const store = useSharedStore();
|
||||
@@ -380,12 +302,6 @@ const logout = function () {
|
||||
ElMessage.error("注销失败!");
|
||||
});
|
||||
};
|
||||
|
||||
const loginCallback = () => {
|
||||
init();
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
@@ -1,297 +0,0 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="header">
|
||||
<div class="banner">
|
||||
<div class="logo">
|
||||
<el-image :src="logo" @click="router.push('/')" />
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar">
|
||||
<el-tooltip
|
||||
v-if="!license.de_copy"
|
||||
class="box-item"
|
||||
effect="light"
|
||||
content="部署文档"
|
||||
placement="bottom"
|
||||
>
|
||||
<a :href="docsURL" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-book"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
v-if="!license.de_copy"
|
||||
class="box-item"
|
||||
effect="light"
|
||||
content="项目源码"
|
||||
placement="bottom"
|
||||
>
|
||||
<a
|
||||
href="https://github.com/yangjian102621/chatgpt-plus"
|
||||
class="link-button"
|
||||
target="_blank"
|
||||
>
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
|
||||
<el-dropdown
|
||||
:hide-on-click="true"
|
||||
class="user-info"
|
||||
trigger="click"
|
||||
v-if="loginUser.id"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
<el-image :src="loginUser.avatar" />
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="user-info-menu">
|
||||
<el-dropdown-item @click="showConfigDialog = true">
|
||||
<el-icon>
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
<span class="username">{{ loginUser.nickname }}</span>
|
||||
</el-dropdown-item>
|
||||
|
||||
<div v-if="!license.de_copy">
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-book"></i>
|
||||
<a :href="docsURL" target="_blank"> 用户手册 </a>
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item>
|
||||
<i class="iconfont icon-github"></i>
|
||||
<a :href="gitURL" target="_blank"> GeekAI {{ version }} </a>
|
||||
</el-dropdown-item>
|
||||
</div>
|
||||
<el-divider style="margin: 2px 0" />
|
||||
<el-dropdown-item @click="logout">
|
||||
<i class="iconfont icon-logout"></i>
|
||||
<span>退出登录</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<div v-else>
|
||||
<el-button
|
||||
size="small"
|
||||
color="#21aa93"
|
||||
@click="store.setShowLoginDialog(true)"
|
||||
round
|
||||
>登录</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="navigator">
|
||||
<ul class="nav-items">
|
||||
<li v-for="item in mainNavs" :key="item.url">
|
||||
<el-tooltip effect="light" :content="item.name" placement="right">
|
||||
<a
|
||||
@click="changeNav(item)"
|
||||
:class="item.url === curPath ? 'active' : ''"
|
||||
>
|
||||
<el-image :src="item.icon" style="width: 30px; height: 30px" />
|
||||
</a>
|
||||
</el-tooltip>
|
||||
<span :class="item.url === curPath ? 'title active' : 'title'">{{
|
||||
item.name
|
||||
}}</span>
|
||||
</li>
|
||||
|
||||
<el-popover
|
||||
v-if="moreNavs.length > 0"
|
||||
placement="right-end"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #reference>
|
||||
<li>
|
||||
<a class="active">
|
||||
<el-image
|
||||
src="/images/menu/more.png"
|
||||
style="width: 30px; height: 30px"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</template>
|
||||
<template #default>
|
||||
<ul class="more-menus">
|
||||
<li
|
||||
v-for="item in moreNavs"
|
||||
:key="item.url"
|
||||
:class="item.url === curPath ? 'active' : ''"
|
||||
>
|
||||
<a @click="changeNav(item)">
|
||||
<el-image
|
||||
:src="item.icon"
|
||||
style="width: 20px; height: 20px"
|
||||
/>
|
||||
<span
|
||||
:class="item.url === curPath ? 'title active' : 'title'"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-popover>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="content custom-scroll"
|
||||
:style="{ height: mainWinHeight + 'px' }"
|
||||
>
|
||||
<router-view :key="routerViewKey" v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<component :is="Component"></component>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<login-dialog
|
||||
:show="show"
|
||||
@hide="store.setShowLoginDialog(false)"
|
||||
@success="loginCallback"
|
||||
/>
|
||||
<config-dialog
|
||||
v-if="loginUser.id"
|
||||
:show="showConfigDialog"
|
||||
@hide="showConfigDialog = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { httpGet } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { UserFilled } from "@element-plus/icons-vue";
|
||||
import { checkSession, getLicenseInfo, getSystemInfo } from "@/store/cache";
|
||||
import { removeUserToken } from "@/store/session";
|
||||
import LoginDialog from "@/components/LoginDialog.vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import ConfigDialog from "@/components/UserInfoDialog.vue";
|
||||
import { showMessageError } from "@/utils/dialog";
|
||||
|
||||
const router = useRouter();
|
||||
const logo = ref("");
|
||||
const mainNavs = ref([]);
|
||||
const moreNavs = ref([]);
|
||||
const curPath = ref(router.currentRoute.value.path);
|
||||
const title = ref("");
|
||||
const mainWinHeight = window.innerHeight - 50;
|
||||
const loginUser = ref({});
|
||||
const version = ref(process.env.VUE_APP_VERSION);
|
||||
const routerViewKey = ref(0);
|
||||
const showConfigDialog = ref(false);
|
||||
const license = ref({ de_copy: true });
|
||||
const docsURL = ref(process.env.VUE_APP_DOCS_URL);
|
||||
const gitURL = ref(process.env.VUE_APP_GIT_URL);
|
||||
|
||||
const store = useSharedStore();
|
||||
const show = ref(false);
|
||||
watch(
|
||||
() => store.showLoginDialog,
|
||||
(newValue) => {
|
||||
show.value = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
// 监听路由变化
|
||||
router.beforeEach((to, from, next) => {
|
||||
curPath.value = to.path;
|
||||
next();
|
||||
});
|
||||
|
||||
if (curPath.value === "/external") {
|
||||
curPath.value = router.currentRoute.value.query.url;
|
||||
}
|
||||
const changeNav = (item) => {
|
||||
curPath.value = item.url;
|
||||
if (item.url.indexOf("http") !== -1) {
|
||||
// 外部链接
|
||||
router.push({ name: "ExternalLink", query: { url: item.url } });
|
||||
} else {
|
||||
router.push(item.url);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getSystemInfo()
|
||||
.then((res) => {
|
||||
logo.value = res.data.logo;
|
||||
title.value = res.data.title;
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message);
|
||||
});
|
||||
// 获取菜单
|
||||
httpGet("/api/menu/list")
|
||||
.then((res) => {
|
||||
mainNavs.value = res.data;
|
||||
// 根据窗口的高度计算应该显示多少菜单
|
||||
const rows = Math.floor((window.innerHeight - 100) / 90);
|
||||
if (res.data.length > rows) {
|
||||
mainNavs.value = res.data.slice(0, rows);
|
||||
moreNavs.value = res.data.slice(rows);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("获取系统菜单失败:" + e.message);
|
||||
});
|
||||
|
||||
getLicenseInfo()
|
||||
.then((res) => {
|
||||
license.value = res.data;
|
||||
})
|
||||
.catch((e) => {
|
||||
license.value = { de_copy: false };
|
||||
showMessageError("获取 License 配置:" + e.message);
|
||||
});
|
||||
|
||||
init();
|
||||
});
|
||||
|
||||
const init = () => {
|
||||
checkSession()
|
||||
.then((user) => {
|
||||
loginUser.value = user;
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const logout = function () {
|
||||
httpGet("/api/user/logout")
|
||||
.then(() => {
|
||||
removeUserToken();
|
||||
store.setShowLoginDialog(true);
|
||||
store.setIsLogin(false);
|
||||
loginUser.value = {};
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1;
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("注销失败!");
|
||||
});
|
||||
};
|
||||
|
||||
const loginCallback = () => {
|
||||
init();
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import "@/assets/css/custom-scroll.styl"
|
||||
@import "@/assets/css/home.styl"
|
||||
</style>
|
@@ -18,20 +18,8 @@
|
||||
<div class="param-line pt">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="8" v-for="item in rates" :key="item.value">
|
||||
<div
|
||||
class="flex-col items-center"
|
||||
:class="
|
||||
item.value === params.rate
|
||||
? 'grid-content active'
|
||||
: 'grid-content'
|
||||
"
|
||||
@click="changeRate(item)"
|
||||
>
|
||||
<el-image
|
||||
class="icon"
|
||||
:src="item.img"
|
||||
fit="cover"
|
||||
></el-image>
|
||||
<div class="flex-col items-center" :class="item.value === params.rate ? 'grid-content active' : 'grid-content'" @click="changeRate(item)">
|
||||
<el-image class="icon" :src="item.img" fit="cover"></el-image>
|
||||
<div class="text">{{ item.text }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -42,23 +30,10 @@
|
||||
<el-form-item label="图片画质">
|
||||
<template #default>
|
||||
<div class="form-item-inner flex-row items-center">
|
||||
<el-select
|
||||
v-model="params.quality"
|
||||
placeholder="请选择"
|
||||
style="width: 175px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
</el-option>
|
||||
<el-select v-model="params.quality" placeholder="请选择" style="width: 150px">
|
||||
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
||||
</el-select>
|
||||
<el-tooltip
|
||||
content="生成的图片质量,质量越好出图越慢"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="生成的图片质量,质量越好出图越慢" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -70,11 +45,7 @@
|
||||
|
||||
<div class="param-line pt">
|
||||
<span>模型选择:</span>
|
||||
<el-tooltip
|
||||
content="MJ: 偏真实通用模型 <br/>NIJI: 偏动漫风格、适用于二次元模型"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="MJ: 偏真实通用模型 <br/>NIJI: 偏动漫风格、适用于二次元模型" raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -83,12 +54,7 @@
|
||||
<div class="param-line pt">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12" v-for="item in models" :key="item.value">
|
||||
<div
|
||||
:class="
|
||||
item.value === params.model ? 'model active' : 'model'
|
||||
"
|
||||
@click="changeModel(item)"
|
||||
>
|
||||
<div :class="item.value === params.model ? 'model active' : 'model'" @click="changeModel(item)">
|
||||
<el-image :src="item.img" fit="cover"></el-image>
|
||||
<div class="text">{{ item.text }}</div>
|
||||
</div>
|
||||
@@ -101,11 +67,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-switch v-model="params.tile" inactive-color="#464649" />
|
||||
<el-tooltip
|
||||
content="重复:--tile,参数释义:生成可用作重复平铺的图像,以创建无缝图案。"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="重复:--tile,参数释义:生成可用作重复平铺的图像,以创建无缝图案。" raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -138,12 +100,7 @@
|
||||
<el-form-item label="创意度">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-slider
|
||||
v-model.number="params.chaos"
|
||||
:max="100"
|
||||
:step="1"
|
||||
style="width: 180px"
|
||||
/>
|
||||
<el-slider v-model.number="params.chaos" :max="100" :step="1" style="width: 180px" />
|
||||
<el-tooltip
|
||||
content="参数用法:--chaos 或--c,取值范围: 0-100 <br/> 取值越高结果越发散,反之则稳定收敛<br /> 默认值0最为精准稳定"
|
||||
raw-content
|
||||
@@ -162,13 +119,7 @@
|
||||
<el-form-item label="风格化">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-slider
|
||||
v-model.number="params.stylize"
|
||||
:min="0"
|
||||
:max="1000"
|
||||
:step="1"
|
||||
style="width: 180px"
|
||||
/>
|
||||
<el-slider v-model.number="params.stylize" :min="0" :max="1000" :step="1" style="width: 180px" />
|
||||
<el-tooltip
|
||||
content="风格化:--stylize 或 --s,范围 1-1000,默认值100 <br/>高取值会产生非常艺术化但与提示关联性较低的图像"
|
||||
raw-content
|
||||
@@ -188,11 +139,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.seed" />
|
||||
<el-tooltip
|
||||
content="随机种子:--seed,默认值0表示随机产生 <br/>使用相同的种子参数和描述将产生相似的图像"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="随机种子:--seed,默认值0表示随机产生 <br/>使用相同的种子参数和描述将产生相似的图像" raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -204,25 +151,18 @@
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-list-box">
|
||||
<div class="task-list-box pl-6 pr-6 pb-4">
|
||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||
<div class="extra-params">
|
||||
<el-form>
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
class="title-tabs"
|
||||
@tabChange="tabChange"
|
||||
>
|
||||
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
|
||||
<el-tab-pane label="文生图" name="txt2img">
|
||||
<div class="prompt-box">
|
||||
<div class="param-line pt">
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>提示词:</span>
|
||||
<el-tooltip
|
||||
content="输入你想要的内容,用逗号分割"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="输入你想要的内容,用逗号分割" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -244,13 +184,7 @@
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
size="small"
|
||||
@click="generatePrompt"
|
||||
color="#5865f2"
|
||||
:disabled="isGenerating"
|
||||
>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
@@ -260,10 +194,7 @@
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>不希望出现的内容:(可选)</span>
|
||||
<el-tooltip
|
||||
content="不想出现在图片上的元素(例如:树,建筑)"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -284,33 +215,16 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="图生图" name="img2img">
|
||||
<div class="text">
|
||||
图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持
|
||||
PNG 和 JPG 格式图片;
|
||||
</div>
|
||||
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;</div>
|
||||
<div class="param-line">
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div
|
||||
class="img-item"
|
||||
v-for="imgURL in imgList"
|
||||
:key="imgURL"
|
||||
>
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover" />
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeUploadImage(imgURL)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle />
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
class="img-uploader"
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
@@ -322,12 +236,7 @@
|
||||
<el-form-item label="参考权重:">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-slider
|
||||
v-model.number="params.iw"
|
||||
:max="1"
|
||||
:step="0.01"
|
||||
style="width: 180px"
|
||||
/>
|
||||
<el-slider v-model.number="params.iw" :max="1" :step="0.01" style="width: 180px" />
|
||||
<el-tooltip
|
||||
content="使用图像权重参数--iw来调整图像 URL 与文本的重要性 <br/>权重较高时意味着图像提示将对完成的作业产生更大的影响"
|
||||
raw-content
|
||||
@@ -347,10 +256,7 @@
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>提示词:</span>
|
||||
<el-tooltip
|
||||
content="输入你想要的内容,用逗号分割"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="输入你想要的内容,用逗号分割" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -372,13 +278,7 @@
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
size="small"
|
||||
@click="generatePrompt"
|
||||
color="#5865f2"
|
||||
:disabled="isGenerating"
|
||||
>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
@@ -388,10 +288,7 @@
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>不希望出现的内容:(可选)</span>
|
||||
<el-tooltip
|
||||
content="不想出现在图片上的元素(例如:树,建筑)"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -413,31 +310,15 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="融图" name="blend">
|
||||
<div class="text">
|
||||
请上传两张以上的图片,最多不超过五张,超过五张图片请使用图生图功能
|
||||
</div>
|
||||
<div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用图生图功能</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div
|
||||
class="img-item"
|
||||
v-for="imgURL in imgList"
|
||||
:key="imgURL"
|
||||
>
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover" />
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeUploadImage(imgURL)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle />
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
class="img-uploader"
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
@@ -446,31 +327,15 @@
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="换脸" name="swapFace">
|
||||
<div class="text">
|
||||
请上传两张有脸部的图片,用左边图片的脸替换右边图片的脸
|
||||
</div>
|
||||
<div class="text">请上传两张有脸部的图片,用左边图片的脸替换右边图片的脸</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div
|
||||
class="img-item"
|
||||
v-for="imgURL in imgList"
|
||||
:key="imgURL"
|
||||
>
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover" />
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeUploadImage(imgURL)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle />
|
||||
</div>
|
||||
</div>
|
||||
<el-upload
|
||||
class="img-uploader"
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
@@ -485,29 +350,17 @@
|
||||
</el-badge>
|
||||
</template>
|
||||
|
||||
<div class="text">
|
||||
注意:只有于 niji6 和 v6
|
||||
模型支持一致性功能,如果选择其他模型此功能将会生成失败。
|
||||
</div>
|
||||
<div class="text">注意:只有于 niji6 和 v6 模型支持一致性功能,如果选择其他模型此功能将会生成失败。</div>
|
||||
<div class="param-line">
|
||||
<el-form-item label="角色一致性:" prop="cref">
|
||||
<el-input
|
||||
v-model="params.cref"
|
||||
placeholder="请输入图片URL或者上传图片"
|
||||
style="
|
||||
--el-input-focus-border-color: #b0a0f8;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
"
|
||||
style="--el-input-focus-border-color: #b0a0f8; max-width: 500px; width: 100%"
|
||||
size="small"
|
||||
>
|
||||
<template #append>
|
||||
<el-upload
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
@click="beforeUpload('cref')"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('cref')" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
@@ -522,20 +375,11 @@
|
||||
<el-input
|
||||
v-model="params.sref"
|
||||
placeholder="请输入图片URL或者上传图片"
|
||||
style="
|
||||
--el-input-focus-border-color: #b0a0f8;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
"
|
||||
style="--el-input-focus-border-color: #b0a0f8; max-width: 500px; width: 100%"
|
||||
size="small"
|
||||
>
|
||||
<template #append>
|
||||
<el-upload
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
@click="beforeUpload('sref')"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" @click="beforeUpload('sref')" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
@@ -549,17 +393,8 @@
|
||||
<el-form-item label="参考权重:">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-slider
|
||||
v-model.number="params.cw"
|
||||
:max="100"
|
||||
:step="1"
|
||||
style="width: 180px"
|
||||
/>
|
||||
<el-tooltip
|
||||
content="取值范围 0-100 <br/>默认值100参考原图的脸部、头发和衣服<br/>0则表示只换脸"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-slider v-model.number="params.cw" :max="100" :step="1" style="width: 180px" />
|
||||
<el-tooltip content="取值范围 0-100 <br/>默认值100参考原图的脸部、头发和衣服<br/>0则表示只换脸" raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -574,10 +409,7 @@
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>提示词:</span>
|
||||
<el-tooltip
|
||||
content="输入你想要的内容,用逗号分割"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="输入你想要的内容,用逗号分割" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -600,10 +432,7 @@
|
||||
<div class="flex-row justify-between items-center">
|
||||
<div class="flex-row justify-start items-center">
|
||||
<span>不希望出现的内容:(可选)</span>
|
||||
<el-tooltip
|
||||
content="不想出现在图片上的元素(例如:树,建筑)"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="不想出现在图片上的元素(例如:树,建筑)" placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -629,30 +458,24 @@
|
||||
<el-text type="primary"
|
||||
>每次绘图消耗
|
||||
<el-text type="warning">{{ mjPower }}算力;</el-text>
|
||||
U/V 操作消耗<el-text type="warning"
|
||||
>{{ mjActionPower }}算力;</el-text
|
||||
> </el-text
|
||||
U/V 操作消耗<el-text type="warning">{{ mjActionPower }}算力;</el-text> </el-text
|
||||
>
|
||||
<el-text type="primary"
|
||||
>当前可用算力:<el-text type="warning">{{
|
||||
power
|
||||
}}</el-text></el-text
|
||||
>当前可用算力:<el-text type="warning">{{ power }}</el-text></el-text
|
||||
>
|
||||
</el-row>
|
||||
|
||||
<div class="submit-btn">
|
||||
<el-button type="primary" :dark="false" @click="generate" round
|
||||
>立即生成</el-button
|
||||
>
|
||||
<el-button type="primary" :dark="false" @click="generate" round>立即生成</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="job-list-box">
|
||||
<h2>任务列表</h2>
|
||||
<h2 class="text-xl">任务列表</h2>
|
||||
<task-list :list="runningJobs" />
|
||||
<template v-if="finishedJobs.length > 0">
|
||||
<h2>创作记录</h2>
|
||||
<h2 class="text-xl">创作记录</h2>
|
||||
<div class="finish-job-list">
|
||||
<div v-if="finishedJobs.length > 0">
|
||||
<v3-waterfall
|
||||
@@ -695,22 +518,12 @@
|
||||
<div class="err-msg-container">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<el-popover
|
||||
title="错误详情"
|
||||
trigger="click"
|
||||
:width="250"
|
||||
:content="slotProp.item['err_msg']"
|
||||
placement="top"
|
||||
>
|
||||
<el-popover title="错误详情" trigger="click" :width="250" :content="slotProp.item['err_msg']" placement="top">
|
||||
<template #reference>
|
||||
<el-button type="info">详情</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click="removeImage(slotProp.item)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="danger" @click="removeImage(slotProp.item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -741,12 +554,7 @@
|
||||
<a @click="upscale(4, slotProp.item)">U4</a>
|
||||
</li>
|
||||
<li class="show-prompt">
|
||||
<el-popover
|
||||
placement="left"
|
||||
title="提示词"
|
||||
:width="240"
|
||||
trigger="hover"
|
||||
>
|
||||
<el-popover placement="left" title="提示词" :width="240" trigger="hover">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<ChromeFilled />
|
||||
@@ -756,12 +564,7 @@
|
||||
<template #default>
|
||||
<div class="mj-list-item-prompt">
|
||||
<span>{{ slotProp.item.prompt }}</span>
|
||||
<el-icon
|
||||
class="copy-prompt-mj"
|
||||
:data-clipboard-text="
|
||||
slotProp.item.prompt
|
||||
"
|
||||
>
|
||||
<el-icon class="copy-prompt-mj" :data-clipboard-text="slotProp.item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -789,48 +592,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="remove"
|
||||
v-if="slotProp.item.progress === 100"
|
||||
>
|
||||
<div class="remove" v-if="slotProp.item.progress === 100">
|
||||
<el-tooltip content="删除任务" placement="top">
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeImage(slotProp.item)"
|
||||
circle
|
||||
/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(slotProp.item)" circle />
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
content="取消发布"
|
||||
placement="top"
|
||||
v-if="slotProp.item.publish"
|
||||
>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="publishImage(slotProp.item, false)"
|
||||
circle
|
||||
>
|
||||
<el-tooltip content="取消发布" placement="top" v-if="slotProp.item.publish">
|
||||
<el-button type="warning" @click="publishImage(slotProp.item, false)" circle>
|
||||
<i class="iconfont icon-cancel-share"></i>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="发布图片" placement="top" v-else>
|
||||
<el-button
|
||||
type="success"
|
||||
@click="publishImage(slotProp.item, true)"
|
||||
circle
|
||||
>
|
||||
<el-button type="success" @click="publishImage(slotProp.item, true)" circle>
|
||||
<i class="iconfont icon-share-bold"></i>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="复制提示词" placement="top">
|
||||
<el-button
|
||||
type="success"
|
||||
class="copy-prompt-mj"
|
||||
:data-clipboard-text="slotProp.item.prompt"
|
||||
circle
|
||||
>
|
||||
<el-button type="success" class="copy-prompt-mj" :data-clipboard-text="slotProp.item.prompt" circle>
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
@@ -846,12 +624,7 @@
|
||||
</template>
|
||||
</v3-waterfall>
|
||||
</div>
|
||||
<el-empty
|
||||
:image-size="100"
|
||||
:image="nodata"
|
||||
description="暂无记录"
|
||||
v-else
|
||||
/>
|
||||
<el-empty :image-size="100" :image="nodata" description="暂无记录" v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -877,15 +650,7 @@
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
||||
import {
|
||||
ChromeFilled,
|
||||
Delete,
|
||||
DocumentCopy,
|
||||
InfoFilled,
|
||||
Picture,
|
||||
Plus,
|
||||
UploadFilled
|
||||
} from "@element-plus/icons-vue";
|
||||
import { ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus, UploadFilled } from "@element-plus/icons-vue";
|
||||
import nodata from "@/assets/img/no-data.png";
|
||||
import Compressor from "compressorjs";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
@@ -930,14 +695,14 @@ const rates = [
|
||||
css: "size16-9",
|
||||
value: "16:9",
|
||||
text: "16:9",
|
||||
img: "/images/mj/rate_16_9.png"
|
||||
img: "/images/mj/rate_16_9.png",
|
||||
},
|
||||
{
|
||||
css: "size9-16",
|
||||
value: "9:16",
|
||||
text: "9:16",
|
||||
img: "/images/mj/rate_9_16.png"
|
||||
}
|
||||
img: "/images/mj/rate_9_16.png",
|
||||
},
|
||||
];
|
||||
const models = [
|
||||
{ text: "写实模式MJ-6.1", value: " --v 6.1", img: "/images/mj/mj-v6.png" },
|
||||
@@ -950,33 +715,33 @@ const models = [
|
||||
{
|
||||
text: "动漫风-niji5 可爱",
|
||||
value: " --niji 5 --style cute",
|
||||
img: "/images/mj/nj1.jpg"
|
||||
img: "/images/mj/nj1.jpg",
|
||||
},
|
||||
{
|
||||
text: "动漫风-niji5 风景",
|
||||
value: " --niji 5 --style scenic",
|
||||
img: "/images/mj/nj2.jpg"
|
||||
img: "/images/mj/nj2.jpg",
|
||||
},
|
||||
{ text: "动漫风-niji6", value: " --niji 6", img: "/images/mj/nj3.jpg" }
|
||||
{ text: "动漫风-niji6", value: " --niji 6", img: "/images/mj/nj3.jpg" },
|
||||
];
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: 0,
|
||||
label: "默认"
|
||||
label: "默认",
|
||||
},
|
||||
{
|
||||
value: 0.25,
|
||||
label: "普通"
|
||||
label: "普通",
|
||||
},
|
||||
{
|
||||
value: 0.5,
|
||||
label: "清晰"
|
||||
label: "清晰",
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: "高清"
|
||||
}
|
||||
label: "高清",
|
||||
},
|
||||
];
|
||||
|
||||
const router = useRouter();
|
||||
@@ -997,7 +762,7 @@ const initParams = {
|
||||
quality: 0,
|
||||
cref: "",
|
||||
sref: "",
|
||||
cw: 0
|
||||
cw: 0,
|
||||
};
|
||||
const params = ref(copyObj(initParams));
|
||||
|
||||
@@ -1086,7 +851,7 @@ const fetchRunningJobs = () => {
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `任务ID:${jobs[i]["task_id"]}<br />原因:${jobs[i]["err_msg"]}`,
|
||||
type: "error",
|
||||
duration: 0
|
||||
duration: 0,
|
||||
});
|
||||
if (jobs[i].type === "image") {
|
||||
power.value += mjPower.value;
|
||||
@@ -1114,19 +879,15 @@ const fetchFinishJobs = () => {
|
||||
loading.value = true;
|
||||
page.value = page.value + 1;
|
||||
// 获取已完成的任务
|
||||
httpGet(
|
||||
`/api/mj/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`
|
||||
)
|
||||
httpGet(`/api/mj/jobs?finish=true&page=${page.value}&page_size=${pageSize.value}`)
|
||||
.then((res) => {
|
||||
const jobs = res.data.items;
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
if (jobs[i]["img_url"] !== "") {
|
||||
if (jobs[i].type === "upscale" || jobs[i].type === "swapFace") {
|
||||
jobs[i]["thumb_url"] =
|
||||
jobs[i]["img_url"] + "?imageView2/1/w/480/h/600/q/75";
|
||||
jobs[i]["thumb_url"] = jobs[i]["img_url"] + "?imageView2/1/w/480/h/600/q/75";
|
||||
} else {
|
||||
jobs[i]["thumb_url"] =
|
||||
jobs[i]["img_url"] + "?imageView2/1/w/480/h/480/q/75";
|
||||
jobs[i]["thumb_url"] = jobs[i]["img_url"] + "?imageView2/1/w/480/h/480/q/75";
|
||||
}
|
||||
} else {
|
||||
jobs[i]["thumb_url"] = "/images/img-placeholder.jpg";
|
||||
@@ -1196,7 +957,7 @@ const uploadImg = (file) => {
|
||||
},
|
||||
error(err) {
|
||||
console.log(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1249,7 +1010,7 @@ const send = (url, index, item) => {
|
||||
message_id: item.message_id,
|
||||
message_hash: item.hash,
|
||||
session_id: getSessionId(),
|
||||
prompt: item.prompt
|
||||
prompt: item.prompt,
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("任务推送成功,请耐心等待任务执行...");
|
||||
@@ -1265,7 +1026,7 @@ const removeImage = (item) => {
|
||||
ElMessageBox.confirm("此操作将会删除任务和图片,继续操作码?", "删除提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/mj/remove", { id: item.id, user_id: item.user_id })
|
||||
@@ -1291,7 +1052,7 @@ const publishImage = (item, action) => {
|
||||
httpGet("/api/mj/publish", {
|
||||
id: item.id,
|
||||
action: action,
|
||||
user_id: item.user_id
|
||||
user_id: item.user_id,
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success(text + "成功");
|
||||
|
@@ -11,19 +11,10 @@
|
||||
<el-form-item label="采样方法">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select v-model="params.sampler" style="width: 176px">
|
||||
<el-option
|
||||
v-for="item in samplers"
|
||||
:label="item"
|
||||
:value="item"
|
||||
:key="item"
|
||||
/>
|
||||
<el-select v-model="params.sampler" style="width: 150px">
|
||||
<el-option v-for="item in samplers" :label="item" :value="item" :key="item" />
|
||||
</el-select>
|
||||
<el-tooltip
|
||||
content="出图效果比较好的一般是 Euler 和 DPM 系列算法"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="出图效果比较好的一般是 Euler 和 DPM 系列算法" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -37,22 +28,10 @@
|
||||
<el-form-item label="采样调度">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select
|
||||
v-model="params.scheduler"
|
||||
style="width: 176px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in schedulers"
|
||||
:label="item"
|
||||
:value="item"
|
||||
:key="item"
|
||||
/>
|
||||
<el-select v-model="params.scheduler" style="width: 150px">
|
||||
<el-option v-for="item in schedulers" :label="item" :value="item" :key="item" />
|
||||
</el-select>
|
||||
<el-tooltip
|
||||
content="推荐自动或者 Karras"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="推荐自动或者 Karras" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -68,16 +47,10 @@
|
||||
<div class="form-item-inner">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
v-model.number="params.width"
|
||||
placeholder="图片宽度"
|
||||
/>
|
||||
<el-input v-model.number="params.width" placeholder="图片宽度" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
v-model.number="params.height"
|
||||
placeholder="图片高度"
|
||||
/>
|
||||
<el-input v-model.number="params.height" placeholder="图片高度" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -90,11 +63,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.steps" />
|
||||
<el-tooltip
|
||||
content="值越大则代表细节越多,同时也意味着出图速度越慢"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="值越大则代表细节越多,同时也意味着出图速度越慢" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -109,11 +78,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.cfg_scale" />
|
||||
<el-tooltip
|
||||
content="提示词引导系数,图像在多大程度上服从提示词<br/> 较低值会产生更有创意的结果"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="提示词引导系数,图像在多大程度上服从提示词<br/> 较低值会产生更有创意的结果" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -128,21 +93,13 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.seed" />
|
||||
<el-tooltip
|
||||
content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
content="使用随机数"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="使用随机数" raw-content placement="right">
|
||||
<el-icon @click="params.seed = -1" class="info-icon">
|
||||
<Orange />
|
||||
</el-icon>
|
||||
@@ -156,16 +113,8 @@
|
||||
<el-form-item label="高清修复">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-switch
|
||||
v-model="params.hd_fix"
|
||||
style="--el-switch-on-color: #47fff1"
|
||||
size="large"
|
||||
/>
|
||||
<el-tooltip
|
||||
content="先以较小的分辨率生成图像,接着方法图像<br />然后在不更改构图的情况下再修改细节"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-switch v-model="params.hd_fix" style="--el-switch-on-color: #47fff1" size="large" />
|
||||
<el-tooltip content="先以较小的分辨率生成图像,接着方法图像<br />然后在不更改构图的情况下再修改细节" raw-content placement="right">
|
||||
<el-icon style="margin-left: 10px; top: 12px">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -180,20 +129,8 @@
|
||||
<el-form-item label="重绘幅度">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-slider
|
||||
v-model.number="params.hd_redraw_rate"
|
||||
:max="1"
|
||||
:step="0.1"
|
||||
style="
|
||||
width: 180px;
|
||||
--el-slider-main-bg-color: #47fff1;
|
||||
"
|
||||
/>
|
||||
<el-tooltip
|
||||
content="决定算法对图像内容的影响程度<br />较大的值将得到越有创意的图像"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-slider v-model.number="params.hd_redraw_rate" :max="1" :step="0.1" style="width: 180px; --el-slider-main-bg-color: #47fff1" />
|
||||
<el-tooltip content="决定算法对图像内容的影响程度<br />较大的值将得到越有创意的图像" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -207,22 +144,10 @@
|
||||
<el-form-item label="放大算法">
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-select
|
||||
v-model="params.hd_scale_alg"
|
||||
style="width: 176px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in scaleAlg"
|
||||
:label="item"
|
||||
:value="item"
|
||||
:key="item"
|
||||
/>
|
||||
<el-select v-model="params.hd_scale_alg" style="width: 176px">
|
||||
<el-option v-for="item in scaleAlg" :label="item" :value="item" :key="item" />
|
||||
</el-select>
|
||||
<el-tooltip
|
||||
content="高清修复放大算法,主流算法有Latent和ESRGAN_4x"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="高清修复放大算法,主流算法有Latent和ESRGAN_4x" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -237,11 +162,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.hd_scale" />
|
||||
<el-tooltip
|
||||
content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="随机数种子,相同的种子会得到相同的结果<br/> 设置为 -1 则每次随机生成种子" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -256,11 +177,7 @@
|
||||
<template #default>
|
||||
<div class="form-item-inner">
|
||||
<el-input v-model.number="params.hd_steps" />
|
||||
<el-tooltip
|
||||
content="重绘迭代步数,如果设置为0,则设置跟原图相同的迭代步数"
|
||||
raw-content
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="重绘迭代步数,如果设置为0,则设置跟原图相同的迭代步数" raw-content placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -284,77 +201,48 @@
|
||||
</div>
|
||||
|
||||
<el-row class="text-info">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
size="small"
|
||||
@click="generatePrompt"
|
||||
color="#5865f2"
|
||||
:disabled="isGenerating"
|
||||
>
|
||||
<i
|
||||
class="iconfont icon-chuangzuo"
|
||||
style="margin-right: 5px"
|
||||
></i>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
|
||||
<span>生成专业绘画指令</span>
|
||||
</el-button>
|
||||
</el-row>
|
||||
|
||||
<div class="param-line pt">
|
||||
<span>反向提示词:</span>
|
||||
<el-tooltip
|
||||
content="不希望出现的元素,下面给了默认的起手式"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip content="不希望出现的元素,下面给了默认的起手式" placement="right">
|
||||
<el-icon class="info-icon">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="param-line">
|
||||
<el-input
|
||||
v-model="params.neg_prompt"
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
type="textarea"
|
||||
placeholder="反向提示词"
|
||||
/>
|
||||
<el-input v-model="params.neg_prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea" placeholder="反向提示词" />
|
||||
</div>
|
||||
|
||||
<div class="text-info">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-text type="primary"
|
||||
>单次绘图消耗
|
||||
<el-text type="warning">{{ sdPower }}算力;</el-text>
|
||||
</el-text>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-text type="primary"
|
||||
>当前可用
|
||||
<el-text type="warning">
|
||||
{{ power }}算力</el-text
|
||||
></el-text
|
||||
>
|
||||
</el-col>
|
||||
<el-text type="primary"
|
||||
>单次绘图消耗
|
||||
<el-text type="warning">{{ sdPower }}算力,</el-text>
|
||||
</el-text>
|
||||
<el-text type="primary"
|
||||
>当前可用 <el-text type="warning"> {{ power }}算力</el-text></el-text
|
||||
>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="submit-btn">
|
||||
<el-button type="primary" :dark="false" round @click="generate"
|
||||
>立即生成</el-button
|
||||
>
|
||||
<el-button type="primary" :dark="false" round @click="generate">立即生成</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-list-box">
|
||||
<div
|
||||
class="task-list-inner"
|
||||
:style="{ height: listBoxHeight + 'px' }"
|
||||
>
|
||||
<div class="task-list-box pl-6 pr-6 pb-4 pt-4">
|
||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||
<div class="job-list-box">
|
||||
<h2>任务列表</h2>
|
||||
<h2 class="text-xl">任务列表</h2>
|
||||
<task-list :list="runningJobs" />
|
||||
<template v-if="finishedJobs.length > 0">
|
||||
<h2>创作记录</h2>
|
||||
<h2 class="text-xl">创作记录</h2>
|
||||
<div class="finish-job-list">
|
||||
<div v-if="finishedJobs.length > 0">
|
||||
<v3-waterfall
|
||||
@@ -377,55 +265,25 @@
|
||||
<div class="err-msg-container">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<el-popover
|
||||
title="错误详情"
|
||||
trigger="click"
|
||||
:width="250"
|
||||
:content="slotProp.item['err_msg']"
|
||||
placement="top"
|
||||
>
|
||||
<el-popover title="错误详情" trigger="click" :width="250" :content="slotProp.item['err_msg']" placement="top">
|
||||
<template #reference>
|
||||
<el-button type="info">详情</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click="removeImage(slotProp.item)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button type="danger" @click="removeImage(slotProp.item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div v-else>
|
||||
<el-image
|
||||
:src="slotProp.item['img_thumb']"
|
||||
@click="showTask(slotProp.item)"
|
||||
fit="cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
<el-image :src="slotProp.item['img_thumb']" @click="showTask(slotProp.item)" fit="cover" loading="lazy" />
|
||||
<div class="remove">
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="removeImage(slotProp.item)"
|
||||
circle
|
||||
/>
|
||||
<el-button
|
||||
type="warning"
|
||||
v-if="slotProp.item.publish"
|
||||
@click="publishImage(slotProp.item, false)"
|
||||
circle
|
||||
>
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(slotProp.item)" circle />
|
||||
<el-button type="warning" v-if="slotProp.item.publish" @click="publishImage(slotProp.item, false)" circle>
|
||||
<i class="iconfont icon-cancel-share"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
v-else
|
||||
@click="publishImage(slotProp.item, true)"
|
||||
circle
|
||||
>
|
||||
<el-button type="success" v-else @click="publishImage(slotProp.item, true)" circle>
|
||||
<i class="iconfont icon-share-bold"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -441,12 +299,7 @@
|
||||
</template>
|
||||
</v3-waterfall>
|
||||
</div>
|
||||
<el-empty
|
||||
:image-size="100"
|
||||
v-else
|
||||
:image="nodata"
|
||||
description="暂无记录"
|
||||
/>
|
||||
<el-empty :image-size="100" v-else :image="nodata" description="暂无记录" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -459,17 +312,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 任务详情弹框 -->
|
||||
<el-dialog
|
||||
v-model="showTaskDialog"
|
||||
title="绘画任务详情"
|
||||
:fullscreen="true"
|
||||
>
|
||||
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div
|
||||
class="img-container"
|
||||
:style="{ maxHeight: fullImgHeight + 'px' }"
|
||||
>
|
||||
<div class="img-container" :style="{ maxHeight: fullImgHeight + 'px' }">
|
||||
<el-image :src="item['img_url']" fit="contain" />
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -479,10 +325,7 @@
|
||||
<el-divider> 正向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.prompt }}</span>
|
||||
<el-icon
|
||||
class="copy-prompt-sd"
|
||||
:data-clipboard-text="item.prompt"
|
||||
>
|
||||
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -492,10 +335,7 @@
|
||||
<el-divider> 反向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.params.neg_prompt }}</span>
|
||||
<el-icon
|
||||
class="copy-prompt-sd"
|
||||
:data-clipboard-text="item.params.neg_prompt"
|
||||
>
|
||||
<el-icon class="copy-prompt-sd" :data-clipboard-text="item.params.neg_prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -511,9 +351,7 @@
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>图片尺寸:</label>
|
||||
<div class="item-value">
|
||||
{{ item.params.width }} x {{ item.params.height }}
|
||||
</div>
|
||||
<div class="item-value">{{ item.params.width }} x {{ item.params.height }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -572,9 +410,7 @@
|
||||
</div>
|
||||
|
||||
<div class="copy-params">
|
||||
<el-button type="primary" round @click="copyParams(item)"
|
||||
>画一张同款的</el-button
|
||||
>
|
||||
<el-button type="primary" round @click="copyParams(item)">画一张同款的</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -586,12 +422,7 @@
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, onUnmounted, ref } from "vue";
|
||||
import {
|
||||
Delete,
|
||||
DocumentCopy,
|
||||
InfoFilled,
|
||||
Orange
|
||||
} from "@element-plus/icons-vue";
|
||||
import { Delete, DocumentCopy, InfoFilled, Orange } from "@element-plus/icons-vue";
|
||||
import nodata from "@/assets/img/no-data.png";
|
||||
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
@@ -623,15 +454,7 @@ resizeElement();
|
||||
window.onresize = () => {
|
||||
resizeElement();
|
||||
};
|
||||
const samplers = [
|
||||
"Euler a",
|
||||
"DPM++ 2S a",
|
||||
"DPM++ 2M",
|
||||
"DPM++ SDE",
|
||||
"DPM++ 2M SDE",
|
||||
"UniPC",
|
||||
"Restart"
|
||||
];
|
||||
const samplers = ["Euler a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SDE", "UniPC", "Restart"];
|
||||
const schedulers = ["Automatic", "Karras", "Exponential", "Uniform"];
|
||||
const scaleAlg = ["Latent", "ESRGAN_4x", "R-ESRGAN 4x+", "SwinIR_4x", "LDSR"];
|
||||
const params = ref({
|
||||
@@ -649,8 +472,7 @@ const params = ref({
|
||||
hd_scale_alg: scaleAlg[0],
|
||||
hd_steps: 0,
|
||||
prompt: "",
|
||||
neg_prompt:
|
||||
"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet"
|
||||
neg_prompt: "nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet",
|
||||
});
|
||||
|
||||
const runningJobs = ref([]);
|
||||
@@ -745,17 +567,14 @@ const fetchFinishJobs = () => {
|
||||
loading.value = true;
|
||||
page.value = page.value + 1;
|
||||
|
||||
httpGet(
|
||||
`/api/sd/jobs?finish=1&page=${page.value}&page_size=${pageSize.value}`
|
||||
)
|
||||
httpGet(`/api/sd/jobs?finish=1&page=${page.value}&page_size=${pageSize.value}`)
|
||||
.then((res) => {
|
||||
if (res.data.items.length < pageSize.value) {
|
||||
isOver.value = true;
|
||||
}
|
||||
const imageList = res.data.items;
|
||||
for (let i = 0; i < imageList.length; i++) {
|
||||
imageList[i]["img_thumb"] =
|
||||
imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
}
|
||||
if (page.value === 1) {
|
||||
finishedJobs.value = imageList;
|
||||
@@ -812,7 +631,7 @@ const removeImage = (item) => {
|
||||
ElMessageBox.confirm("此操作将会删除任务和图片,继续操作码?", "删除提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/sd/remove", { id: item.id })
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="page-images-wall">
|
||||
<div class="inner custom-scroll">
|
||||
<div class="header">
|
||||
<h2>AI 绘画作品墙</h2>
|
||||
<h2 class="text-xl pt-4 pb-4">AI 绘画作品墙</h2>
|
||||
<div class="settings">
|
||||
<el-radio-group v-model="imgType" @change="changeImgType">
|
||||
<el-radio value="mj" size="large">MidJourney</el-radio>
|
||||
@@ -11,11 +11,7 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="waterfall"
|
||||
:style="{ height: listBoxHeight + 'px' }"
|
||||
id="waterfall-box"
|
||||
>
|
||||
<div class="waterfall" :style="{ height: listBoxHeight + 'px' }" id="waterfall-box">
|
||||
<v3-waterfall
|
||||
v-if="imgType === 'mj'"
|
||||
id="waterfall"
|
||||
@@ -54,24 +50,14 @@
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="opt">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
content="复制提示词"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon
|
||||
class="copy-prompt-wall"
|
||||
:data-clipboard-text="slotProp.item.prompt"
|
||||
>
|
||||
<el-tooltip class="box-item" content="复制提示词" placement="top">
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="slotProp.item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="box-item" content="画同款" placement="top">
|
||||
<i
|
||||
class="iconfont icon-palette-pen"
|
||||
@click="drawSameMj(slotProp.item)"
|
||||
></i>
|
||||
<i class="iconfont icon-palette-pen" @click="drawSameMj(slotProp.item)"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,15 +102,8 @@
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="opt">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
content="复制提示词"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon
|
||||
class="copy-prompt-wall"
|
||||
:data-clipboard-text="slotProp.item.prompt"
|
||||
>
|
||||
<el-tooltip class="box-item" content="复制提示词" placement="top">
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="slotProp.item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
@@ -149,11 +128,7 @@
|
||||
<template #default="slotProp">
|
||||
<div class="list-item">
|
||||
<div class="image">
|
||||
<el-image
|
||||
:src="slotProp.item['img_thumb']"
|
||||
loading="lazy"
|
||||
@click="showTask(slotProp.item)"
|
||||
>
|
||||
<el-image :src="slotProp.item['img_thumb']" loading="lazy" @click="showTask(slotProp.item)">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">正在加载图片</div>
|
||||
</template>
|
||||
@@ -189,10 +164,7 @@
|
||||
<el-dialog v-model="showTaskDialog" title="绘画任务详情" :fullscreen="true">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="16">
|
||||
<div
|
||||
class="img-container"
|
||||
:style="{ maxHeight: fullImgHeight + 'px' }"
|
||||
>
|
||||
<div class="img-container" :style="{ maxHeight: fullImgHeight + 'px' }">
|
||||
<el-image :src="item['img_url']" fit="contain">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">正在加载图片</div>
|
||||
@@ -214,10 +186,7 @@
|
||||
<el-divider> 正向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.prompt }}</span>
|
||||
<el-icon
|
||||
class="copy-prompt-wall"
|
||||
:data-clipboard-text="item.prompt"
|
||||
>
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="item.prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -227,10 +196,7 @@
|
||||
<el-divider> 反向提示词 </el-divider>
|
||||
<div class="prompt">
|
||||
<span>{{ item.params.negative_prompt }}</span>
|
||||
<el-icon
|
||||
class="copy-prompt-wall"
|
||||
:data-clipboard-text="item.params.negative_prompt"
|
||||
>
|
||||
<el-icon class="copy-prompt-wall" :data-clipboard-text="item.params.negative_prompt">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -246,9 +212,7 @@
|
||||
<div class="info-line">
|
||||
<div class="wrapper">
|
||||
<label>图片尺寸:</label>
|
||||
<div class="item-value">
|
||||
{{ item.params.width }} x {{ item.params.height }}
|
||||
</div>
|
||||
<div class="item-value">{{ item.params.width }} x {{ item.params.height }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -305,9 +269,7 @@
|
||||
</div>
|
||||
|
||||
<div class="copy-params">
|
||||
<el-button type="primary" round @click="drawSameSd(item)"
|
||||
>画一张同款的</el-button
|
||||
>
|
||||
<el-button type="primary" round @click="drawSameSd(item)">画一张同款的</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -329,7 +291,7 @@ import BackTop from "@/components/BackTop.vue";
|
||||
const data = ref({
|
||||
mj: [],
|
||||
sd: [],
|
||||
dall: []
|
||||
dall: [],
|
||||
});
|
||||
const loading = ref(true);
|
||||
const isOver = ref(false);
|
||||
@@ -384,8 +346,7 @@ const getNext = () => {
|
||||
// 生成缩略图
|
||||
const imageList = res.data.items;
|
||||
for (let i = 0; i < imageList.length; i++) {
|
||||
imageList[i]["img_thumb"] =
|
||||
imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
imageList[i]["img_thumb"] = imageList[i]["img_url"] + "?imageView2/4/w/300/h/0/q/75";
|
||||
}
|
||||
if (data.value[imgType.value].length === 0) {
|
||||
data.value[imgType.value] = imageList;
|
||||
@@ -427,7 +388,7 @@ const changeImgType = () => {
|
||||
data.value = {
|
||||
mj: [],
|
||||
sd: [],
|
||||
dall: []
|
||||
dall: [],
|
||||
};
|
||||
loading.value = true;
|
||||
isOver.value = false;
|
||||
@@ -443,7 +404,7 @@ const router = useRouter();
|
||||
const drawSameSd = (row) => {
|
||||
router.push({
|
||||
name: "image-sd",
|
||||
params: { copyParams: JSON.stringify(row.params) }
|
||||
params: { copyParams: JSON.stringify(row.params) },
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -12,22 +12,12 @@
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<span v-if="!license.de_copy">
|
||||
<el-tooltip
|
||||
v-if="!license.de_copy"
|
||||
class="box-item"
|
||||
content="部署文档"
|
||||
placement="bottom"
|
||||
>
|
||||
<a :href="docsURL" class="link-button" target="_blank">
|
||||
<el-tooltip v-if="!license.de_copy" class="box-item" content="部署文档" placement="bottom">
|
||||
<a :href="docsURL" class="link-button mr-2" target="_blank">
|
||||
<i class="iconfont icon-book"></i>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
v-if="!license.de_copy"
|
||||
class="box-item"
|
||||
content="项目源码"
|
||||
placement="bottom"
|
||||
>
|
||||
<el-tooltip v-if="!license.de_copy" class="box-item" content="项目源码" placement="bottom">
|
||||
<a :href="gitURL" class="link-button" target="_blank">
|
||||
<i class="iconfont icon-github"></i>
|
||||
</a>
|
||||
@@ -41,38 +31,25 @@
|
||||
<el-button @click="router.push('/register')" class="shadow" round
|
||||
>注册</el-button
|
||||
> -->
|
||||
<el-button
|
||||
@click="router.push('/login')"
|
||||
class="btn-go animate__animated animate__pulse animate__infinite"
|
||||
round
|
||||
>登录/注册</el-button
|
||||
>
|
||||
<el-button @click="router.push('/login')" class="btn-go animate__animated animate__pulse animate__infinite" round>登录/注册</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</el-menu>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div style="height: 158px"></div>
|
||||
<h1 class="animate__animated animate__backInDown">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<div class="msg-text cursor-ani">
|
||||
<span
|
||||
v-for="(char, index) in displayedChars"
|
||||
:key="index"
|
||||
:style="{ color: rainbowColor(index) }"
|
||||
>
|
||||
<span v-for="(char, index) in displayedChars" :key="index" :style="{ color: rainbowColor(index) }">
|
||||
{{ char }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="navs animate__animated animate__backInDown">
|
||||
<el-space wrap :size="14">
|
||||
<div
|
||||
v-for="item in navs"
|
||||
:key="item.url"
|
||||
class="nav-item-box"
|
||||
@click="router.push(item.url)"
|
||||
>
|
||||
<div v-for="item in navs" :key="item.url" class="nav-item-box" @click="router.push(item.url)">
|
||||
<i :class="'iconfont ' + iconMap[item.url]"></i>
|
||||
<div>{{ item.name }}</div>
|
||||
</div>
|
||||
@@ -121,7 +98,7 @@ const iconMap = ref({
|
||||
"/apps": "icon-app",
|
||||
"/member": "icon-vip-user",
|
||||
"/invite": "icon-share",
|
||||
"/luma": "icon-luma"
|
||||
"/luma": "icon-luma",
|
||||
});
|
||||
|
||||
const displayedChars = ref([]);
|
||||
@@ -194,6 +171,5 @@ const rainbowColor = (index) => {
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
@import '@/assets/iconfont/iconfont.css'
|
||||
@import "@/assets/css/index.styl"
|
||||
</style>
|
||||
|
@@ -1,49 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="member custom-scroll"
|
||||
v-loading="loading"
|
||||
element-loading-background="rgba(255,255,255,.3)"
|
||||
:element-loading-text="loadingText"
|
||||
>
|
||||
<div class="member custom-scroll" v-loading="loading" element-loading-background="rgba(255,255,255,.3)" :element-loading-text="loadingText">
|
||||
<div class="inner">
|
||||
<div class="user-profile">
|
||||
<user-profile :key="profileKey" />
|
||||
|
||||
<el-row class="user-opt" :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showBindEmailDialog = true"
|
||||
>绑定邮箱</el-button
|
||||
>
|
||||
<el-button type="primary" @click="showBindEmailDialog = true">绑定邮箱</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showBindMobileDialog = true"
|
||||
>绑定手机</el-button
|
||||
>
|
||||
<el-button type="primary" @click="showBindMobileDialog = true">绑定手机</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showThirdLoginDialog = true"
|
||||
>第三方登录</el-button
|
||||
>
|
||||
<el-button type="primary" @click="showThirdLoginDialog = true">第三方登录</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button type="primary" @click="showPasswordDialog = true"
|
||||
>修改密码</el-button
|
||||
>
|
||||
<el-button type="primary" @click="showPasswordDialog = true">修改密码</el-button>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-button type="primary" @click="showRedeemVerifyDialog = true"
|
||||
>卡密兑换
|
||||
</el-button>
|
||||
<el-button type="primary" @click="showRedeemVerifyDialog = true">卡密兑换 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div class="product-box">
|
||||
<div class="info" v-if="orderPayInfoText !== ''">
|
||||
<el-alert type="success" show-icon :closable="false" effect="dark">
|
||||
<strong>说明:</strong> {{ vipInfoText }}
|
||||
</el-alert>
|
||||
<el-alert type="success" show-icon :closable="false" effect="dark"> <strong>说明:</strong> {{ vipInfoText }} </el-alert>
|
||||
</div>
|
||||
|
||||
<el-row v-if="list.length > 0" :gutter="20" class="list-box">
|
||||
@@ -68,9 +51,7 @@
|
||||
</div>
|
||||
<div class="info-line">
|
||||
<span class="label">有效期:</span>
|
||||
<span class="expire" v-if="item.days > 0"
|
||||
>{{ item.days }}天</span
|
||||
>
|
||||
<span class="expire" v-if="item.days > 0">{{ item.days }}天</span>
|
||||
<span class="expire" v-else>长期有效</span>
|
||||
</div>
|
||||
|
||||
@@ -80,41 +61,20 @@
|
||||
</div>
|
||||
|
||||
<div class="pay-way">
|
||||
<span
|
||||
type="primary"
|
||||
v-for="payWay in payWays"
|
||||
@click="pay(item, payWay)"
|
||||
:key="payWay"
|
||||
>
|
||||
<el-button
|
||||
v-if="payWay.pay_type === 'alipay'"
|
||||
color="#15A6E8"
|
||||
circle
|
||||
>
|
||||
<span type="primary" v-for="payWay in payWays" @click="pay(item, payWay)" :key="payWay">
|
||||
<el-button v-if="payWay.pay_type === 'alipay'" color="#15A6E8" circle>
|
||||
<i class="iconfont icon-alipay"></i>
|
||||
</el-button>
|
||||
<el-button v-else-if="payWay.pay_type === 'qqpay'" circle>
|
||||
<i class="iconfont icon-qq"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="payWay.pay_type === 'paypal'"
|
||||
class="paypal"
|
||||
round
|
||||
>
|
||||
<el-button v-else-if="payWay.pay_type === 'paypal'" class="paypal" round>
|
||||
<i class="iconfont icon-paypal"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="payWay.pay_type === 'jdpay'"
|
||||
color="#E1251B"
|
||||
circle
|
||||
>
|
||||
<el-button v-else-if="payWay.pay_type === 'jdpay'" color="#E1251B" circle>
|
||||
<i class="iconfont icon-jd-pay"></i>
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="payWay.pay_type === 'douyin'"
|
||||
class="douyin"
|
||||
circle
|
||||
>
|
||||
<el-button v-else-if="payWay.pay_type === 'douyin'" class="douyin" circle>
|
||||
<i class="iconfont icon-douyin"></i>
|
||||
</el-button>
|
||||
<el-button v-else circle class="wechat" color="#67C23A">
|
||||
@@ -136,41 +96,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<password-dialog
|
||||
v-if="isLogin"
|
||||
:show="showPasswordDialog"
|
||||
@hide="showPasswordDialog = false"
|
||||
/>
|
||||
<bind-mobile
|
||||
v-if="isLogin"
|
||||
:show="showBindMobileDialog"
|
||||
@hide="showBindMobileDialog = false"
|
||||
/>
|
||||
<bind-email
|
||||
v-if="isLogin"
|
||||
:show="showBindEmailDialog"
|
||||
@hide="showBindEmailDialog = false"
|
||||
/>
|
||||
<third-login
|
||||
v-if="isLogin"
|
||||
:show="showThirdLoginDialog"
|
||||
@hide="showThirdLoginDialog = false"
|
||||
/>
|
||||
<redeem-verify
|
||||
v-if="isLogin"
|
||||
:show="showRedeemVerifyDialog"
|
||||
@hide="redeemCallback"
|
||||
/>
|
||||
<password-dialog v-if="isLogin" :show="showPasswordDialog" @hide="showPasswordDialog = false" />
|
||||
<bind-mobile v-if="isLogin" :show="showBindMobileDialog" @hide="showBindMobileDialog = false" />
|
||||
<bind-email v-if="isLogin" :show="showBindEmailDialog" @hide="showBindEmailDialog = false" />
|
||||
<third-login v-if="isLogin" :show="showThirdLoginDialog" @hide="showThirdLoginDialog = false" />
|
||||
<redeem-verify v-if="isLogin" :show="showRedeemVerifyDialog" @hide="redeemCallback" />
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
hide-footer
|
||||
width="auto"
|
||||
class="pay-dialog"
|
||||
>
|
||||
<el-dialog v-model="showDialog" :show-close="false" :close-on-click-modal="false" hide-footer width="auto" class="pay-dialog">
|
||||
<div v-if="qrImg !== ''">
|
||||
<div class="product-info">
|
||||
请使用微信扫码支付:<span class="price">¥{{ price }}</span>
|
||||
@@ -178,12 +111,8 @@
|
||||
<el-image :src="qrImg" fit="cover" />
|
||||
</div>
|
||||
<div style="padding-bottom: 10px; text-align: center">
|
||||
<el-button type="success" @click="payCallback(true)"
|
||||
>支付成功</el-button
|
||||
>
|
||||
<el-button type="danger" @click="payCallback(false)"
|
||||
>支付失败</el-button
|
||||
>
|
||||
<el-button type="success" @click="payCallback(true)">支付成功</el-button>
|
||||
<el-button type="danger" @click="payCallback(false)">支付失败</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -289,24 +218,20 @@ const pay = (product, payWay) => {
|
||||
pay_type: payWay.pay_type,
|
||||
user_id: user.value.id,
|
||||
host: host,
|
||||
device: "jump"
|
||||
device: "jump",
|
||||
})
|
||||
.then((res) => {
|
||||
showDialog.value = true;
|
||||
loading.value = false;
|
||||
if (payWay.pay_way === "wechat") {
|
||||
price.value = Number(product.discount);
|
||||
QRCode.toDataURL(
|
||||
res.data,
|
||||
{ width: 300, height: 300, margin: 2 },
|
||||
(error, url) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
qrImg.value = url;
|
||||
}
|
||||
QRCode.toDataURL(res.data, { width: 300, height: 300, margin: 2 }, (error, url) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
qrImg.value = url;
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
window.open(res.data, "_blank");
|
||||
}
|
||||
|
@@ -4,12 +4,7 @@
|
||||
<div class="inner">
|
||||
<div class="list-box">
|
||||
<div class="handle-box">
|
||||
<el-input
|
||||
v-model="query.model"
|
||||
placeholder="模型"
|
||||
class="handle-input mr10"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-input v-model="query.model" placeholder="模型" class="handle-input mr10" clearable></el-input>
|
||||
<el-date-picker
|
||||
v-model="query.date"
|
||||
type="daterange"
|
||||
@@ -19,36 +14,23 @@
|
||||
value-format="YYYY-MM-DD"
|
||||
style="margin: 0 10px; width: 200px"
|
||||
/>
|
||||
<el-button type="primary" :icon="Search" @click="fetchData"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button type="primary" :icon="Search" @click="fetchData">搜索</el-button>
|
||||
</div>
|
||||
|
||||
<el-row v-if="items.length > 0">
|
||||
<el-table
|
||||
:data="items"
|
||||
:row-key="(row) => row.id"
|
||||
table-layout="auto"
|
||||
border
|
||||
>
|
||||
<el-table :data="items" :row-key="(row) => row.id" table-layout="auto" border>
|
||||
<el-table-column prop="username" label="用户" width="130px" />
|
||||
<el-table-column prop="model" label="模型" width="130px" />
|
||||
<el-table-column prop="type" label="类型">
|
||||
<template #default="scope">
|
||||
<el-tag size="small" :type="tagColors[scope.row.type]">{{
|
||||
scope.row.type_str
|
||||
}}</el-tag>
|
||||
<el-tag size="small" :type="tagColors[scope.row.type]">{{ scope.row.type_str }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数额">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-text type="success" v-if="scope.row.mark === 1"
|
||||
>+{{ scope.row.amount }}</el-text
|
||||
>
|
||||
<el-text type="danger" v-if="scope.row.mark === 0"
|
||||
>-{{ scope.row.amount }}</el-text
|
||||
>
|
||||
<el-text type="success" v-if="scope.row.mark === 1">+{{ scope.row.amount }}</el-text>
|
||||
<el-text type="danger" v-if="scope.row.mark === 0">-{{ scope.row.amount }}</el-text>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -75,12 +57,7 @@
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-empty
|
||||
:image-size="100"
|
||||
v-else
|
||||
:image="nodata"
|
||||
description="暂无数据"
|
||||
/>
|
||||
<el-empty :image-size="100" v-else :image="nodata" description="暂无数据" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,16 +82,9 @@ const loading = ref(false);
|
||||
const listBoxHeight = window.innerHeight - 87;
|
||||
const query = ref({
|
||||
model: "",
|
||||
date: []
|
||||
date: [],
|
||||
});
|
||||
const tagColors = ref([
|
||||
"primary",
|
||||
"success",
|
||||
"primary",
|
||||
"danger",
|
||||
"info",
|
||||
"warning"
|
||||
]);
|
||||
const tagColors = ref(["primary", "success", "primary", "danger", "info", "warning"]);
|
||||
|
||||
onMounted(() => {
|
||||
checkSession()
|
||||
@@ -139,7 +109,7 @@ const fetchData = () => {
|
||||
model: query.value.model,
|
||||
date: query.value.date,
|
||||
page: page.value,
|
||||
page_size: pageSize.value
|
||||
page_size: pageSize.value,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
@@ -173,7 +143,7 @@ const fetchData = () => {
|
||||
margin-top: 20px;
|
||||
border-radius: 10px;
|
||||
.handle-box {
|
||||
padding 20px 0
|
||||
padding 0 20px 20px 0
|
||||
|
||||
.el-input {
|
||||
max-width 150px
|
||||
|
@@ -6,48 +6,27 @@
|
||||
<el-tooltip content="定义模式" placement="top">
|
||||
<black-switch v-model:value="custom" size="large" />
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
content="请上传6-60秒的原始音频,检测到人声的音频将仅设为私人音频。"
|
||||
placement="bottom-end"
|
||||
>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadAudio"
|
||||
accept=".wav,.mp3"
|
||||
>
|
||||
<el-tooltip content="请上传6-60秒的原始音频,检测到人声的音频将仅设为私人音频。" placement="bottom-end">
|
||||
<el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false" :http-request="uploadAudio" accept=".wav,.mp3">
|
||||
<el-button class="upload-music" round type="primary">
|
||||
<i class="iconfont icon-upload"></i>
|
||||
<span>上传音乐</span>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-tooltip>
|
||||
<black-select
|
||||
v-model:value="data.model"
|
||||
:options="models"
|
||||
placeholder="请选择模型"
|
||||
style="width: 100px"
|
||||
/>
|
||||
<black-select v-model:value="data.model" :options="models" placeholder="请选择模型" style="width: 100px" />
|
||||
</div>
|
||||
|
||||
<div class="params">
|
||||
<div class="pure-music">
|
||||
<span class="switch"
|
||||
><black-switch v-model:value="data.instrumental" size="default"
|
||||
/></span>
|
||||
<span class="switch"><black-switch v-model:value="data.instrumental" size="default" /></span>
|
||||
<span class="text">纯音乐</span>
|
||||
</div>
|
||||
<div v-if="custom">
|
||||
<div class="item-group" v-if="!data.instrumental">
|
||||
<div class="label">
|
||||
<span class="text">歌词</span>
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="200"
|
||||
trigger="hover"
|
||||
content="自己写歌词或寻求 AI 的帮助。使用两节歌词(8 行)可获得最佳效果。"
|
||||
>
|
||||
<el-popover placement="right" :width="200" trigger="hover" content="自己写歌词或寻求 AI 的帮助。使用两节歌词(8 行)可获得最佳效果。">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
@@ -55,21 +34,9 @@
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div
|
||||
class="item"
|
||||
v-loading="isGenerating"
|
||||
element-loading-text="正在生成歌词..."
|
||||
element-loading-background="rgba(122, 122, 122, 0.8)"
|
||||
>
|
||||
<black-input
|
||||
v-model:value="data.lyrics"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
:placeholder="promptPlaceholder"
|
||||
/>
|
||||
<button class="btn btn-lyric" @click="createLyric">
|
||||
生成歌词
|
||||
</button>
|
||||
<div class="item" v-loading="isGenerating" element-loading-text="正在生成歌词..." element-loading-background="rgba(122, 122, 122, 0.8)">
|
||||
<black-input v-model:value="data.lyrics" type="textarea" :rows="10" :placeholder="promptPlaceholder" />
|
||||
<button class="btn btn-lyric" @click="createLyric">生成歌词</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -90,24 +57,12 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="item">
|
||||
<black-input
|
||||
v-model:value="data.tags"
|
||||
type="textarea"
|
||||
:maxlength="120"
|
||||
:rows="3"
|
||||
placeholder="请输入音乐风格,多个风格之间用英文逗号隔开..."
|
||||
/>
|
||||
<black-input v-model:value="data.tags" type="textarea" :maxlength="120" :rows="3" placeholder="请输入音乐风格,多个风格之间用英文逗号隔开..." />
|
||||
</div>
|
||||
|
||||
<div class="tag-select">
|
||||
<div class="inner">
|
||||
<span
|
||||
class="tag"
|
||||
@click="selectTag(tag)"
|
||||
v-for="tag in tags"
|
||||
:key="tag.value"
|
||||
>{{ tag.label }}</span
|
||||
>
|
||||
<span class="tag" @click="selectTag(tag)" v-for="tag in tags" :key="tag.value">{{ tag.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,12 +70,7 @@
|
||||
<div class="item-group">
|
||||
<div class="label">
|
||||
<span class="text">歌曲名称</span>
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="200"
|
||||
trigger="hover"
|
||||
content="给你的歌曲起一个标题,以便于分享、发现和组织。"
|
||||
>
|
||||
<el-popover placement="right" :width="200" trigger="hover" content="给你的歌曲起一个标题,以便于分享、发现和组织。">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
@@ -129,12 +79,7 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="item">
|
||||
<black-input
|
||||
v-model:value="data.title"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
placeholder="请输入歌曲名称..."
|
||||
/>
|
||||
<black-input v-model:value="data.title" type="textarea" :rows="1" placeholder="请输入歌曲名称..." />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -156,24 +101,14 @@
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="item">
|
||||
<black-input
|
||||
v-model:value="data.prompt"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="例如:一首关于爱情的摇滚歌曲..."
|
||||
/>
|
||||
<black-input v-model:value="data.prompt" type="textarea" :rows="10" placeholder="例如:一首关于爱情的摇滚歌曲..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ref-song" v-if="refSong">
|
||||
<div class="label">
|
||||
<span class="text">续写</span>
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="200"
|
||||
trigger="hover"
|
||||
content="输入额外的歌词,根据您之前的歌词来扩展歌曲。"
|
||||
>
|
||||
<el-popover placement="right" :width="200" trigger="hover" content="输入额外的歌词,根据您之前的歌词来扩展歌曲。">
|
||||
<template #reference>
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
@@ -186,17 +121,9 @@
|
||||
<div class="song">
|
||||
<el-image :src="refSong.cover_url" fit="cover" />
|
||||
<span class="title">{{ refSong.title }}</span>
|
||||
<el-button
|
||||
type="info"
|
||||
@click="removeRefSong"
|
||||
size="small"
|
||||
:icon="Delete"
|
||||
circle
|
||||
/>
|
||||
</div>
|
||||
<div class="extend-secs">
|
||||
从 <input v-model="refSong.extend_secs" type="text" /> 秒开始续写
|
||||
<el-button type="info" @click="removeRefSong" size="small" :icon="Delete" circle />
|
||||
</div>
|
||||
<div class="extend-secs">从 <input v-model="refSong.extend_secs" type="text" /> 秒开始续写</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -208,11 +135,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="right-box"
|
||||
v-loading="loading"
|
||||
element-loading-background="rgba(100,100,100,0.3)"
|
||||
>
|
||||
<div class="right-box" v-loading="loading" element-loading-background="rgba(100,100,100,0.3)">
|
||||
<div class="list-box" v-if="!noData">
|
||||
<div v-for="item in list" :key="item.id">
|
||||
<div class="item" v-if="item.progress === 100">
|
||||
@@ -227,12 +150,8 @@
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="title">
|
||||
<a :href="'/song/' + item.song_id" target="_blank">{{
|
||||
item.title
|
||||
}}</a>
|
||||
<span class="model" v-if="item.major_model_version">{{
|
||||
item.major_model_version
|
||||
}}</span>
|
||||
<a :href="'/song/' + item.song_id" target="_blank">{{ item.title }}</a>
|
||||
<span class="model" v-if="item.major_model_version">{{ item.major_model_version }}</span>
|
||||
<span class="model" v-if="item.type === 4">用户上传</span>
|
||||
<span class="model" v-if="item.type === 3">
|
||||
<i class="iconfont icon-mp3"></i>
|
||||
@@ -246,50 +165,31 @@
|
||||
<div class="tags" v-if="item.tags">{{ item.tags }}</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="tools">
|
||||
<div class="tools grid grid-flow-row auto-rows-max">
|
||||
<el-tooltip content="以当前歌曲为素材继续创作" placement="top">
|
||||
<button class="btn" @click="extend(item)">续写</button>
|
||||
</el-tooltip>
|
||||
|
||||
<button class="btn btn-publish">
|
||||
<span class="text">发布</span>
|
||||
<black-switch
|
||||
v-model:value="item.publish"
|
||||
@change="publishJob(item)"
|
||||
size="small"
|
||||
/>
|
||||
<black-switch v-model:value="item.publish" @change="publishJob(item)" size="small" />
|
||||
</button>
|
||||
|
||||
<el-tooltip content="下载歌曲" placement="top">
|
||||
<button class="btn btn-icon" @click="download(item)">
|
||||
<i
|
||||
class="iconfont icon-download"
|
||||
v-if="!item.downloading"
|
||||
></i>
|
||||
<el-image
|
||||
src="/images/loading.gif"
|
||||
class="downloading"
|
||||
fit="cover"
|
||||
v-else
|
||||
/>
|
||||
<i class="iconfont icon-download" v-if="!item.downloading"></i>
|
||||
<el-image src="/images/loading.gif" class="downloading" fit="cover" v-else />
|
||||
</button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip
|
||||
content="获取完整歌曲"
|
||||
placement="top"
|
||||
v-if="item.ref_song"
|
||||
>
|
||||
<el-tooltip content="获取完整歌曲" placement="top" v-if="item.ref_song">
|
||||
<button class="btn btn-icon" @click="merge(item)">
|
||||
<i class="iconfont icon-concat"></i>
|
||||
</button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip content="复制歌曲链接" placement="top">
|
||||
<button
|
||||
class="btn btn-icon copy-link"
|
||||
:data-clipboard-text="getShareURL(item)"
|
||||
>
|
||||
<button class="btn btn-icon copy-link" :data-clipboard-text="getShareURL(item)">
|
||||
<i class="iconfont icon-share1"></i>
|
||||
</button>
|
||||
</el-tooltip>
|
||||
@@ -329,12 +229,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty
|
||||
:image-size="100"
|
||||
:image="nodata"
|
||||
description="没有任何作品,赶紧去创作吧!"
|
||||
v-else
|
||||
/>
|
||||
<el-empty :image-size="100" :image="nodata" description="没有任何作品,赶紧去创作吧!" v-else />
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
@@ -351,37 +246,20 @@
|
||||
</div>
|
||||
|
||||
<div class="music-player" v-if="showPlayer">
|
||||
<music-player
|
||||
:songs="playList"
|
||||
ref="playerRef"
|
||||
:show-close="true"
|
||||
@close="showPlayer = false"
|
||||
/>
|
||||
<music-player :songs="playList" ref="playerRef" :show-close="true" @close="showPlayer = false" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<black-dialog
|
||||
v-model:show="showDialog"
|
||||
title="修改歌曲"
|
||||
@cancal="showDialog = false"
|
||||
@confirm="updateSong"
|
||||
:width="500 + 'px'"
|
||||
>
|
||||
<black-dialog v-model:show="showDialog" title="修改歌曲" @cancal="showDialog = false" @confirm="updateSong" :width="500 + 'px'">
|
||||
<form class="form">
|
||||
<div class="form-item">
|
||||
<div class="label">歌曲名称</div>
|
||||
<input class="input" v-model="editData.title" type="text" />
|
||||
<el-input v-model="editData.title" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<div class="label">封面图片</div>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadCover"
|
||||
accept=".png,.jpg,.jpeg,.bmp"
|
||||
>
|
||||
<el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false" :http-request="uploadCover" accept=".png,.jpg,.jpeg,.bmp">
|
||||
<el-avatar :src="editData.cover" shape="square" :size="100" />
|
||||
</el-upload>
|
||||
</div>
|
||||
@@ -393,23 +271,23 @@
|
||||
<script setup>
|
||||
import nodata from "@/assets/img/no-data.png";
|
||||
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {Delete, InfoFilled} from "@element-plus/icons-vue";
|
||||
import { nextTick, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import { Delete, InfoFilled } from "@element-plus/icons-vue";
|
||||
import BlackSelect from "@/components/ui/BlackSelect.vue";
|
||||
import BlackSwitch from "@/components/ui/BlackSwitch.vue";
|
||||
import BlackInput from "@/components/ui/BlackInput.vue";
|
||||
import MusicPlayer from "@/components/MusicPlayer.vue";
|
||||
import {compact} from "lodash";
|
||||
import {httpDownload, httpGet, httpPost} from "@/utils/http";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import {checkSession, getClientId} from "@/store/cache";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {formatTime, replaceImg} from "@/utils/libs";
|
||||
import { compact } from "lodash";
|
||||
import { httpDownload, httpGet, httpPost } from "@/utils/http";
|
||||
import { showMessageError, showMessageOK } from "@/utils/dialog";
|
||||
import { checkSession, getClientId } from "@/store/cache";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { formatTime, replaceImg } from "@/utils/libs";
|
||||
import Clipboard from "clipboard";
|
||||
import BlackDialog from "@/components/ui/BlackDialog.vue";
|
||||
import Compressor from "compressorjs";
|
||||
import Generating from "@/components/ui/Generating.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
|
||||
// const winHeight = ref(window.innerHeight - 50);
|
||||
const winHeight = ref(window.innerHeight - 20);
|
||||
@@ -417,8 +295,8 @@ const winHeight = ref(window.innerHeight - 20);
|
||||
const custom = ref(false);
|
||||
const models = ref([
|
||||
{ label: "v3.0", value: "chirp-v3-0" },
|
||||
{label: "v3.5", value: "chirp-v3-5"},
|
||||
{label: "v4.0", value: "chirp-v4"}
|
||||
{ label: "v3.5", value: "chirp-v3-5" },
|
||||
{ label: "v4.0", value: "chirp-v4" },
|
||||
]);
|
||||
const tags = ref([
|
||||
{ label: "女声", value: "female vocals" },
|
||||
@@ -436,7 +314,7 @@ const tags = ref([
|
||||
{ label: "钢琴", value: "piano" },
|
||||
{ label: "小提琴", value: "violin" },
|
||||
{ label: "贝斯", value: "bass" },
|
||||
{ label: "嘻哈", value: "hip hop" }
|
||||
{ label: "嘻哈", value: "hip hop" },
|
||||
]);
|
||||
const data = ref({
|
||||
client_id: getClientId(),
|
||||
@@ -448,7 +326,7 @@ const data = ref({
|
||||
instrumental: false,
|
||||
ref_task_id: "",
|
||||
extend_secs: 0,
|
||||
ref_song_id: ""
|
||||
ref_song_id: "",
|
||||
});
|
||||
const loading = ref(false);
|
||||
const noData = ref(true);
|
||||
@@ -605,7 +483,7 @@ const uploadAudio = (file) => {
|
||||
httpPost("/api/suno/create", {
|
||||
audio_url: res.data.url,
|
||||
title: res.data.name,
|
||||
type: 4
|
||||
type: 4,
|
||||
})
|
||||
.then(() => {
|
||||
fetchData(1);
|
||||
@@ -680,16 +558,14 @@ const selectTag = (tag) => {
|
||||
if (data.value.tags.length + tag.value.length >= 119) {
|
||||
return;
|
||||
}
|
||||
data.value.tags = compact([...data.value.tags.split(","), tag.value]).join(
|
||||
","
|
||||
);
|
||||
data.value.tags = compact([...data.value.tags.split(","), tag.value]).join(",");
|
||||
};
|
||||
|
||||
const removeJob = (item) => {
|
||||
ElMessageBox.confirm("此操作将会删除任务相关文件,继续操作码?", "删除提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/suno/remove", { id: item.id })
|
||||
@@ -737,7 +613,7 @@ const uploadCover = (file) => {
|
||||
},
|
||||
error(err) {
|
||||
console.log(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<el-button type="primary" :icon="Plus" @click="addRole">新增</el-button>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-table :data="tableData" :border="parentBorder" style="width: 100%">
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<div>
|
||||
@@ -28,34 +28,19 @@
|
||||
<el-table-column label="绑定模型" prop="model_name" />
|
||||
<el-table-column label="启用状态">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row['enable']"
|
||||
@change="roleSet('enable', scope.row)"
|
||||
/>
|
||||
<el-switch v-model="scope.row['enable']" @change="roleSet('enable', scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应用图标" prop="icon">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.icon"
|
||||
style="width: 45px; height: 45px; border-radius: 50%"
|
||||
/>
|
||||
<el-image :src="scope.row.icon" style="width: 45px; height: 45px; border-radius: 50%" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="打招呼信息" prop="hello_msg" />
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="rowEdit(scope.$index, scope.row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-popconfirm
|
||||
title="确定要删除当前应用吗?"
|
||||
@confirm="removeRole(scope.row)"
|
||||
:width="200"
|
||||
>
|
||||
<el-button size="small" type="primary" @click="rowEdit(scope.$index, scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确定要删除当前应用吗?" @confirm="removeRole(scope.row)" :width="200">
|
||||
<template #reference>
|
||||
<el-button size="small" type="danger">删除</el-button>
|
||||
</template>
|
||||
@@ -65,35 +50,14 @@
|
||||
</el-table>
|
||||
</el-row>
|
||||
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
:title="optTitle"
|
||||
:close-on-click-modal="false"
|
||||
width="50%"
|
||||
>
|
||||
<el-form
|
||||
:model="role"
|
||||
label-width="120px"
|
||||
ref="formRef"
|
||||
label-position="left"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-dialog v-model="showDialog" :title="optTitle" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="role" label-width="120px" ref="formRef" label-position="left" :rules="rules">
|
||||
<el-form-item label="应用名称:" prop="name">
|
||||
<el-input v-model="role.name" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="应用分类:" prop="tid">
|
||||
<el-select
|
||||
v-model="role.tid"
|
||||
filterable
|
||||
placeholder="请选择分类"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in appTypes"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-select v-model="role.tid" filterable placeholder="请选择分类" clearable>
|
||||
<el-option v-for="item in appTypes" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -104,30 +68,14 @@
|
||||
<el-form-item label="应用图标:" prop="icon">
|
||||
<el-input v-model="role.icon">
|
||||
<template #append>
|
||||
<el-upload
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
上传
|
||||
</el-upload>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" :http-request="uploadImg"> 上传 </el-upload>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="绑定模型:" prop="model_id">
|
||||
<el-select
|
||||
v-model="role.model_id"
|
||||
filterable
|
||||
placeholder="请选择模型"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-select v-model="role.model_id" filterable placeholder="请选择模型" clearable>
|
||||
<el-option v-for="item in models" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -141,12 +89,7 @@
|
||||
<el-table-column label="对话应用" width="120">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.role" placeholder="Role">
|
||||
<el-option
|
||||
v-for="value in messageRoles"
|
||||
:key="value"
|
||||
:label="value"
|
||||
:value="value"
|
||||
/>
|
||||
<el-option v-for="value in messageRoles" :key="value" :label="value" :value="value" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -155,11 +98,7 @@
|
||||
<div class="context-msg-key">
|
||||
<span>对话内容</span>
|
||||
<span class="fr">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="addContext"
|
||||
size="small"
|
||||
>
|
||||
<el-button type="primary" @click="addContext" size="small">
|
||||
<el-icon>
|
||||
<Plus />
|
||||
</el-icon>
|
||||
@@ -171,38 +110,17 @@
|
||||
|
||||
<template #default="scope">
|
||||
<div class="context-msg-content">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model="scope.row.content"
|
||||
autocomplete="off"
|
||||
v-loading="isGenerating"
|
||||
/>
|
||||
<el-input type="textarea" :rows="3" v-model="scope.row.content" autocomplete="off" v-loading="isGenerating" />
|
||||
<span class="remove-item">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="删除当前行"
|
||||
placement="right"
|
||||
>
|
||||
<el-tooltip effect="dark" content="删除当前行" placement="right">
|
||||
<el-button circle type="danger" size="small">
|
||||
<el-icon @click="removeContext(scope.$index)"
|
||||
><Delete
|
||||
/></el-icon>
|
||||
<el-icon @click="removeContext(scope.$index)"><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="400"
|
||||
trigger="click"
|
||||
>
|
||||
<el-popover placement="right" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="primary"
|
||||
circle
|
||||
size="small"
|
||||
class="icon-btn"
|
||||
>
|
||||
<el-button type="primary" circle size="small" class="icon-btn">
|
||||
<i class="iconfont icon-linggan"></i>
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -214,16 +132,8 @@
|
||||
placeholder="请您输入要 AI实现的目标,任务或者需要AI扮演的角色?"
|
||||
/>
|
||||
<el-row class="text-line">
|
||||
<el-text type="info" size="small"
|
||||
>使用 AI 生成 System 预设指令</el-text
|
||||
>
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
size="small"
|
||||
@click="generatePrompt(scope.row)"
|
||||
color="#5865f2"
|
||||
:disabled="isGenerating"
|
||||
>
|
||||
<el-text type="info" size="small">使用 AI 生成 System 预设指令</el-text>
|
||||
<el-button class="generate-btn" size="small" @click="generatePrompt(scope.row)" color="#5865f2" :disabled="isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
<span>立即生成</span>
|
||||
</el-button>
|
||||
@@ -278,11 +188,9 @@ const rules = reactive({
|
||||
icon: [{ required: true, message: "请输入应用图标", trigger: "blur" }],
|
||||
sort: [
|
||||
{ required: true, message: "请输入排序数字", trigger: "blur" },
|
||||
{ type: "number", message: "请输入有效数字" }
|
||||
{ type: "number", message: "请输入有效数字" },
|
||||
],
|
||||
hello_msg: [
|
||||
{ required: true, message: "请输入打招呼信息", trigger: "change" }
|
||||
]
|
||||
hello_msg: [{ required: true, message: "请输入打招呼信息", trigger: "change" }],
|
||||
});
|
||||
|
||||
const appTypes = ref([]);
|
||||
@@ -339,9 +247,7 @@ const fetchData = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedData = Array.from(from.children).map((row) =>
|
||||
row.querySelector(".sort").getAttribute("data-id")
|
||||
);
|
||||
const sortedData = Array.from(from.children).map((row) => row.querySelector(".sort").getAttribute("data-id"));
|
||||
const ids = [];
|
||||
const sorts = [];
|
||||
sortedData.forEach((id, index) => {
|
||||
@@ -350,12 +256,10 @@ const fetchData = () => {
|
||||
tableData.value[index].sort_num = index + 1;
|
||||
});
|
||||
|
||||
httpPost("/api/admin/role/sort", { ids: ids, sorts: sorts }).catch(
|
||||
(e) => {
|
||||
ElMessage.error("排序失败:" + e.message);
|
||||
}
|
||||
);
|
||||
}
|
||||
httpPost("/api/admin/role/sort", { ids: ids, sorts: sorts }).catch((e) => {
|
||||
ElMessage.error("排序失败:" + e.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -363,7 +267,7 @@ const roleSet = (filed, row) => {
|
||||
httpPost("/api/admin/role/set", {
|
||||
id: row.id,
|
||||
filed: filed,
|
||||
value: row[filed]
|
||||
value: row[filed],
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("操作成功!");
|
||||
@@ -448,7 +352,7 @@ const uploadImg = (file) => {
|
||||
},
|
||||
error(e) {
|
||||
ElMessage.error("上传失败:" + e.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -20,22 +20,22 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useSidebarStore } from "@/store/sidebar";
|
||||
import { useTagsStore } from "@/store/tags";
|
||||
import {useSidebarStore} from "@/store/sidebar";
|
||||
import {useTagsStore} from "@/store/tags";
|
||||
import AdminHeader from "@/components/admin/AdminHeader.vue";
|
||||
import AdminSidebar from "@/components/admin/AdminSidebar.vue";
|
||||
import AdminTags from "@/components/admin/AdminTags.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { checkAdminSession } from "@/store/cache";
|
||||
import { ref, watch } from "vue";
|
||||
import { useSharedStore } from "@/store/sharedata";
|
||||
import {useRouter} from "vue-router";
|
||||
import {checkAdminSession} from "@/store/cache";
|
||||
import {ref, watch} from "vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const tags = useTagsStore();
|
||||
const isLogin = ref(false);
|
||||
const contentHeight = window.innerHeight - 80;
|
||||
const store = useSharedStore();
|
||||
const theme = ref(store.adminTheme);
|
||||
const theme = ref(store.theme);
|
||||
|
||||
// 获取会话信息
|
||||
const router = useRouter();
|
||||
@@ -48,7 +48,7 @@ checkAdminSession()
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.adminTheme,
|
||||
() => store.theme,
|
||||
(val) => {
|
||||
theme.value = val;
|
||||
}
|
||||
@@ -56,7 +56,5 @@ watch(
|
||||
</script>
|
||||
|
||||
<style scoped lang="stylus">
|
||||
// @import '@/assets/css/color-dark.styl';
|
||||
@import '@/assets/css/main.styl';
|
||||
@import '@/assets/iconfont/iconfont.css';
|
||||
</style>
|
||||
|
@@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div class="container menu" v-loading="loading">
|
||||
|
||||
<div class="handle-box">
|
||||
<el-button type="primary" :icon="Plus" @click="add">新增</el-button>
|
||||
</div>
|
||||
|
||||
<el-row>
|
||||
<el-table :data="items" :row-key="row => row.id" table-layout="auto">
|
||||
<el-table :data="items" :row-key="(row) => row.id" table-layout="auto" border style="width: 100%">
|
||||
<el-table-column prop="name" label="菜单名称">
|
||||
<template #default="scope">
|
||||
<span class="sort" :data-id="scope.row.id">
|
||||
@@ -17,13 +16,16 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="icon" label="菜单图标">
|
||||
<template #default="scope">
|
||||
<el-image class="menu-icon" :src="scope.row.icon"/>
|
||||
<span v-if="scope.row.icon.startsWith('icon')">
|
||||
<i class="iconfont" :class="scope.row.icon" style="font-size: 30px"></i>
|
||||
</span>
|
||||
<el-image class="menu-icon" :src="scope.row.icon" v-else />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="url" label="菜单URL"/>
|
||||
<el-table-column prop="url" label="菜单URL" />
|
||||
<el-table-column prop="enabled" label="启用状态">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row['enabled']" @change="enable(scope.row)"/>
|
||||
<el-switch v-model="scope.row['enabled']" @change="enable(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180">
|
||||
@@ -39,26 +41,28 @@
|
||||
</el-table>
|
||||
</el-row>
|
||||
|
||||
<el-dialog
|
||||
v-model="showDialog"
|
||||
:title="title"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-dialog v-model="showDialog" :title="title" :close-on-click-modal="false">
|
||||
<el-form :model="item" label-width="120px" ref="formRef" :rules="rules">
|
||||
<el-form-item label="菜单名称:" prop="name">
|
||||
<el-input v-model="item.name" autocomplete="off"/>
|
||||
<el-input v-model="item.name" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="菜单图标:" prop="icon">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<div class="label-title">
|
||||
开放注册
|
||||
<el-tooltip effect="dark" content="可以填写 iconfont 图标名称也可以自己上传图片" raw-content placement="right">
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="item.icon" placeholder="菜单图标地址">
|
||||
<template #append>
|
||||
<el-upload
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadImg"
|
||||
>
|
||||
<el-upload :auto-upload="true" :show-file-list="false" :http-request="uploadImg">
|
||||
<el-icon class="uploader-icon">
|
||||
<UploadFilled/>
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
</template>
|
||||
@@ -66,141 +70,149 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="菜单URL:" prop="url">
|
||||
<el-input v-model="item.url" autocomplete="off"/>
|
||||
<el-input v-model="item.url" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="启用状态:" prop="enable">
|
||||
<el-switch v-model="item.enabled"/>
|
||||
<el-switch v-model="item.enabled" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="save">提交</el-button>
|
||||
</span>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="save">提交</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {dateFormat, removeArrayItem} from "@/utils/libs";
|
||||
import {Plus, UploadFilled} from "@element-plus/icons-vue";
|
||||
import {Sortable} from "sortablejs";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { httpGet, httpPost } from "@/utils/http";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { dateFormat, removeArrayItem } from "@/utils/libs";
|
||||
import { InfoFilled, Plus, UploadFilled } from "@element-plus/icons-vue";
|
||||
import { Sortable } from "sortablejs";
|
||||
import Compressor from "compressorjs";
|
||||
|
||||
// 变量定义
|
||||
const items = ref([])
|
||||
const item = ref({})
|
||||
const showDialog = ref(false)
|
||||
const title = ref("")
|
||||
const items = ref([]);
|
||||
const item = ref({});
|
||||
const showDialog = ref(false);
|
||||
const title = ref("");
|
||||
const rules = reactive({
|
||||
name: [{required: true, message: '请输入菜单名称', trigger: 'change',}],
|
||||
icon: [{required: true, message: '请上传菜单图标', trigger: 'change',}],
|
||||
url: [{required: true, message: '请输入菜单地址', trigger: 'change',}],
|
||||
})
|
||||
const loading = ref(true)
|
||||
const formRef = ref(null)
|
||||
name: [{ required: true, message: "请输入菜单名称", trigger: "change" }],
|
||||
icon: [{ required: true, message: "请上传菜单图标", trigger: "change" }],
|
||||
url: [{ required: true, message: "请输入菜单地址", trigger: "change" }],
|
||||
});
|
||||
const loading = ref(true);
|
||||
const formRef = ref(null);
|
||||
|
||||
const fetchData = () => {
|
||||
// 获取数据
|
||||
httpGet('/api/admin/menu/list').then((res) => {
|
||||
if (res.data) {
|
||||
// 初始化数据
|
||||
const arr = res.data;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i].last_used_at = dateFormat(arr[i].last_used_at)
|
||||
httpGet("/api/admin/menu/list")
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
// 初始化数据
|
||||
const arr = res.data;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i].last_used_at = dateFormat(arr[i].last_used_at);
|
||||
}
|
||||
items.value = arr;
|
||||
}
|
||||
items.value = arr
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(() => {
|
||||
ElMessage.error("获取数据失败");
|
||||
})
|
||||
}
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("获取数据失败");
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const drawBodyWrapper = document.querySelector('.el-table__body tbody')
|
||||
fetchData()
|
||||
const drawBodyWrapper = document.querySelector(".el-table__body tbody");
|
||||
fetchData();
|
||||
|
||||
// 初始化拖动排序插件
|
||||
Sortable.create(drawBodyWrapper, {
|
||||
sort: true,
|
||||
animation: 500,
|
||||
onEnd({newIndex, oldIndex, from}) {
|
||||
onEnd({ newIndex, oldIndex, from }) {
|
||||
if (oldIndex === newIndex) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id'));
|
||||
const ids = []
|
||||
const sorts = []
|
||||
const sortedData = Array.from(from.children).map((row) => row.querySelector(".sort").getAttribute("data-id"));
|
||||
const ids = [];
|
||||
const sorts = [];
|
||||
sortedData.forEach((id, index) => {
|
||||
ids.push(parseInt(id))
|
||||
sorts.push(index + 1)
|
||||
items.value[index].sort_num = index + 1
|
||||
})
|
||||
ids.push(parseInt(id));
|
||||
sorts.push(index + 1);
|
||||
items.value[index].sort_num = index + 1;
|
||||
});
|
||||
|
||||
httpPost("/api/admin/menu/sort", {ids: ids, sorts: sorts}).catch(e => {
|
||||
ElMessage.error("排序失败:" + e.message)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
httpPost("/api/admin/menu/sort", { ids: ids, sorts: sorts }).catch((e) => {
|
||||
ElMessage.error("排序失败:" + e.message);
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const add = function () {
|
||||
title.value = "新增菜单"
|
||||
showDialog.value = true
|
||||
item.value = {}
|
||||
}
|
||||
title.value = "新增菜单";
|
||||
showDialog.value = true;
|
||||
item.value = {};
|
||||
};
|
||||
|
||||
const edit = function (row) {
|
||||
title.value = "修改菜单"
|
||||
showDialog.value = true
|
||||
item.value = row
|
||||
}
|
||||
title.value = "修改菜单";
|
||||
showDialog.value = true;
|
||||
item.value = row;
|
||||
};
|
||||
|
||||
const save = function () {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
showDialog.value = false
|
||||
showDialog.value = false;
|
||||
if (!item.value.id) {
|
||||
item.value.sort_num = items.value.length + 1
|
||||
item.value.sort_num = items.value.length + 1;
|
||||
}
|
||||
httpPost('/api/admin/menu/save', item.value).then(() => {
|
||||
ElMessage.success('操作成功!')
|
||||
fetchData()
|
||||
}).catch((e) => {
|
||||
ElMessage.error('操作失败,' + e.message)
|
||||
})
|
||||
httpPost("/api/admin/menu/save", item.value)
|
||||
.then(() => {
|
||||
ElMessage.success("操作成功!");
|
||||
fetchData();
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("操作失败," + e.message);
|
||||
});
|
||||
} else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const enable = (row) => {
|
||||
httpPost('/api/admin/menu/enable', {id: row.id, enabled: row.enabled}).then(() => {
|
||||
ElMessage.success("操作成功!")
|
||||
}).catch(e => {
|
||||
ElMessage.error("操作失败:" + e.message)
|
||||
})
|
||||
}
|
||||
httpPost("/api/admin/menu/enable", { id: row.id, enabled: row.enabled })
|
||||
.then(() => {
|
||||
ElMessage.success("操作成功!");
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("操作失败:" + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const remove = function (row) {
|
||||
httpGet('/api/admin/menu/remove?id=' + row.id).then(() => {
|
||||
ElMessage.success("删除成功!")
|
||||
items.value = removeArrayItem(items.value, row, (v1, v2) => {
|
||||
return v1.id === v2.id
|
||||
httpGet("/api/admin/menu/remove?id=" + row.id)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功!");
|
||||
items.value = removeArrayItem(items.value, row, (v1, v2) => {
|
||||
return v1.id === v2.id;
|
||||
});
|
||||
})
|
||||
}).catch((e) => {
|
||||
ElMessage.error("删除失败:" + e.message)
|
||||
})
|
||||
}
|
||||
.catch((e) => {
|
||||
ElMessage.error("删除失败:" + e.message);
|
||||
});
|
||||
};
|
||||
|
||||
// 图片上传
|
||||
const uploadImg = (file) => {
|
||||
@@ -209,35 +221,32 @@ const uploadImg = (file) => {
|
||||
quality: 0.6,
|
||||
success(result) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', result, result.name);
|
||||
formData.append("file", result, result.name);
|
||||
// 执行上传操作
|
||||
httpPost('/api/admin/upload', formData).then((res) => {
|
||||
item.value.icon = res.data.url
|
||||
ElMessage.success('上传成功')
|
||||
}).catch((e) => {
|
||||
ElMessage.error('上传失败:' + e.message)
|
||||
})
|
||||
httpPost("/api/admin/upload", formData)
|
||||
.then((res) => {
|
||||
item.value.icon = res.data.url;
|
||||
ElMessage.success("上传成功");
|
||||
})
|
||||
.catch((e) => {
|
||||
ElMessage.error("上传失败:" + e.message);
|
||||
});
|
||||
},
|
||||
error(e) {
|
||||
ElMessage.error('上传失败:' + e.message)
|
||||
ElMessage.error("上传失败:" + e.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
<style lang="stylus">
|
||||
@import "@/assets/css/admin/form.styl"
|
||||
@import "@/assets/css/main.styl"
|
||||
.menu {
|
||||
|
||||
.opt-box {
|
||||
padding-bottom: 10px;
|
||||
display flex;
|
||||
justify-content flex-end
|
||||
|
||||
.el-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.handle-box {
|
||||
margin-bottom 20px
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width 36px
|
||||
height 36px
|
||||
@@ -255,5 +264,9 @@ const uploadImg = (file) => {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.el-table .cell {
|
||||
height: 100% !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,41 +1,20 @@
|
||||
<template>
|
||||
<div class="container user-list" v-loading="loading">
|
||||
<div class="handle-box">
|
||||
<el-input
|
||||
v-model="query.username"
|
||||
placeholder="账号"
|
||||
class="handle-input mr10"
|
||||
></el-input>
|
||||
<el-button type="primary" :icon="Search" @click="handleSearch"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button type="success" :icon="Plus" @click="addUser"
|
||||
>新增用户</el-button
|
||||
>
|
||||
<el-button type="danger" :icon="Delete" @click="multipleDelete"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-input v-model="query.username" placeholder="账号" class="handle-input mr10"></el-input>
|
||||
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
|
||||
<el-button type="success" :icon="Plus" @click="addUser">新增用户</el-button>
|
||||
<el-button type="danger" :icon="Delete" @click="multipleDelete">删除</el-button>
|
||||
</div>
|
||||
|
||||
<el-row>
|
||||
<el-table
|
||||
:data="users.items"
|
||||
border
|
||||
class="table"
|
||||
:row-key="(row) => row.id"
|
||||
@selection-change="handleSelectionChange"
|
||||
table-layout="auto"
|
||||
>
|
||||
<el-table :data="users.items" border class="table" :row-key="(row) => row.id" @selection-change="handleSelectionChange" table-layout="auto">
|
||||
<el-table-column type="selection" width="38"></el-table-column>
|
||||
<el-table-column prop="id" label="ID" />
|
||||
<el-table-column label="账号">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.username }}</span>
|
||||
<el-image
|
||||
v-if="scope.row.vip"
|
||||
:src="vipImg"
|
||||
style="height: 20px; position: relative; top: 5px; left: 5px"
|
||||
/>
|
||||
<el-image v-if="scope.row.vip" :src="vipImg" style="height: 20px; position: relative; top: 5px; left: 5px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机" />
|
||||
@@ -50,9 +29,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="过期时间">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row['expired_time']">{{
|
||||
scope.row["expired_time"]
|
||||
}}</span>
|
||||
<span v-if="scope.row['expired_time']">{{ scope.row["expired_time"] }}</span>
|
||||
<el-tag v-else>长期有效</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -66,24 +43,9 @@
|
||||
<el-table-column fixed="right" label="操作" width="200">
|
||||
<template #default="scope">
|
||||
<el-button-group class="ml-4">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="userEdit(scope.row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="removeUser(scope.row)"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="success"
|
||||
@click="resetPass(scope.row)"
|
||||
>重置密码</el-button
|
||||
>
|
||||
<el-button size="small" type="primary" @click="userEdit(scope.row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="removeUser(scope.row)">删除</el-button>
|
||||
<el-button size="small" type="success" @click="resetPass(scope.row)">重置密码</el-button>
|
||||
</el-button-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -102,18 +64,8 @@
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-dialog
|
||||
v-model="showUserEditDialog"
|
||||
:title="title"
|
||||
:close-on-click-modal="false"
|
||||
width="50%"
|
||||
>
|
||||
<el-form
|
||||
:model="user"
|
||||
label-width="100px"
|
||||
ref="userEditFormRef"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-dialog v-model="showUserEditDialog" :title="title" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="user" label-width="100px" ref="userEditFormRef" :rules="rules">
|
||||
<el-form-item label="账号:" prop="username">
|
||||
<el-input v-model="user.username" autocomplete="off" />
|
||||
</el-form-item>
|
||||
@@ -124,18 +76,10 @@
|
||||
<el-input v-model="user.email" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="add" label="密码:" prop="password">
|
||||
<el-input
|
||||
v-model="user.password"
|
||||
autocomplete="off"
|
||||
placeholder="8-16位"
|
||||
/>
|
||||
<el-input v-model="user.password" autocomplete="off" placeholder="8-16位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="剩余算力:" prop="power">
|
||||
<el-input
|
||||
v-model.number="user.power"
|
||||
autocomplete="off"
|
||||
placeholder="0"
|
||||
/>
|
||||
<el-input v-model.number="user.power" autocomplete="off" placeholder="0" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效期:" prop="expired_time">
|
||||
@@ -150,34 +94,14 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="聊天角色" prop="chat_roles">
|
||||
<el-select
|
||||
v-model="user.chat_roles"
|
||||
multiple
|
||||
:filterable="true"
|
||||
placeholder="选择聊天角色,多选"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.key"
|
||||
:label="item.name"
|
||||
:value="item.key"
|
||||
/>
|
||||
<el-select v-model="user.chat_roles" multiple :filterable="true" placeholder="选择聊天角色,多选">
|
||||
<el-option v-for="item in roles" :key="item.key" :label="item.name" :value="item.key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="模型权限" prop="chat_models">
|
||||
<el-select
|
||||
v-model="user.chat_models"
|
||||
multiple
|
||||
:filterable="true"
|
||||
placeholder="选择AI模型,多选"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in models"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-select v-model="user.chat_models" multiple :filterable="true" placeholder="选择AI模型,多选">
|
||||
<el-option v-for="item in models" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -201,12 +125,7 @@
|
||||
<el-dialog v-model="showResetPassDialog" title="重置密码" width="50%">
|
||||
<el-form label-width="100px" ref="userEditFormRef">
|
||||
<el-form-item label="账户:">
|
||||
<el-input
|
||||
v-model="pass.username"
|
||||
autocomplete="off"
|
||||
readonly
|
||||
disabled
|
||||
/>
|
||||
<el-input v-model="pass.username" autocomplete="off" readonly disabled />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="新密码:">
|
||||
@@ -252,17 +171,15 @@ const rules = reactive({
|
||||
return !(value.length > 16 || value.length < 8);
|
||||
},
|
||||
message: "密码必须为8-16",
|
||||
trigger: "blur"
|
||||
}
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
calls: [
|
||||
{ required: true, message: "请输入提问次数" },
|
||||
{ type: "number", message: "请输入有效数字" }
|
||||
{ type: "number", message: "请输入有效数字" },
|
||||
],
|
||||
chat_roles: [
|
||||
{ required: true, message: "请选择聊天角色", trigger: "change" }
|
||||
],
|
||||
chat_models: [{ required: true, message: "请选择AI模型", trigger: "change" }]
|
||||
chat_roles: [{ required: true, message: "请选择聊天角色", trigger: "change" }],
|
||||
chat_models: [{ required: true, message: "请选择AI模型", trigger: "change" }],
|
||||
});
|
||||
const loading = ref(true);
|
||||
|
||||
@@ -317,15 +234,11 @@ const handleSearch = () => {
|
||||
|
||||
// 删除用户
|
||||
const removeUser = function (user) {
|
||||
ElMessageBox.confirm(
|
||||
"此操作将会永久删除用户信息和聊天记录,确认操作吗?",
|
||||
"警告",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm("此操作将会永久删除用户信息和聊天记录,确认操作吗?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
httpGet("/api/admin/user/remove", { id: user.id })
|
||||
.then(() => {
|
||||
@@ -385,15 +298,11 @@ const handleSelectionChange = function (rows) {
|
||||
};
|
||||
|
||||
const multipleDelete = function () {
|
||||
ElMessageBox.confirm(
|
||||
"此操作将会永久删除用户信息和聊天记录,确认操作吗?",
|
||||
"警告",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}
|
||||
)
|
||||
ElMessageBox.confirm("此操作将会永久删除用户信息和聊天记录,确认操作吗?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
httpGet("/api/admin/user/remove", { ids: userIds.value })
|
||||
|
@@ -22,9 +22,9 @@ import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const active = ref('home')
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.mobileTheme)
|
||||
const theme = ref(store.theme)
|
||||
|
||||
watch(() => store.mobileTheme, (val) => {
|
||||
watch(() => store.theme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
|
||||
|
@@ -125,7 +125,7 @@
|
||||
<van-cell-group inset>
|
||||
<van-field name="switch" label="暗黑主题">
|
||||
<template #input>
|
||||
<van-switch v-model="dark" @change="(val) => store.setMobileTheme(val?'dark':'light')"/>
|
||||
<van-switch v-model="dark" @change="(val) => store.setTheme(val?'dark':'light')"/>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
@@ -189,7 +189,7 @@ const isLogin = ref(false)
|
||||
const showSettings = ref(false)
|
||||
const store = useSharedStore()
|
||||
const stream = ref(store.chatStream)
|
||||
const dark = ref(store.mobileTheme === 'dark')
|
||||
const dark = ref(store.theme === 'dark')
|
||||
|
||||
onMounted(() => {
|
||||
checkSession().then(user => {
|
||||
|
7
web/tailwind.config.js
Normal file
7
web/tailwind.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
@@ -9,14 +9,14 @@ module.exports = defineConfig({
|
||||
configureWebpack: {
|
||||
// disable performance hints
|
||||
performance: {
|
||||
hints: false
|
||||
hints: false,
|
||||
},
|
||||
plugins: [new webpack.optimize.MinChunkSizePlugin({ minChunkSize: 10000 })],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src")
|
||||
}
|
||||
}
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
publicPath: "/",
|
||||
@@ -29,8 +29,8 @@ module.exports = defineConfig({
|
||||
proxy: {
|
||||
"/static/upload/": {
|
||||
target: process.env.VUE_APP_API_HOST,
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
}
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user