Compare commits
9 Commits
ecaca53dfa
...
995eb85166
Author | SHA1 | Date |
---|---|---|
|
995eb85166 | |
|
f65dd67087 | |
|
9d4d52260f | |
|
ca8532815a | |
|
fcd3cbdee4 | |
|
9f55780796 | |
|
d6decd1979 | |
|
6537775767 | |
|
e6832941d5 |
|
@ -5,11 +5,10 @@
|
|||
## 技术栈
|
||||
|
||||
- Vue 3 - 渐进式 JavaScript 框架
|
||||
- Vite - 下一代前端构建工具
|
||||
- Vue Router - 官方路由管理器
|
||||
- Pinia - Vue 的官方状态管理库
|
||||
- TypeScript - JavaScript 的超集
|
||||
- Tailwind CSS - 实用优先的 CSS 框架
|
||||
- Element Plus - 基于 Vue 3 的组件库
|
||||
- Vite - 下一代前端构建工具
|
||||
- Sass - CSS 预处理器
|
||||
|
||||
## 功能特性
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
||||
<title>对话</title>
|
||||
</head>
|
||||
|
|
20
package.json
20
package.json
|
@ -4,8 +4,8 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --mode development",
|
||||
"dev:test": "vite --mode test",
|
||||
"dev": "vite --mode development --host",
|
||||
"dev:prod": "vite --mode production --host",
|
||||
"build": "vite build",
|
||||
"build:dev": "vite build --mode development",
|
||||
"build:prod": "vite build --mode production",
|
||||
|
@ -48,9 +48,19 @@
|
|||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"sass": "^1.86.3",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@types/node": "^20.11.19",
|
||||
"@vue/compiler-sfc": "^3.4.21",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-import": "^16.0.1",
|
||||
"postcss-preset-env": "^9.3.0",
|
||||
"sass": "^1.71.1",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"vite": "^6.2.0"
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.4",
|
||||
"vite-plugin-mkcert": "^1.17.8",
|
||||
"vite-plugin-vue-devtools": "^7.0.19",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
export default {
|
||||
plugins: {
|
||||
'postcss-import': {},
|
||||
'postcss-preset-env': {
|
||||
stage: 3,
|
||||
features: {
|
||||
'nesting-rules': true
|
||||
}
|
||||
},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="169" height="51" viewBox="0 0 169 51" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M81.76 17.5466C79.9529 15.6083 77.7663 14.0627 75.3361 13.0059C72.906 11.9491 70.2844 11.4037 67.6344 11.4037C64.9844 11.4037 62.3628 11.9491 59.9327 13.0059C57.5025 14.0627 55.3159 15.6083 53.5088 17.5466C50.1929 21.2167 48.3801 26.0007 48.4312 30.9467C48.4823 35.8926 50.3936 40.6381 53.7847 44.2389C55.5931 46.0736 57.7481 47.5306 60.1245 48.525C62.501 49.5195 65.0514 50.0316 67.6275 50.0316C70.2036 50.0316 72.754 49.5195 75.1304 48.525C77.5069 47.5306 79.6619 46.0736 81.4703 44.2389C84.8662 40.633 86.7812 35.8815 86.8349 30.9286C86.8885 25.9757 85.0769 21.1838 81.76 17.5052V17.5466ZM67.6206 18.5536C69.1876 18.5355 70.7408 18.848 72.1788 19.4707C73.6169 20.0935 74.9074 21.0125 75.9663 22.1677C77.1207 23.3101 78.0294 24.6765 78.6366 26.1828C79.2439 27.6891 79.5369 29.3036 79.4977 30.9272C79.5096 33.0868 78.9581 35.2121 77.8975 37.0934C76.8989 38.9229 75.4143 40.441 73.6074 41.48C71.7797 42.4933 69.7242 43.025 67.6344 43.025C65.5446 43.025 63.4891 42.4933 61.6614 41.48C59.8361 40.4511 58.3356 38.9315 57.3299 37.0934C56.2651 35.2138 55.7132 33.0874 55.7297 30.9272C55.6917 29.3062 55.9826 27.6943 56.5848 26.1887C57.187 24.6832 58.0881 23.3154 59.2335 22.1677C60.297 21.0072 61.5943 20.0852 63.0399 19.4622C64.4855 18.8393 66.0467 18.5296 67.6206 18.5536V18.5536Z" fill="#A4CE8A"/>
|
||||
<path d="M160.596 35.7139C159.687 37.375 158.528 38.887 157.161 40.1971C156.059 41.1327 154.791 41.8533 153.423 42.3214C151.967 42.8683 150.425 43.1487 148.87 43.1491C147.374 43.1859 145.886 42.9063 144.505 42.3287C143.124 41.7511 141.881 40.8884 140.856 39.797C138.949 37.8115 137.785 35.2295 137.559 32.486H168.114V31.1065C168.193 26.5874 166.733 22.1756 163.975 18.5949C162.215 16.2968 159.934 14.4503 157.32 13.2078C154.705 11.9654 151.833 11.3626 148.939 11.4494C146.119 11.3651 143.32 11.9558 140.774 13.1723C138.228 14.3888 136.01 16.1959 134.303 18.4432C131.398 22.1416 129.882 26.7426 130.02 31.444C130.159 36.1455 131.943 40.6491 135.062 44.1699C136.822 46.1556 139.004 47.7216 141.449 48.7529C143.893 49.7841 146.538 50.2544 149.188 50.1291C151.662 50.1596 154.121 49.7389 156.444 48.8876C158.592 48.0633 160.563 46.8366 162.251 45.2734C164.11 43.503 165.637 41.4148 166.762 39.1073L167.41 37.8382L161.244 34.5965L160.596 35.7139ZM159.892 25.8508H138.469C139.101 24.1268 140.132 22.5765 141.477 21.3262C143.566 19.4332 146.3 18.4115 149.119 18.4707C150.915 18.4687 152.69 18.8594 154.319 19.6157C155.833 20.2951 157.169 21.3148 158.223 22.5953C158.955 23.581 159.519 24.681 159.892 25.8508V25.8508Z" fill="#A4CE8A"/>
|
||||
<path d="M123.405 20.2917C122.394 17.8965 120.689 15.8593 118.508 14.4428C116.26 12.983 113.629 12.2243 110.949 12.2633C108.289 12.2483 105.67 12.908 103.334 14.1807C101.981 14.9318 100.733 15.8596 99.6237 16.9396V0H92.354V50.074H99.6237V36.1416C99.5151 33.1638 99.7092 30.1827 100.203 27.2441C100.766 24.9664 102.054 22.9331 103.872 21.4504C105.587 19.955 107.791 19.1403 110.066 19.1605C111.75 19.0711 113.414 19.5576 114.784 20.54C116.057 21.6551 116.917 23.1664 117.225 24.8301C117.627 27.3147 117.775 29.8336 117.667 32.3481V50.074H124.923V31.1066C124.923 26.2233 124.44 22.6781 123.405 20.2917Z" fill="#45886C"/>
|
||||
<path d="M26.0716 1.15869H23.1196L0 50.074H8.33187L24.568 15.7671L40.7351 50.074H48.8877L26.0716 1.15869Z" fill="#45886C"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,8 @@
|
|||
<svg width="1931" height="978" viewBox="0 0 1931 978" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M355.325 541.386H318.177C282.033 387.562 263.966 230.046 264.34 72.034C264.34 66.758 264.34 61.2666 264.34 56.4213L272.524 21.5348L247.651 0L221.809 21.5348L229.562 56.4213C229.562 61.5896 229.562 67.1887 229.562 72.034C229.936 230.046 211.868 387.562 175.724 541.386H139.115L148.483 588.009H164.526C160.327 603.298 156.128 618.48 151.713 633.447H215.349C217.825 618.373 220.302 603.191 222.563 588.009H272.631C274.892 603.191 277.369 618.373 279.845 633.447H343.05C338.635 618.48 334.436 603.298 330.345 588.009H346.28L355.325 541.386ZM228.7 541.386C237.745 471.074 243.99 398.394 247.651 324.207C250.773 398.502 257.018 471.074 266.171 541.386H228.7Z" fill="#A4CE8A"/>
|
||||
<path d="M367.169 977.143H494.332C462.616 926.646 434.654 873.887 410.669 819.293H442.218L452.232 762.333H387.627C373.091 725.401 359.632 686.961 347.465 647.014H282.86C289.751 686.315 297.504 724.862 306.118 762.333H189.184C197.905 724.862 205.658 686.315 212.549 647.014H147.944C135.777 686.853 122.318 725.401 107.782 762.333H42.3159L52.1143 819.293H83.7705C59.7079 873.869 31.7107 926.625 0 977.143H127.271C145.073 926.895 161.116 874.278 175.401 819.293H318.931C333.287 874.206 349.367 926.823 367.169 977.143Z" fill="#45886C"/>
|
||||
<path d="M1256.67 723.247C1242.56 708.118 1225.49 696.054 1206.52 687.805C1187.56 679.556 1167.09 675.299 1146.41 675.299C1125.72 675.299 1105.26 679.556 1086.29 687.805C1067.32 696.054 1050.25 708.118 1036.15 723.247C1010.27 751.895 996.116 789.237 996.515 827.843C996.914 866.449 1011.83 903.491 1038.3 931.597C1052.42 945.918 1069.24 957.29 1087.79 965.053C1106.34 972.815 1126.25 976.812 1146.35 976.812C1166.46 976.812 1186.37 972.815 1204.92 965.053C1223.47 957.29 1240.29 945.918 1254.4 931.597C1280.91 903.451 1295.86 866.363 1296.28 827.702C1296.7 789.042 1282.56 751.638 1256.67 722.924V723.247ZM1146.3 731.108C1158.53 730.966 1170.65 733.406 1181.88 738.267C1193.1 743.128 1203.18 750.301 1211.44 759.318C1220.45 768.236 1227.55 778.9 1232.29 790.658C1237.03 802.416 1239.31 815.018 1239.01 827.691C1239.1 844.548 1234.8 861.138 1226.52 875.822C1218.72 890.102 1207.13 901.952 1193.03 910.062C1178.76 917.972 1162.72 922.122 1146.41 922.122C1130.09 922.122 1114.05 917.972 1099.78 910.062C1085.54 902.031 1073.82 890.169 1065.97 875.822C1057.66 861.151 1053.36 844.553 1053.48 827.691C1053.19 815.038 1055.46 802.456 1060.16 790.705C1064.86 778.953 1071.89 768.276 1080.83 759.318C1089.13 750.26 1099.26 743.063 1110.54 738.2C1121.83 733.338 1134.01 730.921 1146.3 731.108Z" fill="#A4CE8A"/>
|
||||
<path d="M1872.02 865.054C1864.93 878.02 1855.88 889.822 1845.21 900.048C1836.61 907.352 1826.71 912.976 1816.03 916.63C1804.67 920.899 1792.64 923.087 1780.5 923.091C1768.82 923.378 1757.21 921.195 1746.43 916.687C1735.65 912.178 1725.94 905.444 1717.94 896.926C1703.06 881.427 1693.97 861.273 1692.21 839.858H1930.71V829.091C1931.32 793.817 1919.93 759.38 1898.4 731.43C1884.67 713.492 1866.86 699.079 1846.45 689.381C1826.04 679.683 1803.62 674.978 1781.04 675.655C1759.02 674.998 1737.17 679.608 1717.3 689.104C1697.43 698.6 1680.11 712.705 1666.8 730.246C1644.11 759.115 1632.28 795.028 1633.36 831.725C1634.45 868.423 1648.37 903.577 1672.72 931.058C1686.45 946.558 1703.49 958.781 1722.57 966.831C1741.65 974.881 1762.29 978.552 1782.98 977.574C1802.29 977.811 1821.48 974.527 1839.61 967.883C1856.38 961.449 1871.77 951.874 1884.94 939.672C1899.45 925.853 1911.38 909.553 1920.15 891.542L1925.21 881.636L1877.08 856.333L1872.02 865.054ZM1866.53 788.067H1699.31C1704.25 774.61 1712.29 762.509 1722.79 752.75C1739.09 737.974 1760.44 729.999 1782.44 730.461C1796.46 730.445 1810.31 733.495 1823.03 739.398C1834.84 744.702 1845.27 752.661 1853.5 762.656C1859.22 770.35 1863.62 778.936 1866.53 788.067Z" fill="#A4CE8A"/>
|
||||
<path d="M1581.73 744.675C1573.84 725.979 1560.53 710.077 1543.51 699.021C1525.96 687.626 1505.42 681.704 1484.5 682.008C1463.74 681.891 1443.3 687.04 1425.07 696.975C1414.5 702.838 1404.76 710.079 1396.1 718.51V586.286H1339.36V977.143H1396.1V868.392C1395.26 845.149 1396.77 821.879 1400.63 798.942C1405.02 781.163 1415.07 765.293 1429.27 753.719C1442.65 742.046 1459.85 735.687 1477.61 735.845C1490.75 735.147 1503.74 738.945 1514.44 746.613C1524.37 755.317 1531.08 767.113 1533.5 780.099C1536.63 799.493 1537.78 819.155 1536.94 838.782V977.143H1593.58V829.091C1593.58 790.974 1589.81 763.302 1581.73 744.675Z" fill="#45886C"/>
|
||||
<path d="M821.985 595.33H798.942L618.48 977.143H683.516L810.248 709.357L936.442 977.143H1000.08L821.985 595.33Z" fill="#45886C"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 676 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
Before Width: | Height: | Size: 1.5 KiB |
68
src/App.vue
68
src/App.vue
|
@ -1,40 +1,58 @@
|
|||
<script setup>
|
||||
import { RouterView } from "vue-router";
|
||||
import { onMounted } from 'vue'
|
||||
onMounted(() => {
|
||||
import { useSettingsStore } from '@/store/settings';
|
||||
import { setTheme } from '@/styles/theme';
|
||||
|
||||
})
|
||||
onMounted(() => {
|
||||
//首先我们获得视口高度并将其乘以1%以获得1vh单位的值
|
||||
let vh = window.innerHeight * 0.01
|
||||
// 然后,我们将——vh自定义属性中的值设置为文档的根
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`)
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
// 我们监听resize事件 视图大小发生变化就重新计算1vh的值
|
||||
window.addEventListener('resize', () => {
|
||||
// 我们执行与前面相同的脚本
|
||||
let vh = window.innerHeight * 0.01
|
||||
console.log(vh);
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`)
|
||||
})
|
||||
})
|
||||
onMounted(() => {
|
||||
// 应用启动时,根据 store 中持久化的主题设置样式
|
||||
setTheme(settingsStore.theme);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
<style lang="scss">
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '\5FAE\8F6F\96C5\9ED1', Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: var(--el-bg-color);
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px; /* 滚动条宽度 */
|
||||
height: 6px; /* 滚动条高度,主要用于水平滚动条 */
|
||||
}
|
||||
.logo.vue:hover {
|
||||
filter: drop-shadow(0 0 2em #42b883aa);
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent; /* 滚动条轨道背景 */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(144, 147, 153, 0.3); /* 滚动条滑块颜色 */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(144, 147, 153, 0.5); /* 悬停时滑块颜色 */
|
||||
}
|
||||
|
||||
// 全局动画控制
|
||||
body.no-animations {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,43 +0,0 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Learn more about IDE Support for Vue in the
|
||||
<a
|
||||
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
||||
target="_blank"
|
||||
>Vue Docs Scaling up Guide</a
|
||||
>.
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
|
@ -1,90 +0,0 @@
|
|||
<template>
|
||||
<el-dropdown
|
||||
trigger="click"
|
||||
@command="handleCommand"
|
||||
class="mode-setting"
|
||||
>
|
||||
<el-button
|
||||
class="setting-btn"
|
||||
:icon="Setting"
|
||||
text
|
||||
bg
|
||||
@click.stop
|
||||
/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<slot>
|
||||
<!-- 默认选项 -->
|
||||
<el-dropdown-item command="edit">
|
||||
<el-icon><Edit /></el-icon>
|
||||
编辑设置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="reset">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
重置设置
|
||||
</el-dropdown-item>
|
||||
</slot>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Setting, Edit, RefreshRight } from '@element-plus/icons-vue'
|
||||
|
||||
const emit = defineEmits(['command'])
|
||||
|
||||
const handleCommand = (command) => {
|
||||
emit('command', command)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mode-setting {
|
||||
.setting-btn {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
opacity: 0;
|
||||
transform: translateX(10px);
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
padding: 8px 16px;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在深色模式下的样式
|
||||
.mode-item.active {
|
||||
.setting-btn {
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div>
|
||||
<h4>整体外观</h4>
|
||||
<el-radio-group
|
||||
v-model="currentDisplayMode"
|
||||
@change="changeDisplayMode"
|
||||
size="small"
|
||||
class="display-mode-group"
|
||||
>
|
||||
<el-radio-button label="system">
|
||||
<span class="icon system"></span> 默认
|
||||
</el-radio-button>
|
||||
<el-radio-button label="light">
|
||||
<span class="icon light"></span> 浅色
|
||||
</el-radio-button>
|
||||
<el-radio-button label="dark">
|
||||
<span class="icon dark"></span> 深色
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useSettingsStore } from '@/store/settings';
|
||||
import { ElRadioGroup, ElRadioButton } from 'element-plus';
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const currentDisplayMode = ref(settingsStore.displayMode);
|
||||
|
||||
const changeDisplayMode = (newMode) => {
|
||||
settingsStore.updateDisplayMode(newMode);
|
||||
currentDisplayMode.value = newMode;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.display-mode-group {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
gap: 8px;
|
||||
.el-radio-button {
|
||||
flex: 1;
|
||||
:deep(.el-radio-button__inner) {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px !important;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: none;
|
||||
padding: 8px 5px;
|
||||
font-size: 12px;
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
|
||||
&.system { background: linear-gradient(135deg, #fff 50%, #222 50%); border-color: #888; }
|
||||
&.light { background-color: #fff; }
|
||||
&.dark { background-color: #222; border-color: #555; }
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active :deep(.el-radio-button__inner) {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.el-radio-button + .el-radio-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<div>
|
||||
<h4>界面设置</h4>
|
||||
<div class="setting-item">
|
||||
<span>启用动画效果</span>
|
||||
<el-switch
|
||||
v-model="animationsEnabled"
|
||||
@change="toggleAnimationsSetting"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useSettingsStore } from '@/store/settings';
|
||||
import { ElSwitch } from 'element-plus';
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const animationsEnabled = ref(settingsStore.animationsEnabled);
|
||||
|
||||
const toggleAnimationsSetting = (value) => {
|
||||
settingsStore.toggleAnimations(value);
|
||||
animationsEnabled.value = value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<div class="settings-popover">
|
||||
<DisplayModeSettings />
|
||||
<el-divider />
|
||||
<ThemeSettings />
|
||||
<el-divider />
|
||||
<InterfaceSettings />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ElDivider } from 'element-plus';
|
||||
import DisplayModeSettings from './DisplayModeSettings.vue';
|
||||
import ThemeSettings from './ThemeSettings.vue';
|
||||
import InterfaceSettings from './InterfaceSettings.vue';
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-popover {
|
||||
padding: 15px;
|
||||
min-width: 240px;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
margin: 18px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div>
|
||||
<h4>主题</h4>
|
||||
<div class="theme-grid">
|
||||
<div
|
||||
v-for="(theme, name) in filteredThemes"
|
||||
:key="name"
|
||||
class="theme-option"
|
||||
:class="{ 'active': currentTheme === name }"
|
||||
@click="changeTheme(name)"
|
||||
>
|
||||
<div class="theme-preview" :style="{ backgroundColor: theme.primary }"></div>
|
||||
<span class="theme-label">{{ getThemeLabel(name) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useSettingsStore } from '@/store/settings';
|
||||
import { themes as availableThemes } from '@/styles/theme';
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const currentTheme = ref(settingsStore.theme);
|
||||
|
||||
const filteredThemes = computed(() => {
|
||||
const { dark, ...rest } = availableThemes;
|
||||
return rest;
|
||||
});
|
||||
|
||||
const themeLabels = {
|
||||
default: '默认',
|
||||
fresh: '清新',
|
||||
warm: '暖色',
|
||||
business: '商务',
|
||||
};
|
||||
|
||||
const getThemeLabel = (themeName) => {
|
||||
return themeLabels[themeName] || themeName;
|
||||
};
|
||||
|
||||
const changeTheme = (newTheme) => {
|
||||
settingsStore.updateTheme(newTheme);
|
||||
currentTheme.value = newTheme;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.theme-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.theme-option {
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent;
|
||||
padding: 4px;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-border-color-hover);
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.theme-preview {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.theme-label {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-regular);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="main-layout">
|
||||
<Sidebar class="sidebar" />
|
||||
<!-- <Sidebar class="sidebar" /> -->
|
||||
<main class="content-area">
|
||||
<!-- 路由视图将渲染在这里 -->
|
||||
<router-view />
|
||||
|
|
26
src/main.js
26
src/main.js
|
@ -1,21 +1,39 @@
|
|||
import { createApp } from "vue";
|
||||
import "./style.css";
|
||||
import "@/styles/style.css";
|
||||
// 使用自定义主题
|
||||
import '@/styles/element-variables.scss'
|
||||
import App from "./App.vue";
|
||||
import {router} from "@/router/index";
|
||||
import { createPinia } from "pinia";
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import { useSettingsStore, applyDisplayMode } from '@/store/settings'; // Import store and helper
|
||||
import { setTheme } from '@/styles/theme'; // Import setTheme
|
||||
|
||||
// 引入 Element Plus
|
||||
import ElementPlus from 'element-plus'
|
||||
// 使用自定义主题
|
||||
import './styles/element-variables.scss'
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(pinia);
|
||||
app.use(pinia); // Pinia is now available
|
||||
|
||||
// Access the store *after* app.use(pinia)
|
||||
// The store state will be hydrated from localStorage here by the persistence plugin
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
// Apply persisted/initial settings
|
||||
applyDisplayMode(settingsStore.displayMode);
|
||||
setTheme(settingsStore.theme);
|
||||
// Apply initial animation setting
|
||||
if (!settingsStore.animationsEnabled) {
|
||||
document.body.classList.add('no-animations');
|
||||
} else {
|
||||
// Ensure the class is not present if animations are enabled initially
|
||||
document.body.classList.remove('no-animations');
|
||||
}
|
||||
|
||||
app.use(router);
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
|
|
|
@ -18,17 +18,11 @@ const routes = [
|
|||
},
|
||||
{
|
||||
path: "/chat",
|
||||
component: () => import("@/layouts/MainLayout.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "chat",
|
||||
meta: {
|
||||
icon: ChatRound
|
||||
},
|
||||
component: () => import("@/views/chat/index.vue"),
|
||||
},
|
||||
],
|
||||
name: "chat",
|
||||
meta: {
|
||||
icon: ChatRound
|
||||
},
|
||||
component: () => import("@/views/chat/index.vue"),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -8,39 +8,44 @@ const chatModes = {
|
|||
token: 'app-88ae2GN49aUyNO6qGg7tbTfX',
|
||||
background: `# 客户信息
|
||||
### **基础背景与性格设定**
|
||||
|
||||
**姓名**:张伟
|
||||
|
||||
**年龄**:34 岁
|
||||
|
||||
**职业**:腾讯员工
|
||||
|
||||
**家庭**:妻子为全职太太,女儿 9 岁,双方父母健在
|
||||
|
||||
**性格**:冷静、理性、务实,偏好客观数据和逻辑分析,对保险持观望态度,防备心较强
|
||||
|
||||
**近期状态**:
|
||||
|
||||
- 体检显示中度脂肪肝,担心投保问题
|
||||
|
||||
- 已为全家配置百万医疗险,妻子有重疾险,但认为保障不足 (保费 50W)
|
||||
|
||||
- 对理财型保险收益不认可,但对补充重疾险有潜在需求 `,
|
||||
chatBackground: '通过顾先生介绍,你和他的老同学在他的家里首次面谈。他的家中布置简洁,茶几上摆放着一套茶具。',
|
||||
},
|
||||
quote_objection: {
|
||||
name: '报价中异议',
|
||||
icon: '💬',
|
||||
token: 'app-88ae2GN49aUyNO6qGg7tbTfX',
|
||||
background: '',
|
||||
chatBackground: '',
|
||||
token: 'app-ur2Altw2LHR6niX8Q1S7Cn41',
|
||||
background: `# 客户信息
|
||||
### **基础背景与性格设定**
|
||||
**姓名**: 王力
|
||||
**年龄**: 40岁
|
||||
**职业**: 私营企业主
|
||||
**车牌号**: 闽C12345
|
||||
**车辆信息**:拥有一辆行驶4年的大众途观L,购买价格约25万,主要用于商务出行及家庭使用。
|
||||
**投保信息**:在其他保司购买交强险及商业险(三者险、车损险),还有1个月到期。有过一次轻微追尾事故,已通过保司处理。王先生对成本控制非常敏感,但也能意识到保险的重要性。他希望得到保障全面、价格合理的产品和优质的服务。`,
|
||||
chatBackground: '现在你将扮演坐席专员,与系统扮演的客户针对报价中的各类异议开展对练,着重训练处理“报价中异议”的能力。按照“保全保足”原则给出险种推荐方案。',
|
||||
},
|
||||
post_quote_objection: {
|
||||
name: '报价后异议',
|
||||
icon: '💰',
|
||||
token: 'app-88ae2GN49aUyNO6qGg7tbTfX',
|
||||
background: '',
|
||||
chatBackground: '',
|
||||
token: 'app-Yiccl0JoXs2QF2lkHxO6f822',
|
||||
background: `# 客户信息
|
||||
### **基础背景与性格设定**
|
||||
**姓名**: 张琳女士
|
||||
**年龄**: 32岁
|
||||
**职业**: 公司行政主管
|
||||
**车牌号**: 粤B56789
|
||||
**车辆信息**:拥有一辆行驶4年多的本田思域,1.5T,CVT燃动版,购买价格约15万,用于日常通勤及周末短途出行。
|
||||
**投保信息**:在其他保司购买交强险及商业险(三者险、车损险、不计免赔险、车上人员责任险),还有2个月到期。无事故记录,仅有两次违章停车记录。张女士注重性价比和服务质量,希望保险的保障全面、价格合理、服务贴心。`,
|
||||
chatBackground: '现在你将扮演坐席专员,与系统扮演的客户针对报价后的各类异议开展对练,着重训练处理“报价后异议”的能力。按照“保全保足”原则给出险种推荐方案。',
|
||||
}
|
||||
}
|
||||
// 返回示例
|
||||
|
|
|
@ -1,34 +1,40 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { setTheme, themes } from '@/styles/theme'
|
||||
|
||||
// Helper function to apply display mode
|
||||
export function applyDisplayMode(mode) {
|
||||
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
let darkModeEnabled = false;
|
||||
|
||||
if (mode === 'dark') {
|
||||
darkModeEnabled = true;
|
||||
} else if (mode === 'system') {
|
||||
darkModeEnabled = prefersDark;
|
||||
} // else mode === 'light', darkModeEnabled remains false
|
||||
|
||||
if (darkModeEnabled) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
export const useSettingsStore = defineStore('settings', {
|
||||
state: () => ({
|
||||
// 侧边栏状态
|
||||
sidebarCollapsed: false,
|
||||
|
||||
// 界面配置
|
||||
interface: {
|
||||
// 是否显示时间戳
|
||||
showTimestamp: true,
|
||||
// 消息气泡最大宽度(px)
|
||||
messageMaxWidth: 800,
|
||||
// 字体大小(px)
|
||||
fontSize: 14
|
||||
},
|
||||
|
||||
// 主题配置
|
||||
theme: {
|
||||
// 主色调
|
||||
primary: '#07c160',
|
||||
// 背景色
|
||||
background: '#f5f5f5',
|
||||
// 文字颜色
|
||||
textColor: '#333333'
|
||||
}
|
||||
// 显示模式: 'light', 'dark', 'system'
|
||||
displayMode: 'system', // Default to system preference
|
||||
// 主题配置 (e.g., 'default', 'fresh', 'warm')
|
||||
theme: 'default',
|
||||
// 动画效果开关
|
||||
animationsEnabled: true,
|
||||
}),
|
||||
|
||||
persist: {
|
||||
key: 'settings',
|
||||
paths: ['sidebarCollapsed']
|
||||
// Persist displayMode and theme
|
||||
paths: ['sidebarCollapsed', 'displayMode', 'theme', 'animationsEnabled']
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -37,25 +43,54 @@ export const useSettingsStore = defineStore('settings', {
|
|||
this.sidebarCollapsed = !this.sidebarCollapsed
|
||||
},
|
||||
|
||||
// 更新界面配置
|
||||
updateInterface(config) {
|
||||
this.interface = {
|
||||
...this.interface,
|
||||
...config
|
||||
// 切换动画状态
|
||||
toggleAnimations(enabled) {
|
||||
if (typeof enabled === 'boolean') {
|
||||
this.animationsEnabled = enabled;
|
||||
} else {
|
||||
this.animationsEnabled = !this.animationsEnabled;
|
||||
}
|
||||
// Apply animation class to body
|
||||
if (this.animationsEnabled) {
|
||||
document.body.classList.remove('no-animations');
|
||||
} else {
|
||||
document.body.classList.add('no-animations');
|
||||
}
|
||||
},
|
||||
|
||||
// 更新主题配置
|
||||
updateTheme(config) {
|
||||
this.theme = {
|
||||
...this.theme,
|
||||
...config
|
||||
// 更新显示模式
|
||||
updateDisplayMode(newMode) {
|
||||
if (['light', 'dark', 'system'].includes(newMode)) {
|
||||
this.displayMode = newMode;
|
||||
applyDisplayMode(newMode); // Apply the change immediately
|
||||
// Optional: Re-apply theme colors if they differ significantly in dark mode
|
||||
// setTheme(this.theme); // Uncomment if needed
|
||||
} else {
|
||||
console.warn(`Invalid display mode: ${newMode}`);
|
||||
}
|
||||
},
|
||||
|
||||
// 更新主题颜色配置
|
||||
updateTheme(newTheme) {
|
||||
if (themes.hasOwnProperty(newTheme)) { // Check if the theme exists
|
||||
this.theme = newTheme;
|
||||
setTheme(newTheme); // Apply the theme colors
|
||||
} else {
|
||||
console.warn(`Invalid theme: ${newTheme}`);
|
||||
}
|
||||
},
|
||||
|
||||
// 重置所有设置
|
||||
resetSettings() {
|
||||
this.$reset()
|
||||
// Re-apply settings after reset
|
||||
setTheme(this.theme);
|
||||
applyDisplayMode(this.displayMode);
|
||||
this.toggleAnimations(this.animationsEnabled); // Re-apply animation class
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// --- Helper Function to apply animation class ---
|
||||
// Moved applying logic into the toggleAnimations action directly
|
||||
// export function applyAnimationSetting(enabled) { ... }
|
|
@ -1,3 +1,22 @@
|
|||
/* 基础样式 */
|
||||
:root {
|
||||
--primary-color: #409eff;
|
||||
--success-color: #67c23a;
|
||||
--warning-color: #e6a23c;
|
||||
--danger-color: #f56c6c;
|
||||
--info-color: #909399;
|
||||
}
|
||||
|
||||
/* 全局样式 */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
|
@ -58,10 +77,6 @@ button:focus-visible {
|
|||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
/* max-width: 100vh; */
|
||||
height: 100%;
|
|
@ -0,0 +1,38 @@
|
|||
// 使用 import.meta.glob 动态导入 themes 目录下的所有 .js 文件
|
||||
// { eager: true } 表示同步导入,立即获取模块内容
|
||||
const themeModules = import.meta.glob('./themes/*.js', { eager: true });
|
||||
|
||||
// 构建 themes 对象
|
||||
export const themes = {};
|
||||
for (const path in themeModules) {
|
||||
// 从路径中提取主题名称 (e.g., './themes/default.js' -> 'default')
|
||||
const themeName = path.replace('./themes/', '').replace('.js', '');
|
||||
// 获取模块的默认导出 (即主题配置对象)
|
||||
if (themeModules[path].default) {
|
||||
themes[themeName] = themeModules[path].default;
|
||||
}
|
||||
}
|
||||
|
||||
// 主题切换函数
|
||||
export function setTheme(themeName) {
|
||||
const theme = themes[themeName] || themes.default // Fallback to default
|
||||
if (!theme) {
|
||||
console.error(`Theme "${themeName}" not found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const root = document.documentElement
|
||||
|
||||
// 设置主题色变量
|
||||
Object.keys(theme).forEach(key => {
|
||||
root.style.setProperty(`--el-color-${key}`, theme[key])
|
||||
})
|
||||
|
||||
// 设置主题类名
|
||||
// 查找 body 上是否已有 theme- 开头的类
|
||||
const currentThemeClass = Array.from(document.body.classList).find(cls => cls.startsWith('theme-'));
|
||||
if (currentThemeClass) {
|
||||
document.body.classList.remove(currentThemeClass);
|
||||
}
|
||||
document.body.classList.add(`theme-${themeName}`);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
primary: '#2C3E50',
|
||||
success: '#27AE60',
|
||||
warning: '#F39C12',
|
||||
danger: '#E74C3C',
|
||||
info: '#3498DB'
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
primary: '#5CACEE',
|
||||
success: '#85CE61',
|
||||
warning: '#E6A23C',
|
||||
danger: '#F78989',
|
||||
info: '#A6A9AD'
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
primary: '#409EFF',
|
||||
success: '#67C23A',
|
||||
warning: '#E6A23C',
|
||||
danger: '#F56C6C',
|
||||
info: '#909399'
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
primary: '#36D1DC',
|
||||
success: '#5CB85C',
|
||||
warning: '#F0AD4E',
|
||||
danger: '#D9534F',
|
||||
info: '#5BC0DE'
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
primary: '#FF6B6B',
|
||||
success: '#4ECDC4',
|
||||
warning: '#FFE66D',
|
||||
danger: '#FF6B6B',
|
||||
info: '#45B7D1'
|
||||
};
|
|
@ -3,13 +3,13 @@
|
|||
<div class="header-content">
|
||||
<div class="title">
|
||||
<el-button
|
||||
class="mobile-sidebar-toggle"
|
||||
class="desktop-sidebar-toggle"
|
||||
:icon="Expand"
|
||||
@click="settingsStore.toggleSidebar"
|
||||
text
|
||||
v-if="isMobile"
|
||||
/>
|
||||
<span class="title-text">{{ title }}</span>
|
||||
|
||||
<el-tag v-if="chatStore.currentConversation?.conversationStatus === 'typing'"
|
||||
size="small"
|
||||
class="status-tag"
|
||||
|
@ -17,9 +17,9 @@
|
|||
>
|
||||
对话中
|
||||
</el-tag>
|
||||
<HistoryButton v-if="settingsStore.sidebarCollapsed" :icon-only="false" class="title-history-btn" />
|
||||
</div>
|
||||
<div class="actions">
|
||||
<HistoryButton :icon-only="false" class="title-history-btn" />
|
||||
<el-tooltip content="删除会话" placement="bottom" v-if="chatStore.conversationId">
|
||||
<el-button
|
||||
class="action-btn delete-btn"
|
||||
|
@ -29,7 +29,7 @@
|
|||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="设置" placement="bottom">
|
||||
<!-- <el-tooltip content="设置" placement="bottom">
|
||||
<el-button
|
||||
class="action-btn"
|
||||
:icon="Setting"
|
||||
|
@ -37,7 +37,7 @@
|
|||
text
|
||||
>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</el-tooltip> -->
|
||||
<el-tooltip :content="hasBackground ? '客户背景' : '暂无客户背景信息'" placement="bottom" v-if="hasBackground">
|
||||
<el-button
|
||||
class="action-btn"
|
||||
|
@ -127,7 +127,7 @@ onUnmounted(() => {
|
|||
.chat-header {
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
background: #fff;
|
||||
background: #FFF;
|
||||
padding: 0 20px;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
@ -186,6 +186,34 @@ onUnmounted(() => {
|
|||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.desktop-sidebar-toggle {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 0;
|
||||
color: #666;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
background: transparent !important;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&:hover {
|
||||
color: #07c160;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 18px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
font-weight: normal;
|
||||
flex-shrink: 0;
|
||||
|
|
|
@ -552,7 +552,7 @@ onUnmounted(() => {
|
|||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
border-radius: 6px;
|
||||
white-space: pre-wrap;
|
||||
position: relative;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
|
@ -709,7 +709,6 @@ onUnmounted(() => {
|
|||
padding: 16px;
|
||||
background: #f5f5f5;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
z-index: 101;
|
||||
|
||||
:deep(.el-textarea) {
|
||||
flex: 1;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="mode-selector-container">
|
||||
<div
|
||||
class="mode-selector-backdrop"
|
||||
:class="{ visible: !settingsStore.sidebarCollapsed }"
|
||||
|
@ -8,43 +8,40 @@
|
|||
></div>
|
||||
<div class="mode-selector" :class="{ 'collapsed': settingsStore.sidebarCollapsed }">
|
||||
<div class="mode-header">
|
||||
<h3 class="mode-title">对话场景</h3>
|
||||
<div class="header-actions">
|
||||
<HistoryButton v-if="!settingsStore.sidebarCollapsed" :icon-only="true" />
|
||||
<el-button
|
||||
class="collapse-btn"
|
||||
:icon="settingsStore.sidebarCollapsed ? Expand : Fold"
|
||||
text
|
||||
bg
|
||||
@click="settingsStore.toggleSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-list">
|
||||
<div
|
||||
v-for="mode in chatModes"
|
||||
:key="mode.id"
|
||||
:class="['mode-item', { active: chatStore.currentMode === mode.id }]"
|
||||
@click="selectMode(mode.id)"
|
||||
>
|
||||
<span class="emoji-icon">{{ mode.icon }}</span>
|
||||
<span v-if="!settingsStore.sidebarCollapsed" class="mode-name">{{ mode.name }}</span>
|
||||
<ModeSetting v-if="!settingsStore.sidebarCollapsed" @command="(cmd) => handleModeSettings(cmd, mode.id)">
|
||||
<el-dropdown-item command="edit">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
编辑设置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="reset">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
重置设置
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="delete" divided>
|
||||
<el-icon><Delete /></el-icon>
|
||||
删除场景
|
||||
</el-dropdown-item>
|
||||
</ModeSetting>
|
||||
<div class="mode-header-title" v-if="!settingsStore.sidebarCollapsed">
|
||||
<img src="/lgogo.svg" alt="Logo" class="mode-header-icon" />
|
||||
</div>
|
||||
</div>
|
||||
<el-menu
|
||||
:default-active="chatStore.currentMode"
|
||||
class="mode-menu"
|
||||
:collapse="settingsStore.sidebarCollapsed"
|
||||
@select="selectMode"
|
||||
>
|
||||
<el-menu-item v-for="mode in chatModes" :key="mode.id" :index="mode.id" class="mode-item">
|
||||
<el-icon><component :is="mode.icon" /></el-icon>
|
||||
<span class="mode-name">{{ mode.name }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
|
||||
<!-- Settings Button Popover -->
|
||||
<el-popover
|
||||
placement="top-end"
|
||||
trigger="click"
|
||||
popper-class="settings-popover-popper"
|
||||
>
|
||||
<template #reference>
|
||||
<div class="sidebar-footer">
|
||||
<div class="settings-button" title="设置">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span class="button-text">设置</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Popover Content -->
|
||||
<SettingsPanel />
|
||||
</el-popover>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -53,10 +50,9 @@
|
|||
import { useChatStore } from '@/store/chat'
|
||||
import { useSettingsStore } from '@/store/settings'
|
||||
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { Delete, EditPen, RefreshRight, Expand, Fold } from '@element-plus/icons-vue'
|
||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElButton, ElMessageBox } from 'element-plus'
|
||||
import ModeSetting from '@/components/chat/ModeSetting.vue'
|
||||
import HistoryButton from '@/components/chat/HistoryButton.vue'
|
||||
import { ChatDotRound, ChatLineRound, ChatRound, Service, Setting } from '@element-plus/icons-vue'
|
||||
import { ElMenu, ElMenuItem, ElIcon, ElPopover } from 'element-plus'
|
||||
import SettingsPanel from '@/components/settings/SettingsPanel.vue'
|
||||
|
||||
const chatStore = useChatStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
@ -65,7 +61,9 @@ const chatModes = computed(() => {
|
|||
return Object.keys(chatStore.chatModes).map(key => ({
|
||||
id: key,
|
||||
name: chatStore.chatModes[key].name,
|
||||
icon: chatStore.chatModes[key].icon
|
||||
icon: key === 'general' ? ChatRound :
|
||||
key === 'assistant' ? Service :
|
||||
key === 'chat' ? ChatDotRound : ChatLineRound
|
||||
}))
|
||||
})
|
||||
|
||||
|
@ -74,58 +72,10 @@ const currentModeHistory = computed(() => {
|
|||
return chatStore.conversations.filter(chat => chat.mode === chatStore.currentMode)
|
||||
})
|
||||
|
||||
// 格式化会话标题
|
||||
const formatChatTitle = (chat) => {
|
||||
// 使用第一条消息作为标题,如果没有则使用默认标题
|
||||
if (chat.messages && chat.messages.length > 0) {
|
||||
const firstMessage = chat.messages[0].question
|
||||
return firstMessage.length > 20 ? firstMessage.substring(0, 20) + '...' : firstMessage
|
||||
}
|
||||
return `会话 ${chat.conversationId.substring(0, 8)}`
|
||||
}
|
||||
|
||||
const selectMode = (modeId) => {
|
||||
chatStore.setCurrentMode(modeId)
|
||||
}
|
||||
|
||||
const handleHistorySelect = (conversationId) => {
|
||||
chatStore.switchConversation(conversationId)
|
||||
}
|
||||
|
||||
const handleDelete = async (conversationId) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定要删除这个会话吗?删除后无法恢复。',
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
chatStore.deleteConversation(conversationId)
|
||||
} catch {
|
||||
// 用户取消删除
|
||||
}
|
||||
}
|
||||
|
||||
const handleModeSettings = (command, modeId) => {
|
||||
switch (command) {
|
||||
case 'edit':
|
||||
// TODO: 实现编辑设置的逻辑
|
||||
console.log('编辑设置:', modeId)
|
||||
break
|
||||
case 'reset':
|
||||
// TODO: 实现重置设置的逻辑
|
||||
console.log('重置设置:', modeId)
|
||||
break
|
||||
case 'delete':
|
||||
// TODO: 实现删除场景的逻辑
|
||||
console.log('删除场景:', modeId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const isMobile = ref(false)
|
||||
|
||||
const checkMobile = () => {
|
||||
|
@ -143,306 +93,275 @@ onUnmounted(() => {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$primary-color: #4CAF50;
|
||||
$text-color: #333;
|
||||
$text-color-dark: #fff;
|
||||
$background-color: #ffffff;
|
||||
$background-color-dark: #1e1e1e;
|
||||
$border-color: #e0e0e0;
|
||||
$border-color-dark: #333;
|
||||
$hover-color: #f5f5f5;
|
||||
$hover-color-dark: #2d2d2d;
|
||||
.mode-selector-container {
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
width: 240px;
|
||||
min-width: 240px;
|
||||
background: $background-color;
|
||||
border-right: 1px solid $border-color;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
background-color: var(--el-bg-color);
|
||||
border-right: 1px solid var(--el-border-color-light);
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
body:not(.no-animations) & {
|
||||
transition: width 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
width: 64px;
|
||||
min-width: 64px;
|
||||
|
||||
.mode-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mode-item {
|
||||
padding: 15px;
|
||||
justify-content: center;
|
||||
|
||||
.mode-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mode-header {
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mode-title {
|
||||
color: $text-color;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.history-btn {
|
||||
font-size: 14px;
|
||||
height: 32px;
|
||||
padding: 4px 12px;
|
||||
color: $text-color;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
&:hover {
|
||||
color: #07c160;
|
||||
background: rgba(7, 193, 96, 0.1);
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
font-size: 14px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
padding: 0;
|
||||
color: $text-color;
|
||||
transition: all 0.3s ease;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
|
||||
&:hover {
|
||||
color: $primary-color;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mode-list {
|
||||
padding: 0 0 20px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.mode-item {
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: $text-color;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: $hover-color;
|
||||
|
||||
:deep(.setting-btn) {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $primary-color;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.emoji-icon {
|
||||
font-size: 20px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mode-name {
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
:deep(.mode-setting) {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
font-size: 13px;
|
||||
padding: 8px 16px;
|
||||
line-height: 1.4;
|
||||
max-width: 300px;
|
||||
overflow-x: hidden;
|
||||
|
||||
&:hover .delete-btn {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu) {
|
||||
min-width: 200px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
.mode-header {
|
||||
height: 60px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.mode-header-icon {
|
||||
height: 32px;
|
||||
will-change: filter;
|
||||
|
||||
body:not(.no-animations) & {
|
||||
transition: filter 300ms;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: drop-shadow(0 0 0.75em rgba(var(--el-color-primary-rgb), 0.6));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.history-title {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 24px;
|
||||
min-width: 0;
|
||||
.mode-menu {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 8px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
color: var(--el-text-color-regular);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 4px;
|
||||
padding: 0 12px !important;
|
||||
overflow: hidden;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 12px;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-image: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.el-icon {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-active):hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.mode-name {
|
||||
font-size: 14px;
|
||||
margin-left: 12px;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
|
||||
body:not(.no-animations) & {
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
.mode-setting-btn {
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
opacity: 0;
|
||||
transform: translateX(10px);
|
||||
transition: all 0.3s ease;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 0;
|
||||
color: #f56c6c;
|
||||
flex-shrink: 0;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
&:hover {
|
||||
color: #f56c6c;
|
||||
background-color: rgba(245, 108, 108, 0.1);
|
||||
}
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 14px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端样式 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.mode-selector {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transform: translateX(-100%);
|
||||
z-index: 1000;
|
||||
|
||||
&.collapsed {
|
||||
transform: translateX(-240px);
|
||||
}
|
||||
|
||||
&:not(.collapsed) {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.mode-selector-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
background-color: var(--el-overlay-color-lighter);
|
||||
z-index: 9;
|
||||
display: none;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mode-selector {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
&.collapsed {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @media (prefers-color-scheme: dark) {
|
||||
// .mode-selector {
|
||||
// background: $background-color-dark;
|
||||
// border-right-color: $border-color-dark;
|
||||
.mode-selector.collapsed {
|
||||
.mode-menu {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
// .mode-title {
|
||||
// color: $text-color-dark;
|
||||
// }
|
||||
:deep(.el-menu-item) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 4px auto;
|
||||
|
||||
// .mode-item {
|
||||
// color: $text-color-dark;
|
||||
.el-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
// &:hover {
|
||||
// background: $hover-color-dark;
|
||||
// }
|
||||
// }
|
||||
.mode-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// .history-btn {
|
||||
// color: $text-color-dark;
|
||||
|
||||
// &:hover {
|
||||
// color: $primary-color;
|
||||
// background: rgba($primary-color, 0.15);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
.settings-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
.mode-name,
|
||||
.button-text {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .delete-btn {
|
||||
// &:hover {
|
||||
// background-color: rgba(245, 108, 108, 0.15);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
.sidebar-footer {
|
||||
padding: 8px;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
color: var(--el-text-color-regular);
|
||||
border-radius: 8px;
|
||||
padding: 0 12px !important;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
body:not(.no-animations) & {
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin-right: 12px;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
color: var(--el-text-color-regular);
|
||||
body:not(.no-animations) & {
|
||||
transition: color 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.button-text {
|
||||
font-size: 14px;
|
||||
margin-left: 12px;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
|
||||
body:not(.no-animations) & {
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
|
||||
.el-icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mode-selector.collapsed {
|
||||
.sidebar-footer {
|
||||
padding: 8px 12px;
|
||||
.settings-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.mode-menu {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 4px auto;
|
||||
|
||||
.el-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.mode-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-button {
|
||||
.mode-name,
|
||||
.button-text {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.settings-popover-popper {
|
||||
padding: 0 !important;
|
||||
min-width: 240px !important;
|
||||
width: auto !important;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="chat">
|
||||
<div class="chat-page">
|
||||
<div class="chat-container">
|
||||
<ChatModeSelector @mode-changed="handleModeChange" />
|
||||
<div class="chat-wrapper">
|
||||
|
@ -27,7 +27,7 @@ const handleModeChange = (mode) => {
|
|||
|
||||
|
||||
|
||||
<style>
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -35,8 +35,8 @@ const handleModeChange = (mode) => {
|
|||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
||||
.chat {
|
||||
height: 100%;
|
||||
.chat-page {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f5f5f5;
|
||||
|
|
|
@ -5,6 +5,7 @@ import vue from '@vitejs/plugin-vue'
|
|||
// import electron from 'vite-plugin-electron'
|
||||
// import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
import mkcert from "vite-plugin-mkcert";
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
const env = loadEnv(mode, process.cwd())
|
||||
|
@ -18,6 +19,11 @@ export default defineConfig(({ command, mode }) => {
|
|||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
mkcert({
|
||||
source: 'coding',
|
||||
autoUpgrade: true,
|
||||
force: true,
|
||||
}),
|
||||
// electron({
|
||||
// entry: 'electron/main.js',
|
||||
// }),
|
||||
|
@ -36,12 +42,49 @@ export default defineConfig(({ command, mode }) => {
|
|||
}
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 31003,
|
||||
https: true,
|
||||
cors: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: env.VITE_APP_BASE_API,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
secure: false
|
||||
secure: false,
|
||||
configure: (proxy, options) => {
|
||||
proxy.on('proxyReq', (proxyReq, req, res) => {
|
||||
const targetOrigin = new URL(env.VITE_APP_BASE_API).origin;
|
||||
proxyReq.setHeader('Origin', targetOrigin);
|
||||
|
||||
if (req.originalUrl && req.originalUrl.includes('chat-messages')) {
|
||||
proxyReq.setHeader('Accept', 'text/event-stream');
|
||||
}
|
||||
});
|
||||
proxy.on('proxyRes', (proxyRes, req, res) => {
|
||||
if (req.originalUrl && req.originalUrl.includes('chat-messages')) {
|
||||
proxyRes.headers['content-type'] = 'text/event-stream';
|
||||
proxyRes.headers['Cache-Control'] = 'no-cache';
|
||||
proxyRes.headers['Connection'] = 'keep-alive';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vendor': ['vue', 'vue-router', 'pinia']
|
||||
}
|
||||
}
|
||||
},
|
||||
chunkSizeWarningLimit: 1500,
|
||||
terserOptions: {
|
||||
compress: {
|
||||
// drop_console: true,
|
||||
drop_debugger: true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue