feat(ui): 第一版样式,引入了NavieUI
This commit is contained in:
parent
249321ac67
commit
0eb226ff60
|
|
@ -18,6 +18,7 @@
|
|||
"lucide-vue-next": "^0.563.0",
|
||||
"markstream-vue": "^0.0.7-beta.4",
|
||||
"mermaid": "^11.12.2",
|
||||
"naive-ui": "^2.44.1",
|
||||
"pinia": "^3.0.4",
|
||||
"shiki": "^3.22.0",
|
||||
"stream-markdown": "^0.0.14",
|
||||
|
|
@ -220,6 +221,30 @@
|
|||
"integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@css-render/plugin-bem": {
|
||||
"version": "0.15.14",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@css-render/plugin-bem/-/plugin-bem-0.15.14.tgz",
|
||||
"integrity": "sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"css-render": "~0.15.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@css-render/vue3-ssr": {
|
||||
"version": "0.15.14",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@css-render/vue3-ssr/-/vue3-ssr-0.15.14.tgz",
|
||||
"integrity": "sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
|
||||
|
|
@ -741,6 +766,12 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@juggle/resize-observer": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@mermaid-js/parser": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@mermaid-js/parser/-/parser-1.0.1.tgz",
|
||||
|
|
@ -1819,6 +1850,21 @@
|
|||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.24",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@types/lodash/-/lodash-4.17.24.tgz",
|
||||
"integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz",
|
||||
|
|
@ -2838,6 +2884,12 @@
|
|||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/async-validator": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.24",
|
||||
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.24.tgz",
|
||||
|
|
@ -3173,6 +3225,22 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-render": {
|
||||
"version": "0.15.14",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/css-render/-/css-render-0.15.14.tgz",
|
||||
"integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "~0.8.0",
|
||||
"csstype": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/css-render/node_modules/csstype": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-3.0.11.tgz",
|
||||
"integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz",
|
||||
|
|
@ -3700,6 +3768,25 @@
|
|||
"lodash-es": "^4.17.21"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns-tz": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/date-fns-tz/-/date-fns-tz-3.2.0.tgz",
|
||||
"integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"date-fns": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.19",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
|
||||
|
|
@ -3927,6 +4014,12 @@
|
|||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/evtd": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/evtd/-/evtd-0.2.4.tgz",
|
||||
"integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/expect-type/-/expect-type-1.3.0.tgz",
|
||||
|
|
@ -4121,6 +4214,15 @@
|
|||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
|
||||
|
|
@ -4872,6 +4974,38 @@
|
|||
"integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/naive-ui": {
|
||||
"version": "2.44.1",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/naive-ui/-/naive-ui-2.44.1.tgz",
|
||||
"integrity": "sha512-reo8Esw0p58liZwbUutC7meW24Xbn3EwNv91zReWKm2W4JPu+zfgJRn/F7aO0BFmvN+h2brA2M5lRvYqLq4kuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@css-render/plugin-bem": "^0.15.14",
|
||||
"@css-render/vue3-ssr": "^0.15.14",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"async-validator": "^4.2.5",
|
||||
"css-render": "^0.15.14",
|
||||
"csstype": "^3.1.3",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"evtd": "^0.2.4",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"seemly": "^0.3.10",
|
||||
"treemate": "^0.3.11",
|
||||
"vdirs": "^0.1.8",
|
||||
"vooks": "^0.2.12",
|
||||
"vueuc": "^0.4.65"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
|
||||
|
|
@ -5363,6 +5497,12 @@
|
|||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/seemly": {
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/seemly/-/seemly-0.3.10.tgz",
|
||||
"integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/semver/-/semver-7.7.4.tgz",
|
||||
|
|
@ -5749,6 +5889,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/treemate": {
|
||||
"version": "0.3.11",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/treemate/-/treemate-0.3.11.tgz",
|
||||
"integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
|
|
@ -6055,6 +6201,18 @@
|
|||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vdirs": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/vdirs/-/vdirs-0.1.8.tgz",
|
||||
"integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"evtd": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz",
|
||||
|
|
@ -6239,6 +6397,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vooks": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/vooks/-/vooks-0.2.12.tgz",
|
||||
"integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"evtd": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-jsonrpc": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||
|
|
@ -6423,6 +6593,24 @@
|
|||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vueuc": {
|
||||
"version": "0.4.65",
|
||||
"resolved": "https://mirrors.cloud.tencent.com/npm/vueuc/-/vueuc-0.4.65.tgz",
|
||||
"integrity": "sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@css-render/vue3-ssr": "^0.15.10",
|
||||
"@juggle/resize-observer": "^3.3.1",
|
||||
"css-render": "^0.15.10",
|
||||
"evtd": "^0.2.4",
|
||||
"seemly": "^0.3.6",
|
||||
"vdirs": "^0.1.4",
|
||||
"vooks": "^0.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"lucide-vue-next": "^0.563.0",
|
||||
"markstream-vue": "^0.0.7-beta.4",
|
||||
"mermaid": "^11.12.2",
|
||||
"naive-ui": "^2.44.1",
|
||||
"pinia": "^3.0.4",
|
||||
"shiki": "^3.22.0",
|
||||
"stream-markdown": "^0.0.14",
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ GLM_MODELS = [
|
|||
),
|
||||
ModelInfo(
|
||||
id="glm-4.6v",
|
||||
name="GLM-4.6V(推荐)",
|
||||
name="GLM-4.6V",
|
||||
description="最新旗舰模型,支持文本/图像/文档/深度思考",
|
||||
max_tokens=128000,
|
||||
provider="ZhipuAI",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ def get_current_user_id(request) -> str:
|
|||
Returns:
|
||||
用户 ID 字符串
|
||||
"""
|
||||
# TODO: 实现 token 验证逻辑
|
||||
# 示例:
|
||||
# auth_header = request.headers.get("Authorization")
|
||||
# if auth_header and auth_header.startswith("Bearer "):
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ def _generate_object_key(filename: str, prefix: str = "chat-ui") -> str:
|
|||
根据文件名生成唯一的 OSS 对象 Key
|
||||
格式: {prefix}/{日期}/{uuid}_{原始文件名}
|
||||
"""
|
||||
# TODO: 需要按用户ID分目录
|
||||
date_str = datetime.now().strftime("%Y%m%d")
|
||||
unique_id = uuid.uuid4().hex[:8]
|
||||
safe_name = Path(filename).name # 只取文件名,去掉路径
|
||||
|
|
|
|||
45
src/App.vue
45
src/App.vue
|
|
@ -1,24 +1,28 @@
|
|||
<template>
|
||||
<div class="app" :class="{ dark: isDark }">
|
||||
<router-view />
|
||||
<n-config-provider>
|
||||
<n-message-provider>
|
||||
<div class="app" :class="{ dark: isDark }">
|
||||
<router-view />
|
||||
|
||||
<!-- Toast 通知 -->
|
||||
<Teleport to="body">
|
||||
<TransitionGroup name="toast" tag="div" class="toast-container">
|
||||
<div
|
||||
v-for="toast in toasts"
|
||||
:key="toast.id"
|
||||
class="toast"
|
||||
:class="toast.type"
|
||||
>
|
||||
<Check v-if="toast.type === 'success'" :size="18" />
|
||||
<AlertCircle v-else-if="toast.type === 'error'" :size="18" />
|
||||
<Info v-else :size="18" />
|
||||
<span>{{ toast.message }}</span>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</Teleport>
|
||||
</div>
|
||||
<!-- Toast 通知 -->
|
||||
<Teleport to="body">
|
||||
<TransitionGroup name="toast" tag="div" class="toast-container">
|
||||
<div
|
||||
v-for="toast in toasts"
|
||||
:key="toast.id"
|
||||
class="toast"
|
||||
:class="toast.type"
|
||||
>
|
||||
<Check v-if="toast.type === 'success'" :size="18" />
|
||||
<AlertCircle v-else-if="toast.type === 'error'" :size="18" />
|
||||
<Info v-else :size="18" />
|
||||
<span>{{ toast.message }}</span>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</Teleport>
|
||||
</div>
|
||||
</n-message-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -26,6 +30,7 @@ import { ref, computed } from "vue";
|
|||
import { storeToRefs } from "pinia";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
import { Check, AlertCircle, Info } from "@/components/icons";
|
||||
import { NConfigProvider, NMessageProvider } from "naive-ui";
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const { settings } = storeToRefs(settingsStore);
|
||||
|
|
@ -147,4 +152,4 @@ window.$toast = showToast;
|
|||
.toast-move {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -2,13 +2,12 @@
|
|||
<header class="chat-header">
|
||||
<!-- 左侧:侧边栏切换和标题 -->
|
||||
<div class="header-left">
|
||||
<button
|
||||
class="toggle-sidebar-btn"
|
||||
title="切换侧边栏 (Ctrl+B)"
|
||||
@click="$emit('toggle-sidebar')"
|
||||
>
|
||||
<Menu v-if="showSidebarToggle" :size="20" />
|
||||
<ChevronLeft v-else :size="18" />
|
||||
<button class="toggle-sidebar-btn" title="切换侧边栏 (Ctrl+B)" @click="$emit('toggle-sidebar')">
|
||||
|
||||
<!-- TODO: 侧边栏图标 -->
|
||||
|
||||
<SidebarExpandIcon v-if="showSidebarToggle" />
|
||||
<SidebarCollapseIcon v-else />
|
||||
</button>
|
||||
|
||||
<!-- <div class="conversation-info">
|
||||
|
|
@ -32,14 +31,14 @@
|
|||
</button> -->
|
||||
|
||||
<!-- 清空对话 -->
|
||||
<button
|
||||
<!-- <button
|
||||
class="header-btn"
|
||||
title="清空对话"
|
||||
:disabled="messageCount === 0"
|
||||
@click="handleClear"
|
||||
>
|
||||
<Trash2 :size="18" />
|
||||
</button>
|
||||
</button> -->
|
||||
|
||||
<!-- 导出对话 -->
|
||||
<!-- <button
|
||||
|
|
@ -90,12 +89,13 @@ import { ref } from "vue";
|
|||
import {
|
||||
Menu,
|
||||
Trash2,
|
||||
ChevronLeft,
|
||||
ExternalLink,
|
||||
Pin,
|
||||
Archive,
|
||||
Settings,
|
||||
} from "@/components/icons";
|
||||
import SidebarExpandIcon from "@/components/icons/custom/SidebarExpandIcon.vue";
|
||||
import SidebarCollapseIcon from "@/components/icons/custom/SidebarCollapseIcon.vue";
|
||||
import { useSettingsStore } from "@/stores/settings.ts";
|
||||
|
||||
const props = withDefaults(
|
||||
|
|
|
|||
|
|
@ -545,7 +545,7 @@ watch(
|
|||
|
||||
.input-wrapper {
|
||||
flex-shrink: 0;
|
||||
padding: 16px 10% 24px;
|
||||
padding: 16px 0px 24px;
|
||||
background: linear-gradient(to top, white 80%, transparent);
|
||||
|
||||
.dark & {
|
||||
|
|
@ -565,7 +565,8 @@ watch(
|
|||
}
|
||||
|
||||
.input-container {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
width: 55%;
|
||||
// min-width: 1000px;
|
||||
// margin: 0 auto;
|
||||
transition: max-width 0.3s ease;
|
||||
|
|
|
|||
|
|
@ -6,39 +6,34 @@
|
|||
</div>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<div class="feature-cards">
|
||||
<div
|
||||
v-for="feature in features"
|
||||
:key="feature.title"
|
||||
class="feature-card"
|
||||
>
|
||||
<!-- <div class="feature-cards">
|
||||
<div v-for="feature in features" :key="feature.title" class="feature-card">
|
||||
<div class="feature-icon" :style="{ background: feature.gradient }">
|
||||
<component :is="feature.icon" :size="22" />
|
||||
</div>
|
||||
<h3>{{ feature.title }}</h3>
|
||||
<p>{{ feature.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 快速开始建议 -->
|
||||
<div class="quick-start">
|
||||
<h4>试试这些问题</h4>
|
||||
<n-divider title-placement="center">
|
||||
试试这些问题
|
||||
</n-divider>
|
||||
|
||||
<div class="suggestions-grid">
|
||||
<button
|
||||
v-for="suggestion in suggestions"
|
||||
:key="suggestion.text"
|
||||
class="suggestion-card"
|
||||
@click="$emit('select', { id: suggestion.id, text: suggestion.text, systemPrompt: suggestion.systemPrompt })"
|
||||
>
|
||||
<button v-for="suggestion in suggestions" :key="suggestion.text" class="suggestion-card"
|
||||
@click="$emit('select', { id: suggestion.id, text: suggestion.text, systemPrompt: suggestion.systemPrompt })">
|
||||
<component :is="suggestion.iconComponent" :size="18" class="suggestion-icon" />
|
||||
<span>{{ suggestion.text }}</span>
|
||||
<ChevronRight :size="16" class="arrow-icon" />
|
||||
<!-- <ChevronRight :size="16" class="arrow-icon" /> -->
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<div class="welcome-footer">
|
||||
<!-- <div class="welcome-footer">
|
||||
<div class="tip">
|
||||
<Keyboard :size="14" />
|
||||
<span>按 <kbd>Ctrl</kbd> + <kbd>/</kbd> 聚焦输入框</span>
|
||||
|
|
@ -47,7 +42,7 @@
|
|||
<Zap :size="14" />
|
||||
<span>支持 Markdown、代码高亮、LaTeX 公式</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -67,47 +62,54 @@ import {
|
|||
} from "@/components/icons";
|
||||
import promptData from "@/assets/prompt.json";
|
||||
import type { Suggestion } from "@/types/chat";
|
||||
import StudyIcon from "../icons/custom/StudyIcon.vue";
|
||||
import CodeIcon from "../icons/custom/CodeIcon.vue";
|
||||
import WritingIcon from "../icons/custom/WritingIcon.vue";
|
||||
import GuideIcon from "../icons/custom/GuideIcon.vue";
|
||||
import ThesisIcon from "../icons/custom/ThesisIcon.vue";
|
||||
import ChatIcon from "../icons/custom/ChatIcon.vue";
|
||||
import { NDivider } from "naive-ui";
|
||||
|
||||
defineEmits<{
|
||||
select: [suggestion: Suggestion];
|
||||
}>();
|
||||
|
||||
const features = computed(() => [
|
||||
{
|
||||
icon: MessageSquare,
|
||||
title: "智能对话",
|
||||
description: "自然流畅的对话体验,理解上下文",
|
||||
gradient: "linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",
|
||||
},
|
||||
{
|
||||
icon: Code,
|
||||
title: "代码助手",
|
||||
description: "编写、解释、优化各种编程语言代码",
|
||||
gradient: "linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)",
|
||||
},
|
||||
{
|
||||
icon: Image,
|
||||
title: "图像理解",
|
||||
description: "分析图片内容,提取关键信息",
|
||||
gradient: "linear-gradient(135deg, #ec4899 0%, #d946ef 100%)",
|
||||
},
|
||||
{
|
||||
icon: FileText,
|
||||
title: "文档处理",
|
||||
description: "阅读、总结、翻译各类文档",
|
||||
gradient: "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)",
|
||||
},
|
||||
]);
|
||||
// const features = computed(() => [
|
||||
// {
|
||||
// icon: MessageSquare,
|
||||
// title: "智能对话",
|
||||
// description: "自然流畅的对话体验,理解上下文",
|
||||
// gradient: "linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)",
|
||||
// },
|
||||
// {
|
||||
// icon: Code,
|
||||
// title: "代码助手",
|
||||
// description: "编写、解释、优化各种编程语言代码",
|
||||
// gradient: "linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)",
|
||||
// },
|
||||
// {
|
||||
// icon: Image,
|
||||
// title: "图像理解",
|
||||
// description: "分析图片内容,提取关键信息",
|
||||
// gradient: "linear-gradient(135deg, #ec4899 0%, #d946ef 100%)",
|
||||
// },
|
||||
// {
|
||||
// icon: FileText,
|
||||
// title: "文档处理",
|
||||
// description: "阅读、总结、翻译各类文档",
|
||||
// gradient: "linear-gradient(135deg, #f59e0b 0%, #f97316 100%)",
|
||||
// },
|
||||
// ]);
|
||||
|
||||
// 图标映射,根据文本内容选择合适的图标
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const iconMap: Record<string, any> = {
|
||||
学习: Lightbulb,
|
||||
调试: Code,
|
||||
写作: PenTool,
|
||||
语言: Globe,
|
||||
职业: Globe,
|
||||
学术: FileText,
|
||||
学习: StudyIcon,
|
||||
调试: CodeIcon,
|
||||
写作: WritingIcon,
|
||||
语言: ChatIcon,
|
||||
职业: GuideIcon,
|
||||
学术: ThesisIcon,
|
||||
};
|
||||
|
||||
const suggestions = computed(() => {
|
||||
|
|
@ -177,19 +179,20 @@ const suggestions = computed(() => {
|
|||
.logo-glow {
|
||||
position: absolute;
|
||||
inset: -20px;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
rgba(59, 130, 246, 0.2) 0%,
|
||||
transparent 70%
|
||||
);
|
||||
background: radial-gradient(circle,
|
||||
rgba(59, 130, 246, 0.2) 0%,
|
||||
transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 32px;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
font-family: "Microsoft YaHei";
|
||||
font-size: 35px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
line-height: normal;
|
||||
|
||||
.dark & {
|
||||
color: #f3f4f6;
|
||||
|
|
@ -280,7 +283,7 @@ const suggestions = computed(() => {
|
|||
}
|
||||
|
||||
.quick-start {
|
||||
max-width: 710px;
|
||||
max-width: 650px;
|
||||
width: 100%;
|
||||
margin-bottom: 40px;
|
||||
|
||||
|
|
@ -300,7 +303,7 @@ const suggestions = computed(() => {
|
|||
.suggestions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
|
|
@ -312,11 +315,10 @@ const suggestions = computed(() => {
|
|||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
background: white;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 14px;
|
||||
color: #374151;
|
||||
font-size: 14px;
|
||||
background: #F8F9FA;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
|
@ -326,7 +328,7 @@ const suggestions = computed(() => {
|
|||
border-color: #2d2d3d;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
// TODO: 悬浮边框和背景颜色
|
||||
&:hover {
|
||||
border-color: #3b82f6;
|
||||
background: rgba(59, 130, 246, 0.05);
|
||||
|
|
@ -385,6 +387,7 @@ const suggestions = computed(() => {
|
|||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<g clip-path="url(#clip0_56_267)">
|
||||
<path d="M14.2902 8.35H10.7972C10.7468 10.863 10.1672 13.0267 9.3321 14.1593C10.6804 13.867 11.8955 13.1404 12.7911 12.091C13.6867 11.0417 14.2134 9.72746 14.2902 8.35ZM14.2902 7.65C14.2134 6.27254 13.6867 4.95833 12.7911 3.90896C11.8955 2.85959 10.6804 2.13302 9.3321 1.8407C10.1679 2.9733 10.7468 5.1377 10.7965 7.65H14.2902ZM1.7098 7.65H5.2028C5.2532 5.137 5.8328 2.9733 6.6679 1.8407C5.31962 2.13302 4.10448 2.85959 3.20887 3.90896C2.31325 4.95833 1.78664 6.27254 1.7098 7.65ZM1.7098 8.35C1.78664 9.72746 2.31325 11.0417 3.20887 12.091C4.10448 13.1404 5.31962 13.867 6.6679 14.1593C5.8321 13.0267 5.2532 10.8623 5.2035 8.35H1.7098ZM10.0958 8.35H5.9042C5.9742 11.6946 7.0487 14.3 8 14.3C8.952 14.3 10.0258 11.6946 10.0965 8.35H10.0958ZM10.0958 7.65C10.0265 4.3054 8.952 1.7 8 1.7C7.048 1.7 5.9742 4.3054 5.9035 7.65H10.0965H10.0958ZM8 15C4.1339 15 1 11.8661 1 8C1 4.1339 4.1339 1 8 1C11.8661 1 15 4.1339 15 8C15 11.8661 11.8661 15 8 15Z" fill="#333333" stroke="#333333" stroke-width="0.2"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_56_267">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M4.76252 4.16667C4.54848 3.94444 4.22741 3.94444 4.01336 4.16667L0.160534 8.05556C-0.0535115 8.27778 -0.0535115 8.61111 0.160534 8.83333L3.90634 12.7222C4.12038 12.9444 4.44145 12.9444 4.6555 12.7222C4.86954 12.5 4.86954 12.1667 4.6555 11.9444L1.33779 8.5L4.76252 4.94444C4.86954 4.72222 4.86954 4.38889 4.76252 4.16667ZM8.61535 4.16667C8.4013 4.16667 8.08023 4.27778 7.97321 4.61111L6.0468 12.1667C5.93977 12.5 6.15382 12.7222 6.47489 12.8333C6.79596 12.9444 7.01 12.7222 7.11703 12.3889L9.04344 4.83333C9.15046 4.61111 8.93642 4.27778 8.61535 4.16667ZM14.9297 8.05556L11.1839 4.16667C10.9699 3.94444 10.6488 3.94444 10.4347 4.16667C10.2207 4.38889 10.2207 4.72222 10.4347 4.94444L13.7524 8.5L10.3277 12.0556C10.1137 12.2778 10.1137 12.6111 10.3277 12.8333C10.5418 13.0556 10.8628 13.0556 11.0769 12.8333L14.8227 8.94444C15.0367 8.72222 15.0367 8.27778 14.9297 8.05556Z" fill="#333333"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="15" viewBox="0 0 14 15" fill="none">
|
||||
<path d="M0.5 2.9H2.9M13.3 2.9H10.9M10.9 2.9V2.5C10.9 1.39543 10.0046 0.5 8.9 0.5H4.9C3.79543 0.5 2.9 1.39543 2.9 2.5V2.9M10.9 2.9H2.9" stroke="#666666" stroke-linecap="round"/>
|
||||
<path d="M2.09985 5.3V12.1C2.09985 13.2046 2.99528 14.1 4.09985 14.1H9.69986C10.8044 14.1 11.6999 13.2046 11.6999 12.1V5.3M5.29985 6.1V12.5M8.49985 6.1V12.5" stroke="#666666" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M11.7854 6.40508L3.71021 14.4783H1.51099V12.2791L9.58423 4.2039L11.7854 6.40508ZM12.3801 1.52031C12.4497 1.52042 12.5204 1.54921 12.5735 1.60234L14.387 3.41484C14.4383 3.46622 14.467 3.53595 14.467 3.6082C14.467 3.66395 14.4509 3.71702 14.4211 3.76152L14.387 3.80254L13.3225 4.86699L11.1213 2.66582L12.1858 1.60234L12.1887 1.60039C12.2399 1.5487 12.3071 1.52031 12.3801 1.52031Z" stroke="#666666" stroke-linejoin="round"/>
|
||||
<path d="M6 14.5H14.5" stroke="#666666" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M6.92074 6.24724C6.1667 5.75404 5.09746 6.01283 4.5325 6.82521C3.9675 7.63761 4.12076 8.69595 4.87478 9.18891C5.62882 9.68211 6.69803 9.4231 7.26299 8.61094C7.82794 7.79851 7.67474 6.74 6.92076 6.24724H6.92074ZM6.65622 8.21403C6.31722 8.70148 5.70288 8.87435 5.28391 8.60053C4.86504 8.3267 4.80026 7.70955 5.13926 7.2221C5.47823 6.73469 6.09265 6.56157 6.51152 6.8354C6.93041 7.10946 6.99519 7.72659 6.65622 8.21403ZM15.9786 7.01976C15.8075 6.31794 14.6329 5.92201 12.906 5.85822C12.6518 5.40916 12.3351 4.99803 11.9669 4.63632C11.9671 4.6361 11.9674 4.63588 11.9677 4.63568C11.9528 4.61806 11.936 4.6026 11.9178 4.58868C10.8933 3.60657 9.48509 3 7.93064 3C4.86828 3 2.37312 5.35359 2.26296 8.29706L2.2626 8.29632C0.274154 9.21706 -0.130379 10.0066 0.0325382 10.6751C0.214203 11.4204 1.52517 11.8222 3.42953 11.8463C4.46632 13.1556 6.09668 14 7.93066 14C10.6995 14 13.0045 12.0759 13.5025 9.53162C15.1816 8.68608 16.1595 7.76201 15.9786 7.01976H15.9786ZM11.4376 5.27741C12.0731 5.92742 12.5185 6.75387 12.6873 7.67319C11.878 7.48883 11.2768 6.80172 11.2768 5.98228C11.2768 5.73029 11.3345 5.49214 11.4376 5.27743V5.27741ZM0.998344 10.3334C0.919114 10.0083 1.55274 9.70124 2.31897 9.30181C2.40924 9.90092 2.59893 10.4687 2.8716 10.9888C1.78816 10.9357 1.09133 10.7147 0.998375 10.3334H0.998344ZM7.93067 13.1852C7.74953 13.1852 7.57083 13.1752 7.39486 13.1564C7.48472 12.8522 7.6351 12.7247 8.1408 12.7247C8.46709 12.7247 8.7978 12.858 9.04986 13.0586C8.69061 13.1412 8.31596 13.1852 7.93067 13.1852ZM9.79456 12.8236C9.43627 12.2934 8.71219 11.9628 8.1408 11.9628C7.37193 11.9628 6.68161 12.2818 6.4956 12.975C5.74273 12.7483 5.06889 12.3476 4.52365 11.8217C5.74519 11.7552 7.13415 11.559 8.58553 11.2264C10.0463 10.8915 11.3867 10.4604 12.5142 9.98588C12.072 11.2691 11.0726 12.305 9.7946 12.8236L9.79456 12.8236ZM12.7389 8.96319C11.6621 9.46339 10.2302 9.95074 8.56092 10.3334C6.79961 10.737 5.14847 10.9548 3.84027 10.9947C3.37075 10.2723 3.09902 9.41698 3.09902 8.50005C3.09902 5.91254 5.26225 3.81493 7.93064 3.81493C9.07144 3.81493 10.1197 4.19852 10.9463 4.83969C10.736 5.15487 10.6616 5.51834 10.6616 5.98231C10.6616 7.105 11.5613 8.03877 12.7556 8.25274C12.76 8.33466 12.7623 8.41709 12.7623 8.50008C12.7623 8.65638 12.7543 8.81087 12.7389 8.96323L12.7389 8.96319ZM13.6024 8.51584C13.6024 8.51056 13.6026 8.50531 13.6026 8.50004C13.6026 7.86315 13.4905 7.25173 13.2851 6.68285C14.2559 6.74069 14.821 6.94078 14.863 7.07401C14.9856 7.46309 14.5173 7.98118 13.6024 8.51584Z" fill="#333333"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<mask id="path-1-inside-1_67_407" fill="white">
|
||||
<path d="M8 1.00391C12.4 1.00391 16 3.83468 16 7.32129C15.9999 10.8079 12.4 13.6387 8 13.6387C7.84133 13.6444 7.68514 13.679 7.54004 13.7412C6.67608 14.0652 5.24423 15.0882 4.24023 15.8545C4.12667 15.9434 3.98598 15.9943 3.83984 16C3.74658 15.9983 3.6546 15.9789 3.56934 15.9424C3.48392 15.9058 3.40674 15.8528 3.34277 15.7871C3.21195 15.6526 3.14013 15.4744 3.14355 15.29C3.14355 14.0031 3.06787 12.3127 2.66797 12.0283C1.02798 10.8711 1.03933e-05 9.18904 0 7.31738C0 3.82692 3.6 1 8 1V1.00391Z"/>
|
||||
</mask>
|
||||
<path d="M8 1.00391H7V2.00391H8V1.00391ZM16 7.32129L17 7.3213V7.32129H16ZM8 13.6387V12.6387H7.98194L7.96389 12.6393L8 13.6387ZM7.54004 13.7412L7.89118 14.6775L7.91273 14.6695L7.93388 14.6604L7.54004 13.7412ZM4.24023 15.8545L3.63346 15.0595L3.62397 15.0669L4.24023 15.8545ZM3.83984 16L3.82128 16.9998L3.8502 17.0004L3.87911 16.9992L3.83984 16ZM3.56934 15.9424L3.17562 16.8616L3.1761 16.8618L3.56934 15.9424ZM3.34277 15.7871L2.62605 16.4845L2.62626 16.4847L3.34277 15.7871ZM3.14355 15.29L4.14338 15.3086L4.14355 15.2993V15.29H3.14355ZM2.66797 12.0283L3.24754 11.2134L3.2445 11.2112L2.66797 12.0283ZM0 7.31738H-1V7.31739L0 7.31738ZM8 1H9V0H8V1ZM8 1.00391V2.00391C12.082 2.00391 15 4.59522 15 7.32129H16H17C17 3.07414 12.718 0.00390625 8 0.00390625V1.00391ZM16 7.32129L15 7.32127C15 10.0473 12.082 12.6387 8 12.6387V13.6387V14.6387C12.718 14.6387 16.9999 11.5684 17 7.3213L16 7.32129ZM8 13.6387L7.96389 12.6393C7.68397 12.6494 7.40633 12.7106 7.14619 12.822L7.54004 13.7412L7.93388 14.6604C7.96395 14.6475 7.99869 14.6394 8.03611 14.638L8 13.6387ZM7.54004 13.7412L7.1889 12.8049C6.64275 13.0097 5.98828 13.402 5.39017 13.7985C4.77325 14.2074 4.14496 14.6692 3.63351 15.0596L4.24023 15.8545L4.84696 16.6494C5.3395 16.2735 5.92914 15.8407 6.49513 15.4655C7.07993 15.0779 7.57337 14.7967 7.89118 14.6775L7.54004 13.7412ZM4.24023 15.8545L3.62397 15.0669C3.67951 15.0235 3.74176 15.0031 3.80058 15.0008L3.83984 16L3.87911 16.9992C4.23019 16.9854 4.57382 16.8632 4.8565 16.642L4.24023 15.8545ZM3.83984 16L3.85841 15.0002C3.89139 15.0008 3.92687 15.0077 3.96257 15.0229L3.56934 15.9424L3.1761 16.8618C3.38234 16.95 3.60178 16.9958 3.82128 16.9998L3.83984 16ZM3.56934 15.9424L3.96305 15.0231C3.99791 15.0381 4.03065 15.0601 4.05929 15.0895L3.34277 15.7871L2.62626 16.4847C2.78284 16.6455 2.96994 16.7735 3.17562 16.8616L3.56934 15.9424ZM3.34277 15.7871L4.0595 15.0898C4.10963 15.1413 4.14505 15.2188 4.14338 15.3086L3.14355 15.29L2.14373 15.2714C2.1352 15.73 2.31426 16.164 2.62605 16.4845L3.34277 15.7871ZM3.14355 15.29H4.14355C4.14355 14.6352 4.12467 13.8471 4.04958 13.1726C4.01239 12.8385 3.9577 12.4962 3.87148 12.1987C3.82828 12.0496 3.76969 11.8851 3.68638 11.7283C3.60749 11.5797 3.47285 11.3736 3.24754 11.2134L2.66797 12.0283L2.0884 12.8432C2.02206 12.7961 1.97973 12.7493 1.95655 12.7201C1.93324 12.6908 1.92216 12.6704 1.92006 12.6664C1.91743 12.6615 1.93036 12.6858 1.95049 12.7553C1.99094 12.8949 2.03014 13.1089 2.06186 13.3939C2.12459 13.9574 2.14355 14.658 2.14355 15.29H3.14355ZM2.66797 12.0283L3.2445 11.2112C1.8111 10.1998 1.00001 8.79804 1 7.31738L0 7.31738L-1 7.31739C-0.999987 9.58004 0.244856 11.5424 2.09143 12.8454L2.66797 12.0283ZM0 7.31738H1C1 4.5878 3.91754 2 8 2V1V0C3.28246 0 -1 3.06604 -1 7.31738H0ZM8 1H7V1.00391H8H9V1H8Z" fill="#333333" mask="url(#path-1-inside-1_67_407)"/>
|
||||
<path d="M5 5.73633H11" stroke="#333333" stroke-linecap="round"/>
|
||||
<path d="M6 8.73633H10" stroke="#333333" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<g clip-path="url(#clip0_75_633)">
|
||||
<path d="M5.43994 0.5H5.44189L7.99268 0.507812H8.0083L10.5581 0.5H10.5601C10.6453 0.500024 10.7177 0.541283 10.7603 0.600586L10.7925 0.666016C10.8015 0.697293 10.8004 0.716064 10.7954 0.732422C10.7899 0.750047 10.767 0.804834 10.6704 0.886719L10.6411 0.914062L10.0366 1.51758C9.84509 1.70911 9.74857 1.9731 9.76904 2.23926L9.77002 2.24121L10.0864 6.15332V6.1543C10.1201 6.56503 10.2691 6.95616 10.5151 7.28418L10.6265 7.4209L11.5933 8.5127C11.6397 8.56497 11.6702 8.62659 11.6841 8.68945L11.6929 8.75293C11.6937 8.78175 11.6899 8.8027 11.686 8.81641C11.6827 8.82804 11.6786 8.83521 11.6733 8.8418C11.6651 8.85078 11.647 8.8623 11.6216 8.8623H11.6206L8.87158 8.84277H7.12842L4.37939 8.8623H4.37842C4.34895 8.8623 4.33235 8.84936 4.32764 8.84375L4.32666 8.84277C4.32122 8.83606 4.31665 8.82825 4.31299 8.81543C4.30892 8.80101 4.30578 8.77903 4.30615 8.74902C4.30865 8.66986 4.34102 8.58452 4.40479 8.5127H4.40576L5.37256 7.4209C5.68385 7.06918 5.8747 6.62516 5.91162 6.15332L6.229 2.24121V2.23926C6.24675 2.00853 6.1773 1.77409 6.02783 1.58984L5.95947 1.51465L5.34521 0.900391L5.32959 0.886719L5.27002 0.831055C5.22258 0.780136 5.2087 0.745665 5.20459 0.732422C5.19958 0.716074 5.19847 0.697262 5.20752 0.666016C5.23456 0.573903 5.32611 0.500071 5.43994 0.5Z" stroke="#666666"/>
|
||||
<path d="M8 9V15.5" stroke="#666666" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_75_633">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="14" viewBox="0 0 12 14" fill="none">
|
||||
<path d="M6 13L6 1M6 1L1 6M6 1L11 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<g clip-path="url(#clip0_75_622)">
|
||||
<circle cx="10.5" cy="2.5" r="2" stroke="#666666"/>
|
||||
<circle cx="3.5" cy="7.5" r="2" stroke="#666666"/>
|
||||
<path d="M8.79367 3.71881L5.19458 6.28959" stroke="#666666" stroke-linecap="round"/>
|
||||
<path d="M5 9L9.5 12" stroke="#666666" stroke-linecap="round"/>
|
||||
<circle cx="12" cy="13" r="2.5" stroke="#666666"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_75_622">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<svg
|
||||
:width="width"
|
||||
:height="height"
|
||||
viewBox="0 0 14 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0.702819 11.5556C0.521256 11.5547 0.346487 11.6268 0.215434 11.7565C0.0843801 11.8862 0.00730705 12.0634 0.000494003 12.2507C-0.00631809 12.4379 0.0576639 12.6206 0.178926 12.7601C0.300187 12.8996 0.46923 12.985 0.650342 12.9982L0.702819 13H13.2972C13.4787 13.0008 13.6535 12.9288 13.7846 12.7991C13.9156 12.6694 13.9927 12.4922 13.9995 12.3049C14.0063 12.1176 13.9423 11.9349 13.8211 11.7954C13.6998 11.6559 13.5308 11.5706 13.3497 11.5574L13.2972 11.5556H0.702819Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M4.81103 4.13111C4.76599 4.04842 4.70562 3.9757 4.63335 3.9171C4.56108 3.8585 4.47833 3.81517 4.38984 3.78958C4.30134 3.764 4.20883 3.75666 4.11759 3.76798C4.02634 3.77931 3.93815 3.80908 3.85806 3.85559L0.38761 5.87058C0.279428 5.93342 0.189389 6.02483 0.12673 6.13544C0.0640707 6.24605 0.0310459 6.37187 0.0310459 6.5C0.0310459 6.62813 0.0640707 6.75395 0.12673 6.86456C0.189389 6.97517 0.279428 7.06658 0.38761 7.12942L3.85806 9.14441C3.96453 9.20627 4.08483 9.23827 4.207 9.23724C4.32917 9.2362 4.44895 9.20216 4.55442 9.13851C4.65989 9.07485 4.74738 8.9838 4.80817 8.87441C4.86896 8.76502 4.90095 8.64111 4.90094 8.515V4.485C4.90089 4.36103 4.86993 4.23916 4.81103 4.13111Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M7.06997 5.77778C6.64491 5.77778 6.30031 6.10097 6.30031 6.5C6.30031 6.88061 6.61412 7.19261 7.01259 7.22042L7.06997 7.22222H13.2272C13.6523 7.22222 13.9969 6.89903 13.9969 6.5C13.9969 6.11939 13.6831 5.80739 13.2846 5.77958L13.2272 5.77778H7.06997Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M0.702819 7.24048e-06C0.521255 -0.000831409 0.346487 0.0712134 0.215434 0.200922C0.0843801 0.33063 0.00730515 0.507843 0.00049305 0.695123C-0.00631905 0.882404 0.0576639 1.06508 0.178926 1.20457C0.300187 1.34406 0.46923 1.42943 0.650342 1.44264L0.702819 1.44445H13.2972C13.4787 1.44529 13.6535 1.37324 13.7846 1.24354C13.9156 1.11383 13.9927 0.936614 13.9995 0.749334C14.0063 0.562054 13.9423 0.379374 13.8211 0.239887C13.6998 0.1004 13.5308 0.0150303 13.3497 0.00181292L13.2972 7.24048e-06H0.702819Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
width?: number;
|
||||
height?: number;
|
||||
}>(),
|
||||
{
|
||||
width: 14,
|
||||
height: 13,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<svg
|
||||
:width="width"
|
||||
:height="height"
|
||||
viewBox="0 0 14 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.2972 11.5556C13.4787 11.5547 13.6535 11.6268 13.7846 11.7565C13.9156 11.8862 13.9927 12.0634 13.9995 12.2507C14.0063 12.4379 13.9423 12.6206 13.8211 12.7601C13.6998 12.8996 13.5308 12.985 13.3497 12.9982L13.2972 13H0.702818C0.521256 13.0008 0.346487 12.9288 0.215433 12.7991C0.0843798 12.6694 0.00730556 12.4922 0.000493166 12.3049C-0.00631923 12.1176 0.0576638 11.9349 0.178925 11.7954C0.300187 11.6559 0.469229 11.5706 0.650342 11.5574L0.702818 11.5556H13.2972Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M9.18897 4.13111C9.23401 4.04842 9.29438 3.9757 9.36665 3.9171C9.43892 3.8585 9.52167 3.81517 9.61016 3.78958C9.69866 3.764 9.79117 3.75666 9.88241 3.76798C9.97366 3.77931 10.0618 3.80908 10.1419 3.85559L13.6124 5.87058C13.7206 5.93342 13.8106 6.02483 13.8733 6.13544C13.9359 6.24605 13.969 6.37187 13.969 6.5C13.969 6.62813 13.9359 6.75395 13.8733 6.86456C13.8106 6.97517 13.7206 7.06658 13.6124 7.12942L10.1419 9.14441C10.0355 9.20627 9.91517 9.23827 9.793 9.23724C9.67083 9.2362 9.55105 9.20216 9.44558 9.13851C9.34011 9.07485 9.25262 8.9838 9.19183 8.87441C9.13104 8.76502 9.09905 8.64111 9.09906 8.515V4.485C9.09911 4.36103 9.13007 4.23916 9.18897 4.13111Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M6.93003 5.77778C7.35509 5.77778 7.69969 6.10097 7.69969 6.5C7.69969 6.88061 7.38588 7.19261 6.98741 7.22042L6.93003 7.22222H0.772787C0.347727 7.22222 0.00313156 6.89903 0.00313156 6.5C0.00313156 6.11939 0.316941 5.80739 0.715413 5.77958L0.772787 5.77778H6.93003Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M13.2972 7.24048e-06C13.4787 -0.000831409 13.6535 0.0712134 13.7846 0.200922C13.9156 0.33063 13.9927 0.507843 13.9995 0.695123C14.0063 0.882404 13.9423 1.06508 13.8211 1.20457C13.6998 1.34406 13.5308 1.42943 13.3497 1.44264L13.2972 1.44445H0.702818C0.521256 1.44529 0.346487 1.37324 0.215433 1.24354C0.08438 1.11383 0.00730581 0.936614 0.000493416 0.749334C-0.00631898 0.562054 0.057664 0.379374 0.178925 0.239887C0.300187 0.1004 0.469229 0.0150303 0.650342 0.00181292L0.702818 7.24048e-06H13.2972Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
width?: number;
|
||||
height?: number;
|
||||
}>(),
|
||||
{
|
||||
width: 14,
|
||||
height: 13,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M6.2 15H9.8C9.90609 15 10.0078 14.9566 10.0828 14.8794C10.1579 14.8022 10.2 14.6974 10.2 14.5882C10.2 14.479 10.1579 14.3743 10.0828 14.2971C10.0078 14.2199 9.90609 14.1765 9.8 14.1765H6.2C6.09391 14.1765 5.99217 14.2199 5.91716 14.2971C5.84214 14.3743 5.8 14.479 5.8 14.5882C5.8 14.6974 5.84214 14.8022 5.91716 14.8794C5.99217 14.9566 6.09391 15 6.2 15ZM6.6024 12.5294V10.8799C6.6024 10.527 6.386 10.2482 6.0408 9.98182C5.89518 9.86912 5.74033 9.76965 5.578 9.68453L5.6256 9.71335C5.06234 9.31673 4.60164 8.78457 4.2835 8.16308C3.96535 7.54159 3.79939 6.84956 3.8 6.14706C3.8 3.75924 5.6804 1.82353 8 1.82353C10.32 1.82353 12.2 3.75882 12.2 6.14706C12.2 7.59071 11.508 8.91288 10.3736 9.71418L10.4216 9.68535C10.3016 9.74712 10.132 9.84841 9.9588 9.98182C9.614 10.2482 9.3976 10.5266 9.3976 10.8799C9.3976 11.0289 9.3976 11.2929 9.3984 11.6384L9.3988 11.8607L9.3996 12.3305L9.4 12.5302C9.4 12.7567 9.2212 12.9412 9 12.9412H7C6.89391 12.9412 6.79217 12.8978 6.71716 12.8206C6.64214 12.7434 6.6 12.6386 6.6 12.5294H5.8C5.8 12.857 5.92643 13.1712 6.15147 13.4029C6.37651 13.6346 6.68174 13.7647 7 13.7647H9C9.15758 13.7647 9.31363 13.7328 9.45922 13.6707C9.60481 13.6086 9.7371 13.5176 9.84853 13.4029C9.95996 13.2882 10.0483 13.152 10.1087 13.0021C10.169 12.8523 10.2 12.6916 10.2 12.5294V12.3293L10.1988 11.8595V11.6371L10.1976 10.8799C10.1976 10.8704 10.282 10.7617 10.4388 10.6411C10.5462 10.5589 10.6599 10.4857 10.7788 10.4224L10.8264 10.3936C11.497 9.92125 12.0455 9.28757 12.4243 8.54755C12.8031 7.80752 13.0007 6.98352 13 6.14706C13 3.30424 10.7616 1 8 1C5.2388 1 3 3.30465 3 6.14706C3 7.86412 3.8236 9.43912 5.1728 10.3924L5.2208 10.4212C5.33982 10.4848 5.45365 10.5582 5.5612 10.6406C5.7176 10.7617 5.8024 10.8708 5.8024 10.8799V12.5294H6.6024ZM6.6 12.1176H9.4V11.2941H6.6V12.1176Z" fill="#333333"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M9.54077 4.86591L10.4935 3.91315L13.0714 6.57146L12.1202 7.52504L9.54077 4.86591Z" fill="#333333"/>
|
||||
<path d="M11.6073 3.51805L4.23558 10.893L6.11103 12.7701L13.4828 5.39431L11.6073 3.51805ZM6.78902 13.9676L2.66173 15L2 14.3716L3.03894 10.215L10.9952 2.25393C11.0756 2.17343 11.1711 2.10957 11.2762 2.066C11.3813 2.02243 11.4939 2 11.6077 2C11.7215 2 11.8342 2.02243 11.9393 2.066C12.0444 2.10957 12.1399 2.17343 12.2203 2.25393L14.7461 4.78217C15.0834 5.12035 15.0834 5.66828 14.7461 6.00646L6.78821 13.9676H6.78902Z" fill="#333333"/>
|
||||
<path d="M14.1697 12.323L13.2023 12.718L12.8064 13.6863L12.4113 12.7189L11.4431 12.323L12.4105 11.9279L12.8064 10.9597L13.2015 11.9271L14.1697 12.323Z" fill="#333333"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M1.42196 1.00146C1.46856 0.997573 1.51672 1.00146 1.56565 1.01311L11.0513 3.30269C11.1362 3.32322 11.2135 3.36761 11.2741 3.43064C11.3346 3.49366 11.3759 3.57269 11.393 3.6584L12.2062 7.72497L12.2559 7.67682C12.3418 7.59098 12.4583 7.54276 12.5798 7.54276C12.7012 7.54276 12.8177 7.59098 12.9036 7.67682L14.8663 9.63943C14.9519 9.72532 15 9.84164 15 9.96291C15 10.0842 14.9519 10.2005 14.8663 10.2864L10.2862 14.8663C10.2003 14.9519 10.084 15 9.96274 15C9.84147 15 9.72515 14.9519 9.63925 14.8663L7.67658 12.9037C7.59073 12.8178 7.54251 12.7013 7.54251 12.5798C7.54251 12.4584 7.59073 12.3419 7.67658 12.256L7.72473 12.2063L3.65803 11.3939C3.58501 11.3793 3.51665 11.3471 3.45887 11.3002C3.40108 11.2532 3.35561 11.1929 3.32639 11.1244L3.30309 11.0522L1.01187 1.56609C1.00093 1.51876 0.997518 1.47001 1.00178 1.42163V1.40998C1.00337 1.39061 1.00649 1.37139 1.0111 1.3525L1.01731 1.33076C1.02141 1.31489 1.0266 1.29932 1.03285 1.28416L1.04372 1.26086C1.05287 1.24118 1.06352 1.22222 1.07556 1.20416L1.08022 1.19795L1.09187 1.18242C1.1362 1.12404 1.19368 1.07694 1.25964 1.04495C1.282 1.03385 1.30542 1.02501 1.32954 1.01854L1.35206 1.01233C1.37067 1.00748 1.38962 1.0041 1.40876 1.00223H1.41886L1.42196 1.00146ZM12.5798 8.64764L8.64743 12.5798L9.96313 13.8947L13.8947 9.96329L12.5798 8.64764ZM2.93183 2.28449L6.46263 5.81595C6.83373 5.60181 7.27002 5.52986 7.69023 5.61351C8.11044 5.69716 8.48591 5.93071 8.7467 6.27064C9.00749 6.61058 9.13582 7.03372 9.10778 7.46124C9.07973 7.88876 8.89723 8.29152 8.59426 8.59447C8.2913 8.89743 7.88853 9.07992 7.461 9.10797C7.03346 9.13601 6.6103 9.00769 6.27036 8.74691C5.93041 8.48612 5.69686 8.11066 5.61321 7.69047C5.52955 7.27027 5.6015 6.83399 5.81565 6.4629L2.28408 2.93222L4.12481 10.5528L8.50375 11.4281L11.428 8.50396L10.5526 4.12439L2.93183 2.28449ZM7.34571 6.49552C7.12016 6.49552 6.90384 6.58512 6.74434 6.74461C6.58485 6.9041 6.49525 7.12041 6.49525 7.34596C6.49525 7.57151 6.58485 7.78783 6.74434 7.94731C6.90384 8.1068 7.12016 8.1964 7.34571 8.1964C7.57127 8.1964 7.78759 8.1068 7.94708 7.94731C8.10658 7.78783 8.19618 7.57151 8.19618 7.34596C8.19618 7.12041 8.10658 6.9041 7.94708 6.74461C7.78759 6.58512 7.57127 6.49552 7.34571 6.49552Z" fill="#333333"/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
size?: number;
|
||||
}>(),
|
||||
{
|
||||
size: 18,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
@ -1,137 +1,82 @@
|
|||
<template>
|
||||
<div
|
||||
class="chat-input-container"
|
||||
:class="{ 'is-focused': isFocused, 'is-expanded': isExpanded }"
|
||||
>
|
||||
<div class="chat-input-container" :class="{ 'is-focused': isFocused, 'is-expanded': isExpanded }">
|
||||
<!-- 输入区域 -->
|
||||
<div class="input-area">
|
||||
<!-- 左侧功能按钮 -->
|
||||
<div class="input-actions left">
|
||||
<!-- 附件按钮 -->
|
||||
<button
|
||||
class="action-btn"
|
||||
:class="{ disabled: !supports_files }"
|
||||
:disabled="!supports_files"
|
||||
:title="supports_files ? '添加附件' : '当前模型不支持文件附件'"
|
||||
@click="supports_files && triggerFileInput()"
|
||||
>
|
||||
<button class="action-btn" :class="{ disabled: !supports_files }" :disabled="!supports_files"
|
||||
:title="supports_files ? '添加附件' : '当前模型不支持文件附件'" @click="supports_files && triggerFileInput()">
|
||||
<Paperclip :size="20" />
|
||||
</button>
|
||||
|
||||
<!-- 图片按钮 -->
|
||||
<button
|
||||
class="action-btn"
|
||||
:class="{ disabled: !supports_vision }"
|
||||
:disabled="!supports_vision"
|
||||
:title="supports_vision ? '添加图片' : '当前模型不支持图片识别'"
|
||||
@click="supports_vision && triggerImageInput()"
|
||||
>
|
||||
<button class="action-btn" :class="{ disabled: !supports_vision }" :disabled="!supports_vision"
|
||||
:title="supports_vision ? '添加图片' : '当前模型不支持图片识别'" @click="supports_vision && triggerImageInput()">
|
||||
<Image :size="20" />
|
||||
</button>
|
||||
|
||||
<!-- 隐藏的文件输入框 -->
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
multiple
|
||||
hidden
|
||||
@change="handleFileSelect"
|
||||
/>
|
||||
<input
|
||||
ref="imageInputRef"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
multiple
|
||||
hidden
|
||||
@change="handleImageSelect"
|
||||
/>
|
||||
<input ref="fileInputRef" type="file" multiple hidden @change="handleFileSelect" />
|
||||
<input ref="imageInputRef" type="file" accept="image/*" multiple hidden @change="handleImageSelect" />
|
||||
</div>
|
||||
|
||||
<!-- 文本输入框 -->
|
||||
<div class="textarea-wrapper">
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
v-model="inputText"
|
||||
:placeholder="placeholder"
|
||||
:rows="1"
|
||||
@beforeinput="handleBeforeInput"
|
||||
@input="autoResize"
|
||||
@focus="isFocused = true"
|
||||
@blur="isFocused = false"
|
||||
@keydown="handleKeydown"
|
||||
@paste="handlePaste"
|
||||
/>
|
||||
<textarea ref="textareaRef" v-model="inputText" :placeholder="placeholder" :rows="1"
|
||||
@beforeinput="handleBeforeInput" @input="autoResize" @focus="isFocused = true" @blur="isFocused = false"
|
||||
@keydown="handleKeydown" @paste="handlePaste" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧功能按钮 -->
|
||||
<div class="input-actions right">
|
||||
<!-- 发送/停止按钮 -->
|
||||
<button
|
||||
v-if="isStreaming"
|
||||
class="action-btn stop"
|
||||
title="停止生成"
|
||||
@click="$emit('stop')"
|
||||
>
|
||||
<StopCircle :size="20" />
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="action-btn send"
|
||||
:class="{ active: canSend, loading: isUploading }"
|
||||
:disabled="!canSend"
|
||||
:title="isUploading ? '上传中...' : '发送消息 (Ctrl+Enter)'"
|
||||
@click="handleSend"
|
||||
>
|
||||
<Loader2 v-if="isUploading" :size="20" class="animate-spin" />
|
||||
<Send v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 底部工具栏 -->
|
||||
<div class="input-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<!-- 展开/收起 -->
|
||||
<button class="toolbar-btn" title="展开输入框" @click="toggleExpand">
|
||||
<!-- <button class="toolbar-btn" title="展开输入框" @click="toggleExpand">
|
||||
<Maximize2 v-if="!isExpanded" :size="16" />
|
||||
<Minimize2 v-else :size="16" />
|
||||
</button>
|
||||
</button> -->
|
||||
<!-- 深度思考开关 -->
|
||||
<button
|
||||
class="toolbar-btn"
|
||||
<button class="toolbar-btn"
|
||||
:class="{ active: isDeepThinking, disabled: isForceDeepThinkingModel || !supports_thinking }"
|
||||
:disabled="isForceDeepThinkingModel || !supports_thinking"
|
||||
:title="isForceDeepThinkingModel ? '当前模型强制开启深度思考' : (supports_thinking ? '深度思考' : '当前模型不支持深度思考')"
|
||||
@click="!isForceDeepThinkingModel && supports_thinking && toggleDeepThink()"
|
||||
>
|
||||
@click="!isForceDeepThinkingModel && supports_thinking && toggleDeepThink()">
|
||||
<Brain :size="16" />
|
||||
<span>深度思考</span>
|
||||
</button>
|
||||
|
||||
<!-- 深度搜索开关 -->
|
||||
<button
|
||||
class="toolbar-btn"
|
||||
:class="{ active: isDeepSearch, disabled: !supports_web_search }"
|
||||
:disabled="!supports_web_search"
|
||||
:title="supports_web_search ? '深度搜索' : '当前模型不支持联网搜索'"
|
||||
@click="supports_web_search && toggleDeepSearch()"
|
||||
>
|
||||
<button class="toolbar-btn" :class="{ active: isDeepSearch, disabled: !supports_web_search }"
|
||||
:disabled="!supports_web_search" :title="supports_web_search ? '深度搜索' : '当前模型不支持联网搜索'"
|
||||
@click="supports_web_search && toggleDeepSearch()">
|
||||
<Sparkles :size="16" />
|
||||
<span>深度搜索</span>
|
||||
</button>
|
||||
|
||||
<!-- 联网搜索开关 -->
|
||||
<button
|
||||
class="toolbar-btn"
|
||||
:class="{ active: isWebSearch, disabled: !supports_web_search }"
|
||||
:disabled="!supports_web_search"
|
||||
:title="supports_web_search ? '联网搜索' : '当前模型不支持联网搜索'"
|
||||
@click="supports_web_search && toggleWebSearch()"
|
||||
>
|
||||
<button class="toolbar-btn" :class="{ active: isWebSearch, disabled: !supports_web_search }"
|
||||
:disabled="!supports_web_search" :title="supports_web_search ? '联网搜索' : '当前模型不支持联网搜索'"
|
||||
@click="supports_web_search && toggleWebSearch()">
|
||||
<Globe :size="16" />
|
||||
<span>联网搜索</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 右侧功能按钮 -->
|
||||
<div class="input-actions right">
|
||||
<!-- 发送/停止按钮 -->
|
||||
<button v-if="isStreaming" class="action-btn stop" title="停止生成" @click="$emit('stop')">
|
||||
<StopCircle :size="20" />
|
||||
</button>
|
||||
<button v-else class="action-btn send" :class="{ active: canSend, loading: isUploading }" :disabled="!canSend"
|
||||
:title="isUploading ? '上传中...' : '发送消息 (Ctrl+Enter)'" @click="handleSend">
|
||||
<Loader2 v-if="isUploading" :size="20" class="animate-spin" />
|
||||
<SendIcon v-else :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -155,6 +100,7 @@ import type { Attachment } from "@/types/chat";
|
|||
import { chatApi } from "@/services/api";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
import SendIcon from "../icons/custom/SendIcon.vue";
|
||||
|
||||
interface AttachmentWithProgress extends Attachment {
|
||||
uploading?: boolean;
|
||||
|
|
@ -562,7 +508,7 @@ onMounted(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.chat-input-container {
|
||||
background: #f3f4f5;
|
||||
background: #F8F9FA;
|
||||
// border: 2px solid #e2e8f0;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
|
|
@ -587,7 +533,8 @@ onMounted(() => {
|
|||
|
||||
.input-area {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
|
@ -614,7 +561,7 @@ onMounted(() => {
|
|||
width: 38px;
|
||||
height: 38px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
border-radius: 50px;
|
||||
background: transparent;
|
||||
color: #6b7280;
|
||||
cursor: pointer;
|
||||
|
|
@ -635,10 +582,10 @@ onMounted(() => {
|
|||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: transparent;
|
||||
color: #6b7280;
|
||||
}
|
||||
// &:hover {
|
||||
// background: transparent;
|
||||
// color: #6b7280;
|
||||
// }
|
||||
}
|
||||
|
||||
&.send {
|
||||
|
|
@ -650,12 +597,12 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
background: #000E32;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||||
// box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -734,11 +681,12 @@ onMounted(() => {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
height: 36px;
|
||||
padding: 10px 15px;
|
||||
border-radius: 50px;
|
||||
background: var(---FFFFFF, #FFF);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: #6b7280;
|
||||
color: var(--6-666666, #666);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
|
@ -754,12 +702,12 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
color: #3b82f6;
|
||||
background: #DFE2E6;
|
||||
// border-color: rgba(59, 130, 246, 0.3);
|
||||
color: #000F33;
|
||||
|
||||
svg {
|
||||
color: #3b82f6;
|
||||
color: #000F33;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -776,10 +724,12 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
<template>
|
||||
<aside
|
||||
class="chat-sidebar"
|
||||
:class="{ collapsed: isCollapsed }"
|
||||
:style="{ width: isCollapsed ? '0px' : `${sidebarWidth}px` }"
|
||||
>
|
||||
<aside class="chat-sidebar" :class="{ collapsed: isCollapsed }"
|
||||
:style="{ width: isCollapsed ? '0px' : `${sidebarWidth}px` }">
|
||||
<div class="sidebar-inner">
|
||||
<!-- 头部 -->
|
||||
<!-- <div class="sidebar-header">
|
||||
|
|
@ -22,45 +19,41 @@
|
|||
|
||||
<!-- 模型选择 -->
|
||||
<div v-show="!isCollapsed" class="model-selector-section">
|
||||
<button class="model-selector" @click="showModelMenu = !showModelMenu">
|
||||
<Sparkles :size="16" />
|
||||
<button class="model-selector" :class="{ 'is-open': showModelMenu }" @click="showModelMenu = !showModelMenu">
|
||||
<span class="model-name-display">{{ currentModel }}</span>
|
||||
<ChevronDown :size="14" />
|
||||
<ChevronDown v-if="!showModelMenu" :size="14" />
|
||||
<ChevronUp v-else :size="14" />
|
||||
</button>
|
||||
|
||||
<Transition name="dropdown">
|
||||
<div v-if="showModelMenu" class="model-menu">
|
||||
<button
|
||||
v-for="model in models"
|
||||
:key="model.id"
|
||||
class="model-option"
|
||||
:class="{ active: model.id === currentModelId }"
|
||||
@click="selectModel(model.id, model.name)"
|
||||
>
|
||||
<button v-for="model in models" :key="model.id" class="model-option"
|
||||
:class="{ active: model.id === currentModelId }" @click="selectModel(model.id, model.name)">
|
||||
<div class="model-info">
|
||||
<span class="model-name">{{ model.name }}</span>
|
||||
<span class="model-desc">{{ model.description }}</span>
|
||||
</div>
|
||||
<Check
|
||||
v-if="model.id === currentModelId"
|
||||
:size="16"
|
||||
class="check-icon"
|
||||
/>
|
||||
<Check v-if="model.id === currentModelId" :size="16" class="check-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<button class="new-chat-btn" @click="handleNewChat">
|
||||
<Plus :size="18" />
|
||||
<span>新对话</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 新建对话按钮 -->
|
||||
<div class="new-chat-section">
|
||||
<!-- <div class="new-chat-section">
|
||||
<button class="new-chat-btn" @click="handleNewChat">
|
||||
<Plus :size="18" />
|
||||
<span>新建对话</span>
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 分享按钮 -->
|
||||
<ShareButton />
|
||||
<!-- <ShareButton /> -->
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<!-- <div class="search-section">
|
||||
|
|
@ -80,19 +73,11 @@
|
|||
<span>置顶</span>
|
||||
</div>
|
||||
<div class="group-list">
|
||||
<ConversationItem
|
||||
v-for="conv in pinnedConversations"
|
||||
:key="conv.id"
|
||||
:conversation="conv"
|
||||
:is-active="conv.id === currentConversationId"
|
||||
:is-select-mode="isSelectMode"
|
||||
:is-selected="isConversationSelected(conv.id)"
|
||||
@select="selectConversation"
|
||||
@delete="deleteConversation"
|
||||
@rename="renameConversation"
|
||||
@toggle-pin="togglePinConversation"
|
||||
@toggle-select="toggleConversationSelection"
|
||||
/>
|
||||
<ConversationItem v-for="conv in pinnedConversations" :key="conv.id" :conversation="conv"
|
||||
:is-active="conv.id === currentConversationId" :is-select-mode="isSelectMode"
|
||||
:is-selected="isConversationSelected(conv.id)" @select="selectConversation" @delete="deleteConversation"
|
||||
@rename="renameConversation" @toggle-pin="togglePinConversation"
|
||||
@toggle-select="toggleConversationSelection" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -103,32 +88,20 @@
|
|||
<span>对话历史</span>
|
||||
</div>
|
||||
<div class="group-list">
|
||||
<ConversationItem
|
||||
v-for="conv in recentConversations"
|
||||
:key="conv.id"
|
||||
:conversation="conv"
|
||||
:is-active="conv.id === currentConversationId"
|
||||
:is-select-mode="isSelectMode"
|
||||
:is-selected="isConversationSelected(conv.id)"
|
||||
@select="selectConversation"
|
||||
@delete="deleteConversation"
|
||||
@rename="renameConversation"
|
||||
@toggle-pin="togglePinConversation"
|
||||
@toggle-select="toggleConversationSelection"
|
||||
/>
|
||||
<ConversationItem v-for="conv in recentConversations" :key="conv.id" :conversation="conv"
|
||||
:is-active="conv.id === currentConversationId" :is-select-mode="isSelectMode"
|
||||
:is-selected="isConversationSelected(conv.id)" @select="selectConversation" @delete="deleteConversation"
|
||||
@rename="renameConversation" @toggle-pin="togglePinConversation"
|
||||
@toggle-select="toggleConversationSelection" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-if="
|
||||
pinnedConversations.length === 0 && recentConversations.length === 0
|
||||
"
|
||||
class="empty-state"
|
||||
>
|
||||
<MessageSquare :size="40" class="empty-icon" />
|
||||
<div v-if="
|
||||
pinnedConversations.length === 0 && recentConversations.length === 0
|
||||
" class="empty-state">
|
||||
<img src="../../assets/无对话历史.png" alt="无对话历史" srcset="">
|
||||
<p>暂无对话</p>
|
||||
<span>点击上方按钮开始新对话</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -156,7 +129,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useChatStore } from "@/stores/chat";
|
||||
import { useSettingsStore } from "@/stores/settings";
|
||||
|
|
@ -166,10 +139,9 @@ import ShareButton from "./ShareButton.vue";
|
|||
import {
|
||||
Plus,
|
||||
Pin,
|
||||
MessageSquare,
|
||||
Sparkles,
|
||||
ChevronDown,
|
||||
Check,
|
||||
ChevronUp,
|
||||
} from "@/components/icons";
|
||||
|
||||
const chatStore = useChatStore();
|
||||
|
|
@ -280,6 +252,12 @@ function handleClickOutside(event: MouseEvent) {
|
|||
if (typeof window !== "undefined") {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -379,6 +357,10 @@ if (typeof window !== "undefined") {
|
|||
}
|
||||
|
||||
.model-selector-section {
|
||||
display: grid;
|
||||
grid-template-columns: 6fr 4fr;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 12px 16px 6px;
|
||||
}
|
||||
|
|
@ -388,12 +370,10 @@ if (typeof window !== "undefined") {
|
|||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 6px 12px;
|
||||
height: 36px;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
background: #f3f4f5;
|
||||
color: #374151;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
|
|
@ -407,12 +387,19 @@ if (typeof window !== "undefined") {
|
|||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
&.is-open {
|
||||
background: var(--000-f-3310, rgba(0, 15, 51, 0.10));
|
||||
}
|
||||
|
||||
svg:first-child {
|
||||
color: #8b5cf6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.model-name-display {
|
||||
text-align: left;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -527,8 +514,9 @@ if (typeof window !== "undefined") {
|
|||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 6px 12px;
|
||||
border-radius: 12px;
|
||||
height: 36px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: #f3f4f5;
|
||||
color: #374151;
|
||||
font-size: 14px;
|
||||
|
|
@ -539,6 +527,7 @@ if (typeof window !== "undefined") {
|
|||
.dark & {
|
||||
background: #2d2d3d;
|
||||
color: #e5e7eb;
|
||||
|
||||
&:hover {
|
||||
background: #0475ed;
|
||||
color: #e5e7eb;
|
||||
|
|
@ -597,6 +586,8 @@ if (typeof window !== "undefined") {
|
|||
|
||||
.conversations-section {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
|
@ -611,8 +602,8 @@ if (typeof window !== "undefined") {
|
|||
gap: 6px;
|
||||
padding: 8px 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #9ca3af;
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
|
|
@ -622,11 +613,12 @@ if (typeof window !== "undefined") {
|
|||
}
|
||||
|
||||
.empty-state {
|
||||
margin: auto 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
padding: 20px 20px 160px;
|
||||
text-align: center;
|
||||
|
||||
.empty-icon {
|
||||
|
|
@ -638,11 +630,17 @@ if (typeof window !== "undefined") {
|
|||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 140px;
|
||||
height: 80px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #6b7280;
|
||||
color: var(--BBBBBB, #BBB);
|
||||
}
|
||||
|
||||
span {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
<template>
|
||||
<div
|
||||
class="conversation-item group"
|
||||
:class="{
|
||||
active: isActive,
|
||||
pinned: conversation.pinned,
|
||||
selected: isSelected,
|
||||
'select-mode': isSelectMode,
|
||||
}"
|
||||
@click="handleClick"
|
||||
@dblclick="handleRename"
|
||||
>
|
||||
<div class="conversation-item group" :class="{
|
||||
active: isActive,
|
||||
pinned: conversation.pinned,
|
||||
selected: isSelected,
|
||||
'select-mode': isSelectMode,
|
||||
}" @click="handleClick" @dblclick="handleRename">
|
||||
<!-- 选择模式复选框 -->
|
||||
<div v-if="isSelectMode" class="item-checkbox" @click.stop="handleToggleSelect">
|
||||
<div class="checkbox" :class="{ checked: isSelected }">
|
||||
|
|
@ -19,7 +14,7 @@
|
|||
|
||||
<!-- 图标 -->
|
||||
<div v-if="!isSelectMode" class="item-icon">
|
||||
<MessageSquare :size="18" />
|
||||
<MessageIcon :size="14" />
|
||||
</div>
|
||||
|
||||
<!-- 内容 -->
|
||||
|
|
@ -27,43 +22,60 @@
|
|||
<div v-if="!isEditing" class="item-title">
|
||||
{{ conversation.title }}
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
ref="inputRef"
|
||||
v-model="editTitle"
|
||||
class="item-title-input"
|
||||
@blur="handleSaveRename"
|
||||
@keydown.enter="handleSaveRename"
|
||||
@keydown.escape="handleCancelRename"
|
||||
@click.stop
|
||||
/>
|
||||
<input v-else ref="inputRef" v-model="editTitle" class="item-title-input" @blur="handleSaveRename"
|
||||
@keydown.enter="handleSaveRename" @keydown.escape="handleCancelRename" @click.stop />
|
||||
<div class="item-meta">
|
||||
<Clock :size="12" />
|
||||
<span>{{ formattedTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 置顶标识 -->
|
||||
<div v-if="conversation.pinned && !isSelectMode" class="pin-indicator">
|
||||
<Pin :size="12" />
|
||||
<PinIcon :size="14" />
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 (非选择模式显示) -->
|
||||
<div v-if="!isSelectMode" class="item-actions" @click.stop>
|
||||
<button
|
||||
class="action-btn"
|
||||
:title="conversation.pinned ? '取消置顶' : '置顶'"
|
||||
@click="handleTogglePin"
|
||||
>
|
||||
<PinOff v-if="conversation.pinned" :size="14" />
|
||||
<Pin v-else :size="14" />
|
||||
</button>
|
||||
<button class="action-btn" title="重命名" @click="handleRename">
|
||||
<Edit3 :size="14" />
|
||||
</button>
|
||||
<button class="action-btn delete" title="删除" @click="handleDelete">
|
||||
<Trash2 :size="14" />
|
||||
</button>
|
||||
<n-tooltip :style="{ borderRadius: '5px', padding: '7px 15px' }">
|
||||
<template #trigger>
|
||||
<button
|
||||
class="action-btn"
|
||||
@click="handleTogglePin"
|
||||
>
|
||||
<!-- TODO: 取消置顶图标 -->
|
||||
<PinOff v-if="conversation.pinned" :size="14" />
|
||||
<PinIcon v-else :size="14" />
|
||||
</button>
|
||||
</template>
|
||||
{{ conversation.pinned ? '取消置顶' : '置顶' }}
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip :style="{ borderRadius: '5px', padding: '7px 15px' }">
|
||||
<template #trigger>
|
||||
<button class="action-btn" @click="handleDelete">
|
||||
<ShareIcon :size="14" />
|
||||
</button>
|
||||
</template>
|
||||
分享
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip :style="{ borderRadius: '5px', padding: '7px 15px' }">
|
||||
<template #trigger>
|
||||
<button class="action-btn" @click="handleRename">
|
||||
<EditIcon :size="14" />
|
||||
</button>
|
||||
</template>
|
||||
重命名
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip :style="{ borderRadius: '5px', padding: '7px 15px' }">
|
||||
<template #trigger>
|
||||
<button class="action-btn delete" @click="handleDelete">
|
||||
<DeleteIcon :size="14" />
|
||||
</button>
|
||||
</template>
|
||||
删除
|
||||
</n-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -71,20 +83,21 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, nextTick } from "vue";
|
||||
import {
|
||||
MessageSquare,
|
||||
Pin,
|
||||
PinOff,
|
||||
Edit3,
|
||||
Trash2,
|
||||
Clock,
|
||||
Check,
|
||||
} from "@/components/icons";
|
||||
import { formatTimestamp } from "@/utils/helpers";
|
||||
import type { Conversation } from "@/types/chat";
|
||||
import { NTooltip } from "naive-ui";
|
||||
import MessageIcon from "../icons/custom/MessageIcon.vue";
|
||||
import PinIcon from "../icons/custom/PinIcon.vue";
|
||||
import EditIcon from "../icons/custom/EditIcon.vue";
|
||||
import DeleteIcon from "../icons/custom/DeleteIcon.vue";
|
||||
import ShareIcon from "../icons/custom/ShareIcon.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
conversation: Conversation;
|
||||
isActive: boolean;
|
||||
isActive: boolean;
|
||||
isSelectMode?: boolean;
|
||||
isSelected?: boolean;
|
||||
}>();
|
||||
|
|
@ -159,10 +172,9 @@ function handleDelete() {
|
|||
margin: 2px 8px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
&:hover:not(.active) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
|
||||
.dark & {
|
||||
|
|
@ -180,14 +192,14 @@ function handleDelete() {
|
|||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
background: #e6e7eb;
|
||||
|
||||
.dark & {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
color: #3b82f6;
|
||||
.item-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
|
@ -18,7 +18,6 @@ export const authService = {
|
|||
* 获取当前用户(预留,目前返回默认用户)
|
||||
*/
|
||||
getCurrentUser(): AuthUser | null {
|
||||
// TODO: 从 token 解析用户信息
|
||||
return { id: 'default' };
|
||||
},
|
||||
|
||||
|
|
@ -47,7 +46,6 @@ export const authService = {
|
|||
* 检查是否已认证(预留,目前始终返回 true)
|
||||
*/
|
||||
isAuthenticated(): boolean {
|
||||
// TODO: 实现真实的认证检查
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ function getToken(): string | null {
|
|||
* body: JSON.stringify({ name: 'John' })
|
||||
* });
|
||||
*/
|
||||
export async function apiRequest(
|
||||
url: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<Response> {
|
||||
const token = getToken();
|
||||
export async function apiRequest(
|
||||
url: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<Response> {
|
||||
const authStore = useAuthStore();
|
||||
const token = getToken();
|
||||
|
||||
// 判断是否为 FormData,不设置 Content-Type 让浏览器自动处理
|
||||
const isFormData = options.body instanceof FormData;
|
||||
|
|
@ -50,12 +51,12 @@ export async function apiRequest(
|
|||
},
|
||||
};
|
||||
|
||||
const response = await fetch(url, config);
|
||||
|
||||
// 401 认证失败提示
|
||||
if (response.status === 401) {
|
||||
window.$toast?.('认证失败,请重新登录', 'error');
|
||||
}
|
||||
const response = await fetch(url, config);
|
||||
|
||||
// 401 认证失败提示
|
||||
if (response.status === 401 && !(import.meta.env.DEV && authStore.isAuthenticated)) {
|
||||
window.$toast?.('认证失败,请重新登录', 'error');
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
|
@ -97,4 +98,4 @@ export function getAuthHeaders(): Record<string, string> {
|
|||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
/**
|
||||
* 用户认证状态管理
|
||||
*/
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { UserInfo } from '@/types/chat';
|
||||
|
||||
// MARK: dev 默认 token(当 URL 无 token 参数时使用)
|
||||
const DEV_DEFAULT_TOKEN = '';
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { UserInfo } from '@/types/chat';
|
||||
|
||||
// 开发环境默认跳过登录校验,避免频繁登录打断调试
|
||||
const DEV_AUTH_BYPASS = import.meta.env.DEV;
|
||||
|
||||
// MARK: dev 默认 token(当 URL 无 token 参数时使用)
|
||||
const DEV_DEFAULT_TOKEN = '';
|
||||
const DEV_BYPASS_USER: UserInfo = {
|
||||
id: 'dev-user',
|
||||
username: 'dev-user',
|
||||
nickname: '开发环境用户',
|
||||
};
|
||||
|
||||
// 认证接口返回格式
|
||||
interface AuthResponse {
|
||||
|
|
@ -18,17 +26,18 @@ interface AuthResponse {
|
|||
}
|
||||
|
||||
// 认证接口
|
||||
const AUTH_CHECK_URL = '/api/auth/check/checkTokenRn';
|
||||
const AUTH_CHECK_URL = '/api/auth/check/checkTokenRn';
|
||||
const AUTH_TOKEN_STORAGE_KEY = 'DEV_DEFAULT_TOKEN';
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// 状态
|
||||
const token = ref<string | null>(null);
|
||||
const user = ref<UserInfo | null>(null);
|
||||
const isInitialized = ref(false);
|
||||
|
||||
// 计算属性
|
||||
const isAuthenticated = computed(() => !!token.value);
|
||||
const userId = computed(() => user.value?.username || null); // username 用于 OSS 路径和数据库 user_id
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// 状态
|
||||
const token = ref<string | null>(null);
|
||||
const user = ref<UserInfo | null>(null);
|
||||
const isInitialized = ref(false);
|
||||
|
||||
// 计算属性
|
||||
const isAuthenticated = computed(() => DEV_AUTH_BYPASS || !!token.value);
|
||||
const userId = computed(() => user.value?.username || null); // username 用于 OSS 路径和数据库 user_id
|
||||
|
||||
/**
|
||||
* 验证 token 并获取用户信息
|
||||
|
|
@ -58,20 +67,27 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
/**
|
||||
* 初始化 - 从 URL 参数获取 token,验证后设置用户信息
|
||||
*/
|
||||
async function init() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const urlToken = searchParams.get('token');
|
||||
async function init() {
|
||||
if (DEV_AUTH_BYPASS) {
|
||||
token.value = null;
|
||||
user.value = DEV_BYPASS_USER;
|
||||
isInitialized.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const urlToken = searchParams.get('token');
|
||||
|
||||
// 获取 token:URL > localStorage > 默认值
|
||||
const tokenValue = urlToken
|
||||
|| localStorage.getItem('DEV_DEFAULT_TOKEN')
|
||||
|| DEV_DEFAULT_TOKEN;
|
||||
|
||||
if (!tokenValue) {
|
||||
isInitialized.value = true;
|
||||
window.$toast?.('未登录,请先登录', 'error');
|
||||
return;
|
||||
}
|
||||
const tokenValue = urlToken
|
||||
|| localStorage.getItem(AUTH_TOKEN_STORAGE_KEY)
|
||||
|| DEV_DEFAULT_TOKEN;
|
||||
|
||||
if (!tokenValue) {
|
||||
isInitialized.value = true;
|
||||
window.$toast?.('未登录,请先登录', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证 token
|
||||
const userInfo = await checkToken(tokenValue);
|
||||
|
|
@ -122,4 +138,4 @@ export const useAuthStore = defineStore('auth', () => {
|
|||
getAuthHeader,
|
||||
init,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ export interface ShareResult {
|
|||
}
|
||||
|
||||
export const useSettingsStore = defineStore("settings", () => {
|
||||
const MIN_SIDEBAR_WIDTH = 310;
|
||||
const MAX_SIDEBAR_WIDTH = 400;
|
||||
|
||||
// 默认设置
|
||||
const defaultSettings: AppSettings = {
|
||||
// 外观设置
|
||||
|
|
@ -88,7 +91,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
// 状态
|
||||
const settings = ref<AppSettings>({ ...defaultSettings });
|
||||
const sidebarCollapsed = ref(false);
|
||||
const sidebarWidth = ref(280);
|
||||
const sidebarWidth = ref(MIN_SIDEBAR_WIDTH);
|
||||
const showShortcutsModal = ref(false);
|
||||
const showSearchModal = ref(false);
|
||||
const showSettingsModal = ref(false);
|
||||
|
|
@ -151,7 +154,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
}
|
||||
|
||||
function setSidebarWidth(width: number) {
|
||||
sidebarWidth.value = Math.max(200, Math.min(400, width));
|
||||
sidebarWidth.value = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, width));
|
||||
saveToStorage();
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +305,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
|
||||
const widthStored = localStorage.getItem("chat-sidebar-width");
|
||||
if (widthStored) {
|
||||
sidebarWidth.value = JSON.parse(widthStored);
|
||||
setSidebarWidth(JSON.parse(widthStored));
|
||||
}
|
||||
|
||||
// 应用主题和字体
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@
|
|||
--chat-sidebar-width: 280px;
|
||||
--chat-input-height: 140px;
|
||||
--header-height: 60px;
|
||||
--app-text-color: #333;
|
||||
--app-font-family: "Microsoft YaHei", sans-serif;
|
||||
--app-font-size: 12px;
|
||||
--app-font-style: normal;
|
||||
--app-font-weight: 400;
|
||||
--app-line-height: normal;
|
||||
}
|
||||
|
||||
// 基础样式重置
|
||||
|
|
@ -44,15 +50,18 @@
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
font-family:
|
||||
"Inter",
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
sans-serif;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
color: var(--app-text-color);
|
||||
font-family: var(--app-font-family);
|
||||
font-size: var(--app-font-size);
|
||||
font-style: var(--app-font-style);
|
||||
font-weight: var(--app-font-weight);
|
||||
line-height: var(--app-line-height);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue