mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 08:13:43 +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