Sfoglia il codice sorgente

修改颜色及布局,增加个人中心

masoft 2 mesi fa
parent
commit
f80aa0caed

+ 1 - 1
web/next.config.js

@@ -29,7 +29,7 @@ const nextConfig = {
     '@ant-design/pro-chat',
     'react-intersection-observer',
   ],
-  //reactStrictMode: false
+  reactStrictMode: false
 };
 
 module.exports = withPWA(nextConfig);

File diff suppressed because it is too large
+ 0 - 0
web/public/icons/a-1.svg


+ 1 - 0
web/public/icons/a-2.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734408673083" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17022" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M325.1 176.1h373.1v74.6H325.1zM959.3 288c0 20.5-16.8 37.3-37.3 37.3-20.5 0-37.3-16.8-37.3-37.3 0-20.5-16.8-37.3-37.3-37.3H735.5v-74.6h111.9c61.6 0 111.9 50.4 111.9 111.9" fill="#0061ff" p-id="17023"></path><path d="M959.3 437.3v410.3c0 61.6-50.3 111.9-111.9 111.9H175.9C114.4 959.6 64 909.2 64 847.6V288c0-61.6 50.4-111.9 111.9-111.9h111.9v74.6H175.9c-20.5 0-37.3 16.8-37.3 37.3v559.6c0 20.5 16.8 37.3 37.3 37.3h671.5c20.5 0 37.3-16.8 37.3-37.3V437.3c0-20.6 16.8-37.3 37.3-37.3 20.5 0 37.3 16.7 37.3 37.3" fill="#0061ff" p-id="17024"></path><path d="M306.5 64.2c-51.5 0-93.3 41.8-93.3 93.3v111.9c0 51.5 41.8 93.3 93.3 93.3s93.3-41.8 93.3-93.3V157.5c0-51.5-41.8-93.3-93.3-93.3m18.6 205.2c0 10.4-8.2 18.6-18.6 18.6-10.4 0-18.6-8.2-18.6-18.6V157.5c0-10.5 8.2-18.6 18.6-18.6 10.4 0 18.6 8.2 18.6 18.6v111.9zM716.8 64.2c-51.5 0-93.3 41.8-93.3 93.3v111.9c0 51.5 41.8 93.3 93.3 93.3s93.3-41.8 93.3-93.3V157.5c0-51.5-41.8-93.3-93.3-93.3m18.7 205.2c0 10.4-8.2 18.6-18.6 18.6-10.4 0-18.6-8.2-18.6-18.6V157.5c0-10.5 8.2-18.6 18.6-18.6 10.4 0 18.6 8.2 18.6 18.6v111.9zM810.1 735.7H213.2c-20.6 0-37.3-16.7-37.3-37.3s16.7-37.3 37.3-37.3h596.9c20.6 0 37.3 16.7 37.3 37.3s-16.7 37.3-37.3 37.3M810.1 511.9H213.2c-20.6 0-37.3-16.7-37.3-37.3 0-20.6 16.7-37.3 37.3-37.3h596.9c20.6 0 37.3 16.7 37.3 37.3 0 20.6-16.7 37.3-37.3 37.3" fill="#0061ff" p-id="17025"></path><path d="M362.5 847.6c-20.6 0-37.3-16.7-37.3-37.3V474.6c0-20.6 16.7-37.3 37.3-37.3 20.6 0 37.3 16.7 37.3 37.3v335.7c0 20.7-16.7 37.3-37.3 37.3M660.9 847.6c-20.6 0-37.3-16.7-37.3-37.3V474.6c0-20.6 16.7-37.3 37.3-37.3 20.6 0 37.3 16.7 37.3 37.3v335.7c0 20.7-16.7 37.3-37.3 37.3" fill="#0061ff" p-id="17026"></path></svg>

+ 1 - 0
web/public/icons/a-3.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734408742298" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19584" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M698.709333 216.32a108.928 108.928 0 1 0 0 217.856 108.928 108.928 0 0 0 0-217.856z m-46.677333 108.928a46.677333 46.677333 0 1 1 93.397333 0 46.677333 46.677333 0 0 1-93.397333 0zM652.032 589.781333c0-21.162667 6.101333-37.12 14.336-47.146666a40.021333 40.021333 0 0 1 32.341333-15.061334c13.824 0 24.533333 5.504 32.341334 15.061334 8.234667 10.026667 14.378667 25.984 14.378666 47.146666v217.856a31.146667 31.146667 0 0 0 62.208 0v-217.856c0-32.725333-9.429333-63.445333-28.458666-86.613333a102.186667 102.186667 0 0 0-80.469334-37.845333c-32.853333 0-61.056 14.165333-80.469333 37.845333-18.986667 23.168-28.458667 53.888-28.458667 86.613333v78.805334l-82.133333 68.48a31.146667 31.146667 0 1 0 39.850667 47.786666l93.354666-77.781333a31.146667 31.146667 0 0 0 11.178667-23.893333v-93.397334z" fill="#0061ff" p-id="19585"></path><path d="M309.717333 185.173333a93.354667 93.354667 0 1 0 0 186.752 93.354667 93.354667 0 0 0 0-186.709333z m-31.146666 93.397334a31.146667 31.146667 0 1 1 62.293333 0 31.146667 31.146667 0 0 1-62.293333 0zM325.248 403.072c-32.853333 0-61.056 14.208-80.469333 37.845333-18.986667 23.168-28.416 53.888-28.416 86.613334v280.106666a31.146667 31.146667 0 0 0 62.208 0v-280.064c0-21.205333 6.144-37.12 14.336-47.189333a40.021333 40.021333 0 0 1 32.341333-15.061333c13.824 0 24.533333 5.546667 32.384 15.061333 8.192 10.026667 14.336 26.026667 14.336 47.189333v62.208a31.146667 31.146667 0 0 0 11.178667 23.893334l93.354666 77.824a31.146667 31.146667 0 1 0 39.850667-47.786667l-82.176-68.48v-47.658667c0-32.768-9.429333-63.488-28.416-86.656a102.229333 102.229333 0 0 0-80.469333-37.845333z" fill="#0061ff" p-id="19586"></path></svg>

+ 1 - 0
web/public/icons/a-4.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734408794664" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20639" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 967.68c251.668 0 455.68-204.012 455.68-455.68S763.668 56.32 512 56.32 56.32 260.332 56.32 512 260.332 967.68 512 967.68z m0-71.68c-212.07 0-384-171.93-384-384s171.93-384 384-384 384 171.93 384 384-171.93 384-384 384z" p-id="20640" fill="#0061ff"></path><path d="M544.297 405.279a77.599 77.599 0 1 1 77.599 77.578 30.72 30.72 0 0 0 0 61.44c76.78 0 139.028-62.249 139.028-139.029S698.675 266.24 621.896 266.24s-139.039 62.249-139.039 139.028a30.72 30.72 0 0 0 61.44 0z m-61.44 213.217a77.588 77.588 0 0 1-155.177 0 77.588 77.588 0 0 1 77.588-77.599 30.72 30.72 0 0 0 0-61.44c-76.769 0-139.028 62.239-139.028 139.039 0 76.78 62.249 139.028 139.028 139.028 76.79 0 139.029-62.248 139.029-139.028a30.72 30.72 0 0 0-61.44 0z" p-id="20641" fill="#0061ff"></path><path d="M482.857 402.156V619.93h61.44V402.156z" p-id="20642" fill="#0061ff"></path></svg>

+ 1 - 0
web/public/icons/a-5.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734408827930" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22029" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M272.164571 326.802286L248.246857 201.142857H62.902857v-59.757714h233.179429l35.84 173.348571-59.757715 11.995429z" fill="#0061ff" p-id="22030"></path><path d="M865.645714 769.389714h-514.194285L266.24 291.035429H950.857143l-85.211429 478.354285zM402.285714 709.485714h414.134857l64.292572-358.765714H337.92l64.365714 358.765714z" fill="#0061ff" p-id="22031"></path><path d="M391.753143 933.741714c-58.294857 0-104.594286-46.372571-104.594286-104.667428s46.299429-104.594286 104.594286-104.594286c58.294857 0 104.667429 46.299429 104.667428 104.594286 0 58.294857-46.372571 104.594286-104.594285 104.594285z m0-149.504a44.032 44.032 0 0 0-44.836572 44.836572c0 25.380571 19.456 44.836571 44.836572 44.836571a44.032 44.032 0 0 0 44.836571-44.836571 44.032 44.032 0 0 0-44.836571-44.836572z m433.517714 149.504c-58.294857 0-104.594286-46.372571-104.594286-104.667428s46.299429-104.594286 104.594286-104.594286c58.294857 0 104.594286 46.299429 104.594286 104.594286 0 58.294857-46.299429 104.594286-104.594286 104.594285z m0-149.504a44.032 44.032 0 0 0-44.836571 44.836572c0 25.380571 19.456 44.836571 44.836571 44.836571a44.032 44.032 0 0 0 44.836572-44.836571 44.032 44.032 0 0 0-44.836572-44.836572z" fill="#0061ff" p-id="22032"></path><path d="M451.584 455.460571h313.929143v59.757715H451.584v-59.757715z" fill="#0061ff" p-id="22033"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
web/public/icons/a-6.svg


+ 1 - 1
web/public/icons/icon-app.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074080941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1477" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M706.346667 858.88H317.653333c-62.506667 0-113.493333-56.533333-113.493333-125.866667V464.426667a32 32 0 0 1 64 0v268.373333a56.746667 56.746667 0 0 0 49.493333 61.866667h388.693334a56.746667 56.746667 0 0 0 49.493333-61.866667v-118.826667a32 32 0 1 1 64 0v119.04c0 69.333333-51.84 125.866667-113.493333 125.866667z" fill="#FD8F9E" p-id="1478"></path><path d="M512 526.08a124.586667 124.586667 0 0 1-78.293333-27.946667 39.04 39.04 0 0 0-49.706667 0 124.16 124.16 0 0 1-156.8 0l-17.706667-14.293333a136.32 136.32 0 0 1-49.706666-105.6V298.666667a130.986667 130.986667 0 0 1 128-133.333334h448a130.986667 130.986667 0 0 1 128 133.333334v78.72a136.32 136.32 0 0 1-49.706667 105.6l-17.706667 14.293333a124.16 124.16 0 0 1-156.8 0 39.04 39.04 0 0 0-49.706666 0A124.586667 124.586667 0 0 1 512 526.08z m-103.253333-122.24a124.8 124.8 0 0 1 78.293333 27.733333 38.826667 38.826667 0 0 0 49.706667 0 124.16 124.16 0 0 1 156.8 0 38.826667 38.826667 0 0 0 49.706666 0l17.706667-14.293333a50.133333 50.133333 0 0 0 17.92-39.04V298.666667a45.653333 45.653333 0 0 0-42.666667-48h-448a45.653333 45.653333 0 0 0-42.666666 48v78.72a50.133333 50.133333 0 0 0 17.92 39.04l17.706666 14.293333a38.826667 38.826667 0 0 0 49.706667 0 124.8 124.8 0 0 1 77.866667-26.88z" fill="#FC455D" p-id="1479"></path><path d="M644.608 645.51132m-32.432631 32.432631a45.866667 45.866667 0 1 0 64.865262-64.865262 45.866667 45.866667 0 1 0-64.865262 64.865262Z" fill="#FF9B57" p-id="1480" data-spm-anchor-id="a313x.collections_detail.0.i2.3dab3a81xrPYTi" class="selected"></path></svg>
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074080941" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1477" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M706.346667 858.88H317.653333c-62.506667 0-113.493333-56.533333-113.493333-125.866667V464.426667a32 32 0 0 1 64 0v268.373333a56.746667 56.746667 0 0 0 49.493333 61.866667h388.693334a56.746667 56.746667 0 0 0 49.493333-61.866667v-118.826667a32 32 0 1 1 64 0v119.04c0 69.333333-51.84 125.866667-113.493333 125.866667z" fill="#6c62fd" p-id="1478"></path><path d="M512 526.08a124.586667 124.586667 0 0 1-78.293333-27.946667 39.04 39.04 0 0 0-49.706667 0 124.16 124.16 0 0 1-156.8 0l-17.706667-14.293333a136.32 136.32 0 0 1-49.706666-105.6V298.666667a130.986667 130.986667 0 0 1 128-133.333334h448a130.986667 130.986667 0 0 1 128 133.333334v78.72a136.32 136.32 0 0 1-49.706667 105.6l-17.706667 14.293333a124.16 124.16 0 0 1-156.8 0 39.04 39.04 0 0 0-49.706666 0A124.586667 124.586667 0 0 1 512 526.08z m-103.253333-122.24a124.8 124.8 0 0 1 78.293333 27.733333 38.826667 38.826667 0 0 0 49.706667 0 124.16 124.16 0 0 1 156.8 0 38.826667 38.826667 0 0 0 49.706666 0l17.706667-14.293333a50.133333 50.133333 0 0 0 17.92-39.04V298.666667a45.653333 45.653333 0 0 0-42.666667-48h-448a45.653333 45.653333 0 0 0-42.666666 48v78.72a50.133333 50.133333 0 0 0 17.92 39.04l17.706666 14.293333a38.826667 38.826667 0 0 0 49.706667 0 124.8 124.8 0 0 1 77.866667-26.88z" fill="#0061ff" p-id="1479"></path><path d="M644.608 645.51132m-32.432631 32.432631a45.866667 45.866667 0 1 0 64.865262-64.865262 45.866667 45.866667 0 1 0-64.865262 64.865262Z" fill="#6c62fd" p-id="1480" data-spm-anchor-id="a313x.collections_detail.0.i2.3dab3a81xrPYTi" class="selected"></path></svg>

+ 1 - 1
web/public/icons/icon-question.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734073968556" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5429" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 896a384 384 0 1 1 384-384 384 384 0 0 1-384 384z m0-682.666667a298.666667 298.666667 0 1 0 298.666667 298.666667 298.666667 298.666667 0 0 0-298.666667-298.666667z" fill="#FC455D" p-id="5430"></path><path d="M512 618.666667a32 32 0 0 1-32-32v-64A32 32 0 0 1 512 490.666667a58.88 58.88 0 1 0-58.88-58.88 32 32 0 0 1-64 0 122.88 122.88 0 1 1 154.88 118.613333v36.266667A32 32 0 0 1 512 618.666667z" fill="#FD8F9E" p-id="5431"></path><path d="M511.975006 681.112994m-30.169889 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#FF9B57" p-id="5432"></path></svg>
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734073968556" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5429" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M512 896a384 384 0 1 1 384-384 384 384 0 0 1-384 384z m0-682.666667a298.666667 298.666667 0 1 0 298.666667 298.666667 298.666667 298.666667 0 0 0-298.666667-298.666667z" fill="#0061ff" p-id="5430"></path><path d="M512 618.666667a32 32 0 0 1-32-32v-64A32 32 0 0 1 512 490.666667a58.88 58.88 0 1 0-58.88-58.88 32 32 0 0 1-64 0 122.88 122.88 0 1 1 154.88 118.613333v36.266667A32 32 0 0 1 512 618.666667z" fill="#6c62fd" p-id="5431"></path><path d="M511.975006 681.112994m-30.169889 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#6c62fd" p-id="5432"></path></svg>

+ 1 - 1
web/public/icons/icon-shrink.svg

@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="2rem" height="2rem" fill="none" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M10.247 6.844v10.334h7.871c.325 0 .546-.085.695-.229.144-.14.234-.363.234-.708V7.78c0-.345-.09-.568-.234-.708-.149-.144-.37-.23-.695-.23zm-1.423 0H5.889c-.33 0-.553.085-.7.228-.14.14-.228.363-.228.71v8.459c0 .346.087.57.227.71.148.142.371.227.701.227h2.935zM5.702 12.93a.556.556 0 0 1 .005-.814.58.58 0 0 1 .399-.16h1.573c.156 0 .297.053.408.163a.556.556 0 0 1 .003.812.57.57 0 0 1-.411.165H6.106a.57.57 0 0 1-.399-.16zM4.1 5.994c.417-.408 1.021-.594 1.774-.594h12.259c.753 0 1.356.186 1.768.595s.599 1 .599 1.734v8.564c0 .735-.188 1.326-.599 1.734-.412.41-1.015.595-1.768.595H5.874c-.753 0-1.357-.186-1.774-.594-.413-.404-.6-.996-.6-1.735V7.729c0-.739.187-1.33.6-1.735m3.579 3.45H6.106a.56.56 0 0 1-.404-.173.56.56 0 0 1-.174-.4.54.54 0 0 1 .177-.404.56.56 0 0 1 .4-.17H7.68c.16 0 .3.058.41.172a.54.54 0 0 1 .175.401c0 .155-.064.29-.172.398a.56.56 0 0 1-.413.176m0 1.83H6.106a.56.56 0 0 1-.404-.173.56.56 0 0 1-.174-.409c0-.156.065-.29.177-.396a.56.56 0 0 1 .4-.17H7.68c.16 0 .3.059.41.173.111.105.175.239.175.393a.56.56 0 0 1-.172.406.56.56 0 0 1-.413.175" clip-rule="evenodd"></path></svg>
+<svg xmlns="http://www.w3.org/2000/svg" class="bg-slate-400" width="2rem" height="2rem" viewBox="0 0 24 24"><path fill="currentColor" fill-rule="evenodd" d="M10.247 6.844v10.334h7.871c.325 0 .546-.085.695-.229.144-.14.234-.363.234-.708V7.78c0-.345-.09-.568-.234-.708-.149-.144-.37-.23-.695-.23zm-1.423 0H5.889c-.33 0-.553.085-.7.228-.14.14-.228.363-.228.71v8.459c0 .346.087.57.227.71.148.142.371.227.701.227h2.935zM5.702 12.93a.556.556 0 0 1 .005-.814.58.58 0 0 1 .399-.16h1.573c.156 0 .297.053.408.163a.556.556 0 0 1 .003.812.57.57 0 0 1-.411.165H6.106a.57.57 0 0 1-.399-.16zM4.1 5.994c.417-.408 1.021-.594 1.774-.594h12.259c.753 0 1.356.186 1.768.595s.599 1 .599 1.734v8.564c0 .735-.188 1.326-.599 1.734-.412.41-1.015.595-1.768.595H5.874c-.753 0-1.357-.186-1.774-.594-.413-.404-.6-.996-.6-1.735V7.729c0-.739.187-1.33.6-1.735m3.579 3.45H6.106a.56.56 0 0 1-.404-.173.56.56 0 0 1-.174-.4.54.54 0 0 1 .177-.404.56.56 0 0 1 .4-.17H7.68c.16 0 .3.058.41.172a.54.54 0 0 1 .175.401c0 .155-.064.29-.172.398a.56.56 0 0 1-.413.176m0 1.83H6.106a.56.56 0 0 1-.404-.173.56.56 0 0 1-.174-.409c0-.156.065-.29.177-.396a.56.56 0 0 1 .4-.17H7.68c.16 0 .3.059.41.173.111.105.175.239.175.393a.56.56 0 0 1-.172.406.56.56 0 0 1-.413.175" clip-rule="evenodd"></path></svg>

+ 1 - 1
web/public/icons/icon-talk.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074028181" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1157" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M495.573333 162.986667A349.44 349.44 0 0 0 220.373333 704v93.013333a64 64 0 0 0 64 64h218.24c189.013333 0 355.413333-154.24 358.613334-343.253333A349.44 349.44 0 0 0 495.573333 162.986667z m-119.68 394.453333a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 33.28z m147.626667 0a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 32.64z m142.72 0a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 33.28z" fill="#FC455D" p-id="1158"></path></svg>
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074028181" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1157" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M495.573333 162.986667A349.44 349.44 0 0 0 220.373333 704v93.013333a64 64 0 0 0 64 64h218.24c189.013333 0 355.413333-154.24 358.613334-343.253333A349.44 349.44 0 0 0 495.573333 162.986667z m-119.68 394.453333a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 33.28z m147.626667 0a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 32.64z m142.72 0a46.293333 46.293333 0 1 1 33.28-33.28 46.506667 46.506667 0 0 1-33.28 33.28z" fill="#0061ff" p-id="1158"></path></svg>

+ 1 - 1
web/public/icons/icon-vip.svg

@@ -1 +1 @@
-<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074044118" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1315" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M733.44 852.053333H296.96a128 128 0 0 1-128-128V394.453333a42.666667 42.666667 0 0 1 64-36.48l120.746667 72.746667 128-135.893333a40.96 40.96 0 0 1 30.293333-13.653334 42.666667 42.666667 0 0 1 31.146667 14.08l121.6 134.826667 118.186666-72.106667a42.666667 42.666667 0 0 1 64 36.48v257.92a42.666667 42.666667 0 0 1-85.333333 0v-181.973333l-83.413333 50.986667a42.666667 42.666667 0 0 1-53.973334-7.893334L512 386.986667l-119.466667 128a42.666667 42.666667 0 0 1-53.12 7.253333l-85.333333-51.626667V723.2a42.666667 42.666667 0 0 0 42.666667 42.666667h436.693333a42.666667 42.666667 0 0 1 0 85.333333z" fill="#FC455D" p-id="1316"></path><path d="M238.350013 271.283503m-30.169889 30.16989a42.666667 42.666667 0 1 0 60.339778-60.339779 42.666667 42.666667 0 1 0-60.339778 60.339779Z" fill="#FF9B57" p-id="1317"></path><path d="M511.913151 192.728994m-30.16989 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#FF9B57" p-id="1318"></path><path d="M778.747268 271.255922m-30.16989 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#FF9B57" p-id="1319"></path><path d="M516.053333 730.88a32 32 0 0 1-25.386666-13.226667L418.346667 618.666667A32 32 0 0 1 469.333333 581.12l44.16 61.013333 38.186667-59.52a32 32 0 1 1 53.76 34.56l-64 98.986667a32 32 0 0 1-26.026667 14.72z" fill="#FD8F9E" p-id="1320"></path></svg>
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1734074044118" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1315" xmlns:xlink="http://www.w3.org/1999/xlink" width="256" height="256"><path d="M733.44 852.053333H296.96a128 128 0 0 1-128-128V394.453333a42.666667 42.666667 0 0 1 64-36.48l120.746667 72.746667 128-135.893333a40.96 40.96 0 0 1 30.293333-13.653334 42.666667 42.666667 0 0 1 31.146667 14.08l121.6 134.826667 118.186666-72.106667a42.666667 42.666667 0 0 1 64 36.48v257.92a42.666667 42.666667 0 0 1-85.333333 0v-181.973333l-83.413333 50.986667a42.666667 42.666667 0 0 1-53.973334-7.893334L512 386.986667l-119.466667 128a42.666667 42.666667 0 0 1-53.12 7.253333l-85.333333-51.626667V723.2a42.666667 42.666667 0 0 0 42.666667 42.666667h436.693333a42.666667 42.666667 0 0 1 0 85.333333z" fill="#0061ff" p-id="1316"></path><path d="M238.350013 271.283503m-30.169889 30.16989a42.666667 42.666667 0 1 0 60.339778-60.339779 42.666667 42.666667 0 1 0-60.339778 60.339779Z" fill="#6c62fd" p-id="1317"></path><path d="M511.913151 192.728994m-30.16989 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#6c62fd" p-id="1318"></path><path d="M778.747268 271.255922m-30.16989 30.169889a42.666667 42.666667 0 1 0 60.339779-60.339779 42.666667 42.666667 0 1 0-60.339779 60.339779Z" fill="#6c62fd" p-id="1319"></path><path d="M516.053333 730.88a32 32 0 0 1-25.386666-13.226667L418.346667 618.666667A32 32 0 0 1 469.333333 581.12l44.16 61.013333 38.186667-59.52a32 32 0 1 1 53.76 34.56l-64 98.986667a32 32 0 0 1-26.026667 14.72z" fill="#0061ff" p-id="1320"></path></svg>

BIN
web/public/images/avatar.png


BIN
web/public/images/login-bg.png


BIN
web/public/logo.png


+ 1 - 1
web/public/logo.svg

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">  <image id="image0" width="512" height="512" x="0" y="0"
+<svg class="fill-blue-500" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">  <image id="image0" width="512" height="512" x="0" y="0"
     xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAQAAABecRxxAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo
 AAB1MAAA6mAAADqYAAAXcJy6UTwAAAACYktHRAD/h4/MvwAAAAlwSFlzAAA7DgAAOw4BzLahgwAA
 AAd0SU1FB+gMDAIBGccRLBkAACDoSURBVHja7d17nBbVnefxb9M00EBzF4ggclNEjTei+FKzokEN

File diff suppressed because it is too large
+ 0 - 0
web/public/sw.js


BIN
web/public/temp/qrcode.png


BIN
web/public/temp/wxpay.png


+ 554 - 149
web/src/app/dialogue/page.tsx

@@ -1,89 +1,317 @@
 "use client";
-import { getApiUrl } from '@/utils/common';
-import {  getUserSession } from '@/utils/user';
+import { getApiUrl } from "@/utils/common";
+import { getUserSession } from "@/utils/user";
 import { useState, useEffect } from "react";
-import { useRouter } from "next/navigation";
+import { theme } from "antd";
+import { ThemeProvider, css, cx } from "antd-style";
+import { useTheme } from "next-themes";
 import { ProChat, ProChatInstance } from "@ant-design/pro-chat";
-import { RiAddCircleFill, RiCalendarCloseLine,RiLoader2Fill  } from "react-icons/ri";
-import Image from "next/image";
-import { getChatSession, getChatMessage, chatSubmit } from "@/api/client";
-import { ChatSessionResult, ChatMessageResult } from "@/utils/clientsApis";
+import {
+  RiAddCircleFill,
+  RiCalendarCloseLine,
+  RiLoader2Fill,
+  RiBookReadLine,
+} from "react-icons/ri";
+import {
+  getChatSession,
+  getChatMessage,
+  getEmployeeDetail,
+} from "@/api/client";
+import { ChatSessionResult, EmployeeSearchResult } from "@/utils/clientsApis";
 
-import { ChatMessage, MessageChange } from "@/utils/chat";
+import {
+  LoadLocalChatList,
+  ChatMessage,
+  MessageChange,
+  ChatAiModel,
+  SetChatModelListForLocalStorage,
+  GetChatModelListForLocalStorage,
+  SetChatLastUsedForLocalStorage,
+  UpdateLocalChatListForConversationId,
+  GetLocalChatModelDetails,
+} from "@/utils/chat";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 
-var msgId = "";
-
 export default function Dialogue() {
-  const router = useRouter();
+  const themeBig = useTheme();
+  const {
+    changeMenuIndex,
+    loginShow,
+    changeLoginShow,
+    listType,
+    changeListType,
+    agentId,
+    changeAgentId,
+    conversationId,
+    changeConversationId,
+  } = useGlobalContext();
+  const [employeeSearch, setEmployeeSearch] = useState<EmployeeSearchResult>();
 
-  const [uiMsgId, setUiMsgId] = useState('');
-  const { changeMenuIndex,loginShow, changeLoginShow } = useGlobalContext();
-  const [showComponent, setShowComponent] = useState(false);
-  const [showLetter, setShowLetter] = useState(true);
+  const [showLeftLetter, setShowLeftLetter] = useState(true);
+  const [historyOrChatList, setHistoryOrChatList] = useState<number>(0);
 
-  const [chatSessionList, setChatSessionList] = useState<ChatSessionResult[]>([]);
+  const [showComponent, setShowComponent] = useState(false);
   const [iniChatMessage, setIniChatMessage] = useState<ChatMessage[]>();
 
-  const handlShowLetter = () => {
-    setShowLetter(!showLetter);
+  const [chatAiModelList, setChatAiModelList] = useState<ChatAiModel[]>([]);
+  const [chatSessionLastId, setChatSessionLastId] = useState<string>("");
+  const [chatSessionList, setChatSessionList] = useState<ChatSessionResult[]>(
+    []
+  );
+
+  const handlShowLeftLetter = () => {
+    setShowLeftLetter(!showLeftLetter);
   };
+
+  //对话框加载聊天历史记录
   const chatInit = (id: string) => {
-    getChatMessage({ conversationId: id, firstId: "", limit: 100 }).then(
-      (data) => {
-        setIniChatMessage(MessageChange(data.data.data));
+    if (id !== "" && !id.includes("guid_0000_")) {
+      getChatMessage({
+        conversationId: id,
+        firstId: "",
+        limit: 100,
+        agentId,
+      }).then((data) => {
+        if (data.data.data) {
+          setIniChatMessage(MessageChange(data.data.data));
+        }
         setShowComponent(true);
+      });
+    } else {
+      setTimeout(() => {
+        setShowComponent(true);
+      }, 500);
+    }
+  };
+  //历史会话 - 点击
+  const changeChatSession = (id: string) => {
+    if (conversationId !== id) {
+      SetChatLastUsedForLocalStorage({
+        listType: 0,
+        agentId,
+        conversationId: id,
+      });
+      changeConversationId(id);
+      changeListType(0);
+      setShowComponent(false);
+      if (id == "") {
+        setIniChatMessage([]);
+        setTimeout(() => {
+          setShowComponent(true);
+        }, 200);
+      } else {
+        chatInit(id);
       }
-    );
+    }
   };
-
-  const changeChat = (id: string) => {
-    if(msgId !== id){
-      msgId = id;
-      setUiMsgId(msgId);
+  //助手列表 - 点击
+  const changeChatListForLocal = (id: number) => {
+    var model = GetLocalChatModelDetails(id);
+    if (model !== null) {
       setShowComponent(false);
-      chatInit(msgId);
+      setIniChatMessage([]);
+      SetChatLastUsedForLocalStorage({
+        listType: 1,
+        agentId: id,
+        conversationId: model.ConversationId,
+      });
+      changeConversationId(model.ConversationId);
+      changeListType(1);
+      changeAgentId(id);
+      if (model.ConversationId == "") {
+        setTimeout(() => {
+          setShowComponent(true);
+        }, 500);
+      } else {
+        chatInit(model.ConversationId);
+      }
+      loadChatSession("");
+    }
+  };
+  //新对话
+  const newChat = () => {
+    var newConversationId = "guid_0000_" + new Date().getTime();
+    setChatSessionList((chatSessionList) => [
+      ...[
+        {
+          id: newConversationId,
+          name: "新对话",
+          createdAt: new Date().getTime(),
+          inputs: {},
+        },
+      ],
+      ...chatSessionList,
+    ]);
+
+    SetChatLastUsedForLocalStorage({
+      listType: 0,
+      agentId,
+      conversationId: newConversationId,
+    });
+
+    setHistoryOrChatList(0);
+    changeListType(0);
+    changeConversationId(newConversationId);
+
+    setShowComponent(false);
+    setIniChatMessage([]);
+    setTimeout(() => {
+      setShowComponent(true);
+    }, 200);
+  };
+  //设置新会话的ID
+  const setConversationId = (id: string) => {
+    if (listType === 0) {
+      if (conversationId.includes("guid_0000_") || conversationId == "") {
+        if (chatSessionList !== null && chatSessionList.length > 0) {
+          var index = chatSessionList.findIndex((v) => v.id === conversationId);
+          if (index >= 0) {
+            chatSessionList[index].id = id;
+          } else {
+            chatSessionList[0].id = id;
+          }
+          setChatSessionList(chatSessionList);
+        }
+      }
     }
-  }
+    if (listType === 1) {
+      console.log(id);
+      var list = UpdateLocalChatListForConversationId(agentId, id);
+      setChatAiModelList(list);
+    }
+    changeConversationId(id);
+    SetChatLastUsedForLocalStorage({
+      listType: listType,
+      agentId,
+      conversationId: id,
+    });
+  };
+  //加载助手对应的历史会话
+  const loadChatSession = (last_id: string) => {
+    setShowComponent(false);
+    getChatSession({ agentId: agentId, last_id: last_id, limit: 100 }).then(
+      (data) => {
+        if (data.code === 0 && data.data.data !== null) {
+          if (last_id == "") {
+            setChatSessionList(data.data.data);
+          } else {
+            setChatSessionList((chatSessionList) => [
+              ...data.data.data,
+              ...chatSessionList,
+            ]);
+          }
+        } else {
+          setChatSessionList([]);
+        }
+        setShowComponent(true);
+      }
+    );
+  };
 
   useEffect(() => {
     changeMenuIndex(1);
+    if (agentId !== null && agentId > 0) {
+      //智能体进入 加载本地记录
+      getEmployeeDetail({ id: agentId }).then((res) => {
+        setHistoryOrChatList(listType);
+        if (res.code == 0 && res.data !== null) {
+          setEmployeeSearch(res.data);
+          var list = LoadLocalChatList(res.data);
+          setChatAiModelList(list);
+          chatInit(conversationId);
+        } else {
+          var list = GetChatModelListForLocalStorage();
+          setChatAiModelList(list);
+          var model = GetLocalChatModelDetails(agentId);
+          if (model === null && list !== null && list.length > 0) {
+            changeListType(1);
+            changeAgentId(list[0].id);
+            changeConversationId(list[0].ConversationId);
+            chatInit(list[0].ConversationId);
+          } else {
+            chatInit(model.ConversationId);
+          }
+        }
+      });
+    } else {
+      var list = GetChatModelListForLocalStorage();
+      setChatAiModelList(list);
+      var model = GetLocalChatModelDetails(agentId);
+      if (model === null && list !== null && list.length > 0) {
+        changeListType(1);
+        changeAgentId(list[0].id);
+        changeConversationId(list[0].ConversationId);
+        chatInit(list[0].ConversationId);
+      } else {
+        chatInit(model.ConversationId);
+      }
+      setHistoryOrChatList(1);
+    }
+    loadChatSession(chatSessionLastId);
+  }, [loginShow, agentId, chatSessionLastId]);
 
-    getChatSession({ last_id: "", limit: 100 }).then((data) => {
-      setShowComponent(false)
-      if (data.code === 0 && data.data.data !== null) {
-        msgId = data.data.data[0].id;
-        chatInit(msgId);
-        setUiMsgId(msgId);
-        setChatSessionList(data.data.data);
+  //样式覆盖
+  const BarkCustomClassName = cx(
+    css(`
+      .ant-pro-chat-list-item-message-content{
+        background-color: rgb(7  97 255 / 10%);
       }
-      if(data.code === 3){
-        setShowComponent(true);
+      .ant-pro-chat-input-area{
+        background-image: none;
+        background-color: #202329;
       }
-    });
-  }, [loginShow]);
+      .ant-pro-chat-input-area-text-container .acss-sbge3k{
+        color:rgb(2 91 237);
+      }
+  `)
+  );
+  const LightCustomClassName = cx(
+    css(`
+      .ant-pro-chat-list-item-message-content{
+        background-color: rgb(221  222  224 / 24%);
+      }
+        .ant-pro-chat-input-area{
+        background-image: none;
+        background-color: #f8f9fa;
+      }
+      .ant-pro-chat-input-area-text-container .acss-sbge3k{
+        color:#9ca3af;
+      }
+  `)
+  );
+  //TAGS
+  const readTags = (value:string) => {
+    const items = value.split(',');
+    return (
+      <ul>
+        {items.map((item, index) => (
+          <span key={index} className="text-sm py-1 px-2 mx-1 rounded-md bg-[#fdebaf] text-[#bc900c]">
+              {item.trim().replace('"', '').replace('"', '')}
+          </span>
+        ))}
+      </ul>
+    );
+  };
 
   return (
     <div
-      className="flex justify-between items-center bg-slate-200 dark:bg-[#000]"
+      className="flex justify-between items-center bg-[slate-200] dark:bg-[#000]"
       style={{ height: "100%" }}
     >
-      {showLetter && (
+      {showLeftLetter && (
         <div
-          className={`bg-[#f8f9fa] dark:bg-[#202123] ${
-            showLetter ? "p-2" : ""
-          } border-r-1 border-indigo-100  dark:border-r-0`}
+          className={`bg-[#f8f9fa] dark:bg-[#202123] border-r-1 border-indigo-100 dark:border-blue-950 overflow-auto scroll-smooth shrink-0`}
           style={{
             height: "calc(100vh - 5rem)",
             width: "20rem",
           }}
         >
           <div className="sticky top-0 z-50 dark:shadow-[rgba(255,255,255,.15)] backdrop-blur dark:bg-transparent transition-all">
-            <div className="flex justify-between items-center ">
+            <div className="flex justify-between items-center pt-2 px-2">
               <div>
                 <button
-                  className="inline-flex py-1 px-1 rounded-lg items-center text-white bg-indigo-500 border-0 focus:outline-none hover:bg-indigo-600"
-                  onClick={() => router.push("/")}
+                  className="inline-flex py-1 px-1 rounded-lg items-center text-white global-bg-color"
+                  onClick={newChat}
                 >
                   <RiAddCircleFill className="w-6 h-6" />
                   <span className="ml-1 pr-2 flex items-start flex-col leading-none">
@@ -92,105 +320,207 @@ export default function Dialogue() {
                 </button>
               </div>
               <div
-                className="cursor-pointer text-gray-400"
-                onClick={handlShowLetter}
+                className="cursor-pointer text-gray-400 hover:text-blue-600"
+                onClick={handlShowLeftLetter}
               >
-                <Image
-                  src="/icons/icon-shrink.svg"
-                  alt="收缩"
-                  width={32}
-                  height={32}
-                />
+                <RiBookReadLine className="text-2xl" />
+              </div>
+            </div>
+            <div className="w-full mt-4">
+              <div className="flex mx-auto flex-wrap justify-center border-b-2 border-blue-50 shadow-sm dark:border-blue-950 pb-2">
+                <a
+                  className={`cursor-pointer py-1 px-4 mx-4 rounded-lg ${
+                    historyOrChatList === 0
+                      ? "global-txt-color"
+                      : "dark:text-gray-500"
+                  }`}
+                  onClick={() => {
+                    setHistoryOrChatList(0);
+                  }}
+                >
+                  对话历史
+                </a>
+                <a
+                  className={`cursor-pointer py-1 px-4 mx-4 rounded-lg ${
+                    historyOrChatList === 1
+                      ? "global-txt-color"
+                      : "dark:text-gray-500"
+                  }`}
+                  onClick={() => {
+                    setHistoryOrChatList(1);
+                  }}
+                >
+                  助手列表
+                </a>
               </div>
             </div>
           </div>
-          {chatSessionList !== null && chatSessionList.length > 0 && (
-            <div className="pt-4">
-              <div className="mx-auto flex flex-col">
-                {chatSessionList.map((item) => (
-                  <div className="w-full mt-3 cursor-pointer" key={item.id} onClick={() => changeChat(item.id)}>
-                    <div className={`flex rounded-lg border ${uiMsgId==item.id?'bg-red-100 border-red-100  text-red-500':'border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 '} p-3 sm:flex-row flex-col`}>
-                      <div className="flex-grow text-ellipsis line-clamp-2">
-                        {item.name}
+
+          {historyOrChatList === 0 &&
+            chatSessionList !== null &&
+            chatSessionList.length > 0 && (
+              <div className="py-4 px-2">
+                <div className="mx-auto flex flex-col">
+                  {chatSessionList.map((item) => (
+                    <div
+                      className="w-full mt-3 cursor-pointer"
+                      key={item.id}
+                      onClick={() => changeChatSession(item.id)}
+                    >
+                      <div
+                        className={`flex rounded border ${
+                          conversationId == item.id &&
+                          listType === historyOrChatList
+                            ? "dark:border-[#0061ff] bg-blue-50 dark:bg-gray-800 text-[#0061ff]"
+                            : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400"
+                        } p-3 sm:flex-row flex-col`}
+                      >
+                        <div className="flex-grow text-ellipsis line-clamp-2">
+                          <h2 className="pb-2">{item.name}</h2>
+                          <p className="text-sm text-gray-400">
+                            {new Date(item.createdAt).toLocaleString()}
+                          </p>
+                        </div>
                       </div>
                     </div>
-                  </div>
-                ))}
+                  ))}
+                </div>
               </div>
-            </div>
-          )}
-          {chatSessionList === null ||
-            (chatSessionList.length == 0 && (
-              <div className="w-full px-5 text-gray-200 flex flex-col mt-20">
+            )}
+          {historyOrChatList === 0 &&
+            (chatSessionList === null || chatSessionList.length == 0) && (
+              <div className="w-full px-5 text-gray-200 dark:text-gray-600 flex flex-col mt-20">
+                <div className="flex justify-center">
+                  <RiCalendarCloseLine className="w-16 h-16 text-center" />
+                </div>
+                <div className="text-center mt-4">对话历史空空如也</div>
+              </div>
+            )}
+
+          {historyOrChatList === 1 &&
+            chatAiModelList !== null &&
+            chatAiModelList.length > 0 && (
+              <div className="py-4 px-2">
+                <div className="mx-auto flex flex-col">
+                  {chatAiModelList.map((item) => (
+                    <div
+                      className="w-full mt-3 cursor-pointer"
+                      key={item.id}
+                      onClick={() => changeChatListForLocal(item.id)}
+                    >
+                      <div
+                        className={`flex rounded border ${
+                          agentId == item.id && listType === historyOrChatList
+                            ? "dark:border-[#0061ff] bg-blue-50 dark:bg-gray-800 text-[#0061ff]"
+                            : "border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400"
+                        }  p-3 sm:flex-row flex-col`}
+                      >
+                        <div className="flex flex-row">
+                          <div className="shrink-0 inline-flex items-center justify-center h-16 overflow-hidden">
+                            <img src={item.avatar} className="h-16" />
+                          </div>
+                          <div className="flex-grow pl-2 pt-2">
+                            <h2 className="text-base mb-1 line-clamp-1 text-ellipsis">
+                              {item.name}
+                            </h2>
+                            <p className="text-sm line-clamp-1 text-ellipsis">
+                              {item.estimate}
+                            </p>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  ))}
+                </div>
+              </div>
+            )}
+          {historyOrChatList === 1 &&
+            (chatAiModelList === null || chatAiModelList.length == 0) && (
+              <div className="w-full px-5 text-gray-200 dark:text-gray-600 flex flex-col mt-20">
                 <div className="flex justify-center">
                   <RiCalendarCloseLine className="w-16 h-16 text-center" />
                 </div>
-                <div className="text-center mt-4">当前会话列表空空如也</div>
+                <div className="text-center mt-4">当前助手空空如也</div>
               </div>
-            ))}
+            )}
         </div>
       )}
 
-      <div
-        className="bg-[#fcfdff] dark:bg-[#1e1e1e]"
-        style={{
-          height: "calc(100vh - 5rem)",
-          width: showLetter ? "calc(100% - 20rem)" : "100%",
+      <ThemeProvider
+        appearance={themeBig.theme}
+        theme={{
+          algorithm:
+            themeBig.theme == "dark"
+              ? theme.darkAlgorithm
+              : theme.defaultAlgorithm,
         }}
       >
-        {showComponent && (
-          <ProChat
-            style={{
-              height: "100%",
-              width: "100%",
-            }}
-            initialChats={iniChatMessage}
-            actions={{
-              render: (defaultDoms) => {
-                if (showLetter) {
-                  return defaultDoms;
-                }
-                return [
-                  <a
-                    key="handlShowLetter"
-                    className="text-gray-400"
-                    onClick={handlShowLetter}
-                  >
-                    <Image
-                      src="/icons/icon-shrink.svg"
-                      alt="收缩"
-                      width={32}
-                      height={32}
-                    />
-                  </a>,
-                  ...defaultDoms,
-                ];
-              },
-              flexConfig: {
-                gap: 24,
-                direction: "horizontal",
-                justify: "space-start",
-              },
-            }}
-            request={async (messages) => {
-              const response = await fetch( getApiUrl() +"/gpts/chat/submit", {
-                method: "POST",
-                headers: {
-                  "Content-Type": "application/json;charset=UTF-8",
-                  "Authorization": `Bearer ${getUserSession()}`
+        <div
+          className={`bg-[#fcfdff] dark:bg-[#202123] grow`}
+          style={{
+            height: "calc(100vh - 5rem)",
+          }}
+        >
+          {showComponent && (
+            <ProChat
+              className={
+                themeBig.theme == "dark"
+                  ? BarkCustomClassName
+                  : LightCustomClassName
+              }
+              style={{
+                height: "100%",
+                width: "100%",
+              }}
+              initialChats={iniChatMessage}
+              //helloMessage ={helloMessage}
+              actions={{
+                render: (defaultDoms) => {
+                  if (showLeftLetter) {
+                    return defaultDoms;
+                  }
+                  return [
+                    <a
+                      key="handlShowLeftLetter"
+                      className="text-gray-400 hover:text-blue-600"
+                      onClick={handlShowLeftLetter}
+                    >
+                      <RiBookReadLine className="text-2xl" />
+                    </a>,
+                    ...defaultDoms,
+                  ];
                 },
-                body: JSON.stringify({
-                  content: messages[messages.length - 1].content,
-                  conversationId: msgId,
-                }),
-              });
-              if (!response.ok || !response.body) {
-                const errorResponseOptions = {
-                  status: 500,
-                  statusText: "网络错误,请重试!",
-                };
-                return new Response(null, errorResponseOptions);
-              } else {
+                flexConfig: {
+                  gap: 24,
+                  direction: "horizontal",
+                  justify: "space-start",
+                },
+              }}
+              request={async (messages) => {
+                const response = await fetch(
+                  getApiUrl() + "/gpts/chat/submit",
+                  {
+                    method: "POST",
+                    headers: {
+                      "Content-Type": "application/json;charset=UTF-8",
+                      Authorization: `Bearer ${getUserSession()}`,
+                    },
+                    body: JSON.stringify({
+                      content: messages[messages.length - 1].content,
+                      conversationId: conversationId.includes("guid_0000_")
+                        ? ""
+                        : conversationId,
+                      agentId: agentId,
+                    }),
+                  }
+                );
+                if (!response.ok || !response.body) {
+                  const errorResponseOptions = {
+                    status: 500,
+                    statusText: "网络错误,请重试!",
+                  };
+                  return new Response(null, errorResponseOptions);
+                } else {
                   const reader = response.body.getReader();
                   const decoder = new TextDecoder("utf-8");
                   const encoder = new TextEncoder();
@@ -209,26 +539,29 @@ export default function Dialogue() {
                               stream: true,
                             });
 
-                            var linesArray = chunk.split('\n');
+                            var linesArray = chunk.split("\n");
                             for (var i = 0; i < linesArray.length - 1; i++) {
                               const line = linesArray[i];
                               const message = line.replace("data: ", "");
                               const parsed = JSON.parse(message);
-                              if(!parsed.finish && parsed.answer){
+                              if (!parsed.finish && parsed.answer) {
                                 controller.enqueue(
                                   encoder.encode(parsed.answer)
                                 );
-                              }
-                              else{
-                                if(parsed.need_login){
+                              } else {
+                                if (parsed.finish) {
+                                  setConversationId(parsed.conversation_id);
+                                }
+                                if (parsed.need_login) {
                                   changeLoginShow(true);
                                   controller.enqueue(
-                                    encoder.encode('请登录后使用')
+                                    encoder.encode("请登录后使用")
                                   );
-                                }
-                                else if(parsed.need_pay){
+                                } else if (parsed.need_pay) {
                                   controller.enqueue(
-                                    encoder.encode('当前智能体需要会员才能使用,去开通:[会员套餐](/vip)')
+                                    encoder.encode(
+                                      "当前智能体需要会员才能使用,去开通:[会员套餐](/vip)"
+                                    )
                                   );
                                 }
                               }
@@ -244,21 +577,93 @@ export default function Dialogue() {
                     },
                   });
                   return new Response(readableStream);
-              }
-            }}
-          />
-        )}
-        {
-          !showComponent && (
+                }
+              }}
+            />
+          )}
+          {!showComponent && (
             <div className="w-full px-5 text-gray-300 flex flex-col mt-40">
               <div className="flex justify-center">
-                <RiLoader2Fill  className="animate-spin h-16 w-16" />
+                <RiLoader2Fill className="animate-spin h-16 w-16" />
               </div>
               <div className="text-center mt-4">会话加载中</div>
             </div>
-          )
-        }
-      </div>
+          )}
+        </div>
+      </ThemeProvider>
+
+      {employeeSearch != null && (
+        <div
+          className={`bg-[#ecf1f9] dark:bg-[#202123] border-l-1 border-indigo-100 dark:border-blue-950 overflow-auto scroll-smooth shrink-0`}
+          style={{
+            height: "calc(100vh - 5rem)",
+            width: "25rem",
+          }}
+        >
+          <div className="">
+            <div className="h-96 w-11/12 mx-auto relative">
+              <div className="h-72 w-10/12 mx-auto absolute rounded-lg bg-[#f6f9fd] top-20 left-0 right-0 shadow-md dark:bg-gray-950"></div>
+              <div className="h-64 w-full absolute rounded-lg bg-[#fff] top-28 text-center shadow-lg dark:bg-gray-900">
+                <div className="mt-16 text-xl p-3 h-12 overflow-hidden  dark:text-gray-500">
+                  {employeeSearch.title}
+                </div>
+                <div className="p-3 h-12 overflow-hidden">
+                  {readTags(employeeSearch.tags)}
+                </div>
+                <div className="p-5 text-sm text-gray-500">
+                  <div className="grid grid-cols-3 gap-4">
+                    <div className="text-center">
+                      <p className="dark:text-gray-700">被雇佣</p>
+                      <p className="text-2xl text-gray-700  dark:text-gray-500">{employeeSearch.hireCount}</p>
+                    </div>
+                    <div className="text-center">
+                      <p className="dark:text-gray-700">已服务</p>
+                      <p className="text-2xl text-gray-700  dark:text-gray-500">{employeeSearch.serviceCount}</p>
+                    </div>
+                    <div className="text-center">
+                      <p className="dark:text-gray-700">业绩单</p>
+                      <p className="text-2xl text-gray-700  dark:text-gray-500">{employeeSearch.achievementCount}w</p>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <div className="h-32 w-32 absolute rounded-full bg-[#fff] top-10 left-0 right-0 mx-auto overflow-hidden">
+                <img
+                  className="object-top"
+                  src={employeeSearch.avatar}
+                ></img>
+              </div>
+            </div>
+          </div>
+
+          <div className="w-11/12 mx-auto rounded-lg bg-[#fff] dark:bg-gray-900 shadow-md mt-4">
+            <div className="p-3">
+              <h2 className="global-txt-color px-3">个人简介</h2>
+              <div className="leading-6 text-sm text-gray-700 m-3 p-3 rounded-lg bg-[#f9fafb] dark:bg-gray-950">
+                {employeeSearch.intro}
+              </div>
+            </div>
+          </div>
+
+          <div className="w-11/12 mx-auto rounded-lg bg-[#fff] dark:bg-gray-900 shadow-md mt-6 ">
+            <div className="p-3">
+              <h2 className="global-txt-color px-3">自我评价</h2>
+              <div className="leading-6 text-sm text-gray-700 m-3 p-3 rounded-lg bg-[#f9fafb] dark:bg-gray-950">
+              {employeeSearch.estimate}
+              </div>
+            </div>
+          </div>
+
+          <div className="w-11/12 mx-auto rounded-lg bg-[#fff] dark:bg-gray-900 shadow-md my-6">
+            <div className="p-3">
+              <h2 className="global-txt-color px-3">技能</h2>
+              <div className="leading-6 text-sm text-gray-700 m-3 p-3 rounded-lg bg-[#f9fafb] dark:bg-gray-950">
+              {employeeSearch.skill}
+              </div>
+            </div>
+          </div>
+        </div>
+      )}
     </div>
   );
 }

+ 32 - 3
web/src/app/globals.scss

@@ -4,6 +4,17 @@
 
 $white: #fff;
 
+.global-txt-color{
+  background: linear-gradient(90deg, #0061ff, #6c62fd);
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+}
+
+.global-bg-color{
+  background: linear-gradient(90deg, #0061ff, #6c62fd);
+}
+
 @layer base {
   :root {
     --background: 0 0% 100%;
@@ -285,9 +296,27 @@ select:focus {
 }
 
 .login-box .login-div .login {
-  box-shadow: 0 0 60px rgb(226 97 89 / 20%);
+  box-shadow: 0 0 60px hsl(217.18deg 100% 50% / 20%);
 }
 
-.login-box .close {
-  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAACQCAYAAADnRuK4AAAACXBIWXMAACxLAAAsSwGlPZapAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAPTSURBVHgB7d29ThRRGIDh7wyLWGlFQoJ7A4rBBgs7CxPs8QqwsRMra3u8Am/AYGOnnR2yGxNZxM4KsgnQURrY43y7IQgL7PzPnHPeJzFCmGLIvMz/OSsCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoCxGPLGwsNSemjJrVuxzY+SOtWZPrN0UidZ7va19aQBdxygyqyaSZRHbtlaOxcqXJq1jWl4E9PDR4xfG2ncazuWfaUgnYld/b3d3pUaLi0sPrMinq9ZRDcS83vnZ2RDHOB/QcM/Tku83LaN/6SciK3VFNCkeNdobRc9c2xNF4rgokjeTltEN14o34P14Q0rFksSj9OfG2Im/S9M4H1C8D11OtFgNESWN54yev4ljnA8o6cY5W7aqiNLGo9Is2xTu74FEUp0zVBFRlnhG4itHx7gf0PAyOJ0yI8oez/AQtimOcT6g01vyYXgFk1IZEeWJZ7j3mYnWxTHOB/Sr292L70Ws1B1RrnhMvO7TdrW35d7NRG/uROc6dOS8T5Q7npasbHfrvdGZlTcBqToiCjke5VVAqsqIQo9HeReQqiIi4hnxMiBVZkTEc87bgFQZERHPRV4HpIqMiHjGeR+QKiKiaf2aeMYEEZDKG5H+TzzjgglI5XvUkIHn8aigAlKVRRRAPCq4gFTpEQUSjwoyIFVaRAHFo4INSBUeUWDxqKADUoVFFGA8KviAVO6IAo1HtQS52YHIyV8JEoewgg5hdQ9erAsn0QWeRIcYEZfxBV/GhxYRNxJLEFJEPMooSSgR8TC1RCFE5MPQ5kTyvgzWlMGLTRNEQEW8SdiEwYtNxCutN7l0h7nOwYtNxUv117nm8QQRXcSwnqtMeLZFROcYWHhZwgejRDTC0Ob/pXyqTkRMrnAu4ysZoUfE9C4q5/s8IUfkRUBNGDEaakReTDQeTQ033D1Jq+A3CXMPXmSi8erpRONNiEdtx3uQPHesJbIvxTE+TDT+RNIq8R3mXBHZZJOmN4kPE42n2/tU8AJ85oiMbYtjnA8o1UaqcPREloiy7LXq5sMhLNlE4zUMvUkdkUk/aXrdnA9oMC3vJ26gGsdtpYrIwYnGp8Rxh/3+8ezc/FF03af2NGDQ38FB/2hubv5bvC5P42/HL/HjdTy18nbnR8e5jzpwPiB1eNDfnW3Pb5iBfuaW6InoTPxv34r5GP9Vr/U6nT9SM40oXsevw3UUuSsakr7pOJDPcjt65WI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQJX+ATc9oF5n57RZAAAAAElFTkSuQmCC) 50% / cover;
+/* 支付弹窗样式 */
+.pay-box{
+  @apply justify-center items-center fixed inset-0 text-sm z-[1000];
+}
+.pay-box .pay-mask{
+  @apply fixed inset-0 bg-gray-500 opacity-60;
+}
+.pay-box .pay-div {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  border-radius: 8px;
+}
+.div-center {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  border-radius: 8px;
 }

+ 67 - 22
web/src/app/page.tsx

@@ -1,15 +1,29 @@
 "use client";
 import { useRef, useState, useEffect } from "react";
 import { useRouter } from "next/navigation";
-import { RiSearch2Line } from "react-icons/ri";
+import {
+  RiSearch2Line,
+  RiAddCircleFill,
+  RiCalendarCloseLine,
+  RiLoader2Fill,
+} from "react-icons/ri";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 import { getCategoryList, getEmployeeSearch } from "@/api/client";
 import { CategoryListResult, EmployeeSearchResult } from "@/utils/clientsApis";
+import { SetChatLastUsedForLocalStorage } from "@/utils/chat";
 
 export default function Home() {
   const router = useRouter();
   const boxRef = useRef<HTMLDivElement | null>(null);
-  const { changeMenuIndex } = useGlobalContext();
+  const {
+    changeMenuIndex,
+    listType,
+    changeListType,
+    agentId,
+    changeAgentId,
+    conversationId,
+    changeConversationId,
+  } = useGlobalContext();
   const [categoryList, setCategoryList] = useState<CategoryListResult[]>([]);
   const [employeeList, setEmployeeList] = useState<EmployeeSearchResult[]>([]);
   const [categoryId, setCategoryId] = useState<number>(0);
@@ -28,6 +42,9 @@ export default function Home() {
           ...prevEmployeeList,
           ...data.data.data, // Append new data to the existing list
         ]);
+        if (data.data.total <= 20) {
+          setIsBottom(true);
+        }
       } else {
         setIsBottom(true);
       }
@@ -62,10 +79,14 @@ export default function Home() {
     setPage(1);
     setEmployeeList([]);
   };
-  
-  const addChat = (id:number) => {
-    router.push('/dialogue?id='+id);
-  }
+
+  const addChat = (id: number) => {
+    changeListType(1);
+    changeAgentId(id);
+    changeConversationId("");
+    SetChatLastUsedForLocalStorage({listType:1,agentId:id,conversationId:''});
+    router.push("/dialogue");
+  };
 
   const [isFocused, setIsFocused] = useState(false);
   const handleFocus = () => setIsFocused(true);
@@ -82,7 +103,7 @@ export default function Home() {
 
     page_getEmployeeSearch();
     setPage(page + 1);
-  }, [isBottom, categoryId]);
+  }, [isBottom, categoryId, keyword]);
 
   return (
     <div
@@ -93,24 +114,28 @@ export default function Home() {
     >
       <div className="sticky top-0 z-50 dark:shadow-[rgba(255,255,255,.15)] backdrop-blur dark:bg-transparent transition-all">
         <div className="flex flex-row justify-center pt-9">
-          <div className="basis-1/2 relative">
+          <div className="w-11/12 md:basis-1/2 2xl:basis-1/3 relative">
             <input
-              className="w-full border rounded-full pl-4 pr-12 py-2 focus:border-red-300 dark:focus:border-slate-700 focus:outline-none"
+              className="w-full border rounded-full pl-4 pr-12 py-2 focus:border-[#0061ff] dark:focus:border-slate-700 focus:outline-none"
               type="text"
               placeholder="输入关键词查询智能体..."
               value={keyword}
               onChange={handleKeywordChange}
               onFocus={handleFocus}
               onBlur={handleBlur}
+              onKeyDown={(e) => {
+                if (e.key === "Enter") {
+                  handleSearch();
+                }
+              }}
             />
-            <button
-              className={`absolute right-0 top-0 py-2 mr-4 ${
-                isFocused ? "text-red-500" : "text-gray-400"
+            <div
+              className={`absolute right-0 top-0 py-2 mr-4 cursor-pointer ${
+                isFocused ? "text-[#0061ff]" : "text-gray-400"
               }`}
-              onClick={handleSearch}
             >
               <RiSearch2Line size={24} />
-            </button>
+            </div>
           </div>
         </div>
         {categoryList !== null && categoryList.length > 0 && (
@@ -118,7 +143,11 @@ export default function Home() {
             <div className="container mx-auto flex flex-wrap justify-center gap-2 text-sm">
               <button
                 type="button"
-                className="px-4 py-1 border rounded-full text-gray-500 bg-white hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+                className={`px-4 py-1 border rounded-full ${
+                  categoryId == 0
+                    ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+                    : "text-gray-500 dark:text-gray-500 bg-white dark:bg-gray-800 hover:text-gray-900 dark:hover:text-white hover:bg-blue-50 dark:hover:bg-gray-800"
+                }`}
                 onClick={() => selectCategory(0)}
               >
                 全部分类
@@ -129,8 +158,8 @@ export default function Home() {
                   type="button"
                   className={`px-4 py-1 border rounded-full ${
                     categoryId == item.id
-                      ? "bg-red-100 text-red-500"
-                      : "text-gray-500 bg-white hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+                      ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+                      : "text-gray-500 dark:text-gray-500 bg-white dark:bg-gray-800 hover:text-gray-900 dark:hover:text-white hover:bg-blue-50 dark:hover:bg-gray-800"
                   }`}
                   onClick={() => selectCategory(item.id)}
                 >
@@ -147,15 +176,21 @@ export default function Home() {
           <div className="container px-5 py-20 mx-auto">
             <div className="flex flex-wrap -m-4 text-sm">
               {employeeList.map((item) => (
-                <div className="p-4 md:w-1/3" key={item.id}>
-                  <a onClick={()=>addChat(item.id)} className="relative block overflow-hidden rounded-lg border border-gray-100 dark:border-slate-700 p-4 sm:p-6 lg:p-8 shadow-md cursor-pointer">
-                    <span className="absolute inset-x-0 bottom-0 h-2 bg-gradient-to-r from-red-300 via-purple-300 to-green-300"></span>
+                <div
+                  className="p-4 lg:w-1/2 xl:w-1/3 transition transform hover:-translate-y-1 motion-reduce:transition-none motion-reduce:hover:transform-none"
+                  key={item.id}
+                >
+                  <a
+                    onClick={() => addChat(item.id)}
+                    className="relative block overflow-hidden rounded-lg border border-gray-100 dark:border-slate-700 p-4 sm:p-6 lg:p-8 shadow-md cursor-pointer hover:bg-blue-50 dark:hover:bg-blue-950"
+                  >
+                    <span className="absolute inset-x-0 bottom-0 h-2 bg-gradient-to-r from-[#0061ff] via-[#347bec] to-[#6c62fd]"></span>
                     <div className="sm:flex sm:justify-between sm:gap-4">
                       <div>
-                        <h3 className="text-lg font-bold text-gray-900 dark:text-white sm:text-xl">
+                        <h3 className="text-lg font-bold text-gray-900 dark:text-white sm:text-xl text-ellipsis line-clamp-1 h-8">
                           {item.title}
                         </h3>
-                        <p className="mt-1 text-xs font-medium text-gray-600 text-ellipsis line-clamp-2">
+                        <p className="mt-1 text-xs font-medium text-gray-600 text-ellipsis line-clamp-2  h-8">
                           {item.skill}
                         </p>
                       </div>
@@ -184,6 +219,16 @@ export default function Home() {
           )}
         </section>
       )}
+
+      {employeeList === null ||
+        (employeeList.length === 0 && (
+          <div className="w-full px-5 text-gray-200 flex flex-col mt-20">
+            <div className="flex justify-center">
+              <RiCalendarCloseLine className="w-16 h-16 text-center" />
+            </div>
+            <div className="text-center mt-4">查询结果为空</div>
+          </div>
+        ))}
     </div>
   );
 }

+ 173 - 39
web/src/app/user-center/page.tsx

@@ -1,54 +1,188 @@
 "use client";
-import { useState, useEffect } from "react";
+import { useRef, useState, useEffect } from "react";
+import { message } from "antd";
+import { getUserInfo, userPassword } from "@/api/client";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 
 const UserCenter = () => {
   const { changeMenuIndex } = useGlobalContext();
+  const [nickName, setNickName] = useState<string>("");
+  const [phone, setPhone] = useState<string>("");
+  const [email, setEmail] = useState<string>("");
+
+  const [oldPassword, setOldPassword] = useState<string>("");
+  const [newPassword, setNewPassword] = useState<string>("");
+
+  const inputRef_nickName = useRef<HTMLInputElement>(null);
+  const inputRef_phone = useRef<HTMLInputElement>(null);
+  const inputRef_email = useRef<HTMLInputElement>(null);
+  
+  const inputRef_oldPassword = useRef<HTMLInputElement>(null);
+  const inputRef_newPassword = useRef<HTMLInputElement>(null);
+
+  const handleSaveUserInfo = (e: React.FormEvent) => {
+    e.preventDefault();
+    if (nickName) {
+      if (email) {
+        message.success("修改成功!");
+      } else {
+        inputRef_email.current?.focus();
+      }
+    } else {
+      inputRef_nickName.current?.focus();
+    }
+  };
+
+  const handleUpdatePassword = (e: React.FormEvent) => {
+    e.preventDefault();
+    if (oldPassword) {
+      if (newPassword) {
+        userPassword({ password: oldPassword, password2: newPassword }).then((data) => {
+          message.success("密码更新成功");
+          setOldPassword("");
+          setNewPassword("");
+        });
+      } else {
+        inputRef_newPassword.current?.focus();
+      }
+    } else {
+      inputRef_oldPassword.current?.focus();
+    }
+  };
+
+  const logout = () => {
+    localStorage.removeItem("chat-last-used");
+    localStorage.removeItem("chat-model-list");
+    localStorage.removeItem("session");
+    message.success("账户退出成功!");
+    setTimeout(() => {
+      window.location.href = "/";
+    }, 600);
+  };
 
   useEffect(() => {
     changeMenuIndex(0);
+    getUserInfo().then((data) => {
+      if (data.code == 0) {
+        setNickName(data.data.nickname);
+        setPhone(data.data.mobile);
+        setEmail(data.data.email);
+      }});
   }, []);
 
   return (
-    <div
-      style={{
-        display: "flex",
-        flexDirection: "column",
-        alignItems: "center",
-        padding: "20px",
-      }}
-    >
-      {/* Avatar Section */}
-      <div style={{ marginBottom: "20px" }}>
-        <img
-          src="https://via.placeholder.com/150"
-          alt="User Avatar"
-          style={{ width: "150px", height: "150px", borderRadius: "50%" }}
-        />
-      </div>
-
-      {/* User Information */}
-      <div style={{ textAlign: "center", marginBottom: "30px" }}>
-        <h2>Username</h2>
-        <p>email@example.com</p>
-      </div>
-
-      {/* Project List Section */}
-      <div style={{ width: "100%", maxWidth: "800px" }}>
-        <h3>My Projects</h3>
-        <ul style={{ listStyleType: "none", padding: "0" }}>
-          <li style={{ padding: "10px", borderBottom: "1px solid #ddd" }}>
-            Project 1
-          </li>
-          <li style={{ padding: "10px", borderBottom: "1px solid #ddd" }}>
-            Project 2
-          </li>
-          <li style={{ padding: "10px", borderBottom: "1px solid #ddd" }}>
-            Project 3
-          </li>
-        </ul>
+    <>
+      <div className="container px-5 py-7 mx-auto">
+        <div className="w-full border border-slate-100 dark:border-blue-950 rounded-md mb-6 overflow-hidden">
+          <div className="p-3 text-lg border-b-1 border-slate-100 dark:border-slate-900  bg-gray-50 dark:bg-blue-950 text-[#0061ff]">
+            个人信息
+            <span className="text-red-600 float-right cursor-pointer transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 hover:text-red-700 duration-300" onClick={logout}>退出登录</span>
+          </div>
+          <div className="p-5">
+            <div className="flex flex-col items-center justify-center">
+              <img
+                className="w-28 h-28 mb-3 mt-6 rounded-full shadow-lg"
+                src="/images/avatar.png"
+                alt="kakawanzi"
+              />
+              <h5 className="mb-1 text-xl font-medium text-gray-900 dark:text-gray-300">
+                kakawanzi
+              </h5>
+              <div className="container px-5 py-10 mx-auto">
+                <div className="flex lg:w-5/12 w-full flex-col mx-auto px-8 sm:space-x-4 sm:space-y-0 space-y-4 sm:px-0 items-end">
+                  <div className="relative flex-grow w-full">
+                    <label className="leading-7 text-sm text-gray-600 dark:text-gray-400">
+                      昵称
+                    </label>
+                    <input
+                      type="text"
+                      autoComplete="new-password"
+                      className="w-full bg-gray-50 dark:bg-[#202123] bg-opacity-50 rounded border border-gray-300 dark:border-blue-900 focus:border-blue-950 focus:bg-transparent focus:ring-2 focus:ring-blue-950 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
+                      ref={inputRef_nickName}
+                      value={nickName}
+                      onChange={(e) => setNickName(e.target.value)}
+                    />
+                  </div>
+                  <div className="relative flex-grow w-full">
+                    <label className="leading-7 text-sm text-gray-600 dark:text-gray-400">
+                      手机号码
+                    </label>
+                    <input
+                      type="phone"
+                      autoComplete="new-password"
+                      className="w-full bg-gray-50 dark:bg-[#202123] bg-opacity-50 rounded border border-gray-300 dark:border-blue-900 focus:border-blue-950 focus:bg-transparent focus:ring-2 focus:ring-blue-950 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
+                      ref={inputRef_phone}
+                      value={phone}
+                      onChange={(e) => setPhone(e.target.value)}
+                    />
+                  </div>
+                  <div className="relative flex-grow w-full">
+                    <label className="leading-7 text-sm text-gray-600 dark:text-gray-400">
+                      邮箱
+                    </label>
+                    <input
+                      type="email"
+                      autoComplete="new-password"
+                      className="w-full bg-gray-50 dark:bg-[#202123] bg-opacity-50 rounded border border-gray-300 dark:border-blue-900 focus:border-blue-950 focus:bg-transparent focus:ring-2 focus:ring-blue-950 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
+                      ref={inputRef_email}
+                      value={email}
+                      onChange={(e) => setEmail(e.target.value)}
+                    />
+                  </div>
+                  <div className="relative flex-grow w-full pt-6 text-center">
+                    <button className="text-white global-bg-color border-0 py-2 px-12 focus:outline-none hover:bg-indigo-600 rounded-full" onClick={handleSaveUserInfo}>
+                      保存
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div className="w-full border border-slate-100 dark:border-blue-950 rounded-md mb-6 overflow-hidden">
+          <div className="p-3 text-lg border-b-1 border-slate-100 dark:border-slate-900  bg-gray-50 dark:bg-blue-950 text-[#0061ff]">
+            修改密码
+          </div>
+          <div className="p-3">
+            <div className="container px-5 py-10 mx-auto">
+              <div className="flex lg:w-5/12 w-full flex-col mx-auto px-8 sm:space-x-4 sm:space-y-0 space-y-4 sm:px-0 items-end">
+                <div className="relative flex-grow w-full">
+                  <label className="leading-7 text-sm text-gray-600 dark:text-gray-400">
+                    旧密码
+                  </label>
+                  <input
+                    type="password"
+                    autoComplete="new-password"
+                    className="w-full bg-gray-50 dark:bg-[#202123] bg-opacity-50 rounded border border-gray-300 dark:border-blue-900 focus:border-blue-950 focus:bg-transparent focus:ring-2 focus:ring-blue-950 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
+                    ref={inputRef_oldPassword}
+                    value={oldPassword}
+                    onChange={(e) => setOldPassword(e.target.value)}
+                  />
+                </div>
+                <div className="relative flex-grow w-full">
+                  <label className="leading-7 text-sm text-gray-600 dark:text-gray-400">
+                    新密码
+                  </label>
+                  <input
+                    type="password"
+                    autoComplete="new-password"
+                    className="w-full bg-gray-50 dark:bg-[#202123] bg-opacity-50 rounded border border-gray-300 dark:border-blue-900 focus:border-blue-950 focus:bg-transparent focus:ring-2 focus:ring-blue-950 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
+                    ref={inputRef_newPassword}
+                    value={newPassword}
+                    onChange={(e) => setNewPassword(e.target.value)}
+                  />
+                </div>
+                <div className="relative flex-grow w-full pt-6 text-center">
+                  <button className="text-white global-bg-color border-0 py-2 px-12 focus:outline-none hover:bg-indigo-600 rounded-full" onClick={handleUpdatePassword}>
+                    保存
+                  </button>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
       </div>
-    </div>
+    </>
   );
 };
 

+ 62 - 20
web/src/app/vip/page.tsx

@@ -1,6 +1,7 @@
 "use client";
 import { useState, useEffect } from "react";
 import { useGlobalContext } from "@/providers/GlobalProvider";
+import { RiCloseFill } from 'react-icons/ri';
 
 interface Package {
   id: number;
@@ -50,26 +51,18 @@ const packages: Package[] = [
     basePoints: 3000,
     seniorPoints: 3000,
   },
-  {
-    id: 4,
-    name: "年度旗舰会员",
-    description:
-      "套餐包含20000积分通用AI模型(AI3.5Turbo等模型)、20000积分增强AI模型(AI4o大模型)、10000积分高级绘画额度",
-    price: "1999.00",
-    image:
-      "http://ai3666.oss-cn-beijing.aliyuncs.com/ai/2024-11-14/%C3%A7%C2%A8%C2%BF%C3%A5%C2%AE%C2%9A%C3%A8%C2%AE%C2%BE%C3%A8%C2%AE%C2%A1-4.jpg",
-    days: 365,
-    basePoints: 20000,
-    seniorPoints: 20000,
-  },
 ];
 
 export default function VipPage() {
   const { changeMenuIndex } = useGlobalContext();
+  const [showBuyBox, setShowBuyBox] = useState(false);
 
-  useEffect(() =>{
-    changeMenuIndex(3);
+  const closeBuyBox = () => {
+    setShowBuyBox(false);
+  }
 
+  useEffect(() => {
+    changeMenuIndex(3);
   }, []);
 
   return (
@@ -86,9 +79,12 @@ export default function VipPage() {
           </div>
 
           <div className="px-4 py-8 sm:px-6 sm:py-12 lg:px-8 lg:py-16">
-            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 sm:items-stretch md:grid-cols-3 md:gap-8">
+            <div className="grid grid-cols-1 gap-2 md:grid-cols-2 md:gap-4  xl:grid-cols-3 xl:gap-8">
               {packages.map((pkg) => (
-                <div className="rounded-2xl border border-gray-200 shadow-sm dark:border-gray-500" key={pkg.id}>
+                <div
+                  className="rounded-2xl border border-gray-200 shadow-sm dark:border-gray-500"
+                  key={pkg.id}
+                >
                   <div className="p-6 sm:px-8">
                     <img
                       className="lg:h-48 md:h-36 w-full object-cover object-center rounded-2xl"
@@ -96,20 +92,20 @@ export default function VipPage() {
                       alt={pkg.name}
                     />
                     <h2 className="mt-2 text-lg font-medium text-gray-900 dark:text-white">
-                        {pkg.name}
+                      {pkg.name}
                     </h2>
                     <p className="mt-2 text-gray-700 dark:text-gray-500">
-                        {pkg.description}
+                      {pkg.description}
                     </p>
                     <p className="mt-2 sm:mt-4">
                       <strong className="text-3xl font-bold text-gray-900 sm:text-4xl  dark:text-gray-300">
-                      {pkg.days}
+                        {pkg.days}
                       </strong>
                       <span className="text-sm font-medium text-gray-700 dark:text-gray-500 ml-1">
                       </span>
                     </p>
-                    <button className="mt-4 block w-full rounded border border-indigo-600 px-12 py-3 text-center text-sm font-medium text-white bg-indigo-600 focus:outline-none focus:ring sm:mt-6 transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
+                    <button className="mt-4 block w-full rounded px-12 py-3 text-center text-sm font-medium text-white global-bg-color focus:outline-none focus:ring sm:mt-6 transition ease-in-out hover:-translate-y-1" onClick={() => setShowBuyBox(true)}>
                       ¥{pkg.price}
                     </button>
                   </div>
@@ -132,6 +128,52 @@ export default function VipPage() {
           </div>
         </div>
       </section>
+
+      {showBuyBox && (
+        <div className="pay-box">
+          <div className="pay-mask"></div>
+          <div className="pay-div p-7 bg-white dark:bg-gray-900 text-base rounded w-full max-w-screen-lg">
+            <RiCloseFill className="absolute right-2.5 top-2.5 w-8 h-8 cursor-pointer transform duration-500 ease-in-out hover:scale-125 text-gray-500" onClick={closeBuyBox}></RiCloseFill>
+            <h2 className="text-xl global-txt-color">会员套餐支付</h2>
+            <div className="py-10 grid grid-cols-1 md:grid-cols-2 md:gap-8">
+              <div className="">
+                <div className="flex">
+                  <div className="w-20 text-nowrap text-left h-6 leading-8 shrink-0">
+                    需要支付:
+                  </div>
+                  <div className="h-6 text-red-700 text-2xl">¥19.90</div>
+                </div>
+                <div className="mt-4 flex ">
+                  <div className="w-20 text-nowrap text-left shrink-0">套餐名称:</div>
+                  <div className="text-left">入门体验会员</div>
+                </div>
+                <div className="mt-4 flex">
+                  <div className="w-20 text-nowrap text-left shrink-0">套餐描述:</div>
+                  <div className="text-left w-5/6">
+                    套餐包含500积分通用AI模型(AI3.5Turbo等模型)、100积分增强AI模型(AI4o大模型)、30积分高级绘画额度
+                  </div>
+                </div>
+              </div>
+              <div className="text-center py-4 md:py-0 mt-5 md:mt-0 border-t md:border-t-0">
+                <div>
+                  请在 <span className="text-red-700">00:03:34</span>{" "}
+                  时间内完成支付!
+                </div>
+                <div className="w-full pt-3">
+                  <div className="relative w-72 mx-auto p-3 border rounded">
+                    <img
+                      src="/temp/wxpay.png"
+                      className="w-12 bg-white p-1 div-center"
+                    />
+                    <img src="/temp/qrcode.png" />
+                  </div>
+                </div>
+                <div className="mt-2">打开微信扫码支付</div>
+              </div>
+            </div>
+          </div>
+        </div>
+      )}
     </>
   );
 }

+ 11 - 15
web/src/components/Header/index.tsx

@@ -1,8 +1,6 @@
 "use client";
 import { useState, useEffect } from "react";
 import { useRouter } from "next/navigation";
-import { Tooltip } from "@nextui-org/react";
-import Image from "next/image";
 import ThemeMode from "@/components/ThemeMode";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 import { getUserSession } from "@/utils/user";
@@ -10,7 +8,7 @@ import { getUserInfo } from "@/api/client";
 
 export default function Header({ name }: { name: string }) {
   const router = useRouter();
-  const { loginShow,changeLoginShow } = useGlobalContext();
+  const { loginShow, changeLoginShow } = useGlobalContext();
   const [isLogin, setIsLogin] = useState(false);
   const [nickName, setNickName] = useState("");
 
@@ -32,9 +30,9 @@ export default function Header({ name }: { name: string }) {
   return (
     <header className="sticky w-full flex gap-4 justify-between items-center top-0 shadow dark:shadow-none backdrop-blur dark:bg-transparent transition-all py-2 px-1 z-50">
       <div className="flex gap-3 justify-between items-center grow-0 shrink-0">
-        <Image src="/logo.svg" alt="logo" width={40} height={40} />
+        <img src="/logo.png" alt="logo" width={40} height={40} />
         <div>
-          <h1 className="font-black text-xl">{name}</h1>
+          <h1 className="global-txt-color text-xl">{name}</h1>
         </div>
       </div>
       <div className="absolute top-2/4 left-1/2 -translate-x-1/2 -translate-y-2/4 max-md:hidden"></div>
@@ -48,16 +46,14 @@ export default function Header({ name }: { name: string }) {
           </a>
         )}
         {!isLogin && (
-          <Tooltip showArrow content="登录" placement="bottom">
-            <button
-              className="inline-flex py-2 px-2 rounded-lg items-center text-white bg-indigo-500 border-0 focus:outline-none hover:bg-indigo-600 mr-3"
-              onClick={Login}
-            >
-              <span className="ml-1 pr-2 flex items-start flex-col leading-none">
-                <span className="title-font font-medium">用户登录</span>
-              </span>
-            </button>
-          </Tooltip>
+          <button
+            className="inline-flex py-2 px-2 rounded-lg items-center text-white global-bg-color border-0 focus:outline-none mr-3"
+            onClick={Login}
+          >
+            <span className="ml-1 pr-2 flex items-start flex-col leading-none">
+              <span className="title-font font-medium">用户登录</span>
+            </span>
+          </button>
         )}
         <ThemeMode />
       </div>

+ 52 - 142
web/src/components/Login/index.tsx

@@ -2,30 +2,26 @@
 import { useRef, useContext, useEffect, useState } from "react";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 import { userLogin } from "@/api/client";
-import { UserLoginResult } from "@/utils/clientsApis";
 import { message } from "antd";
 import { setUserSession } from "@/utils/user";
+import { RiCloseFill } from 'react-icons/ri';
 
 export default function Login() {
   const { loginShow, changeLoginShow } = useGlobalContext();
-  const [showRegister, setShowRegister] = useState(false);
   const [email, setEmail] = useState<string>("");
   const [password, setPassword] = useState<string>("");
-  const [confirmPassword, setConfirmPassword] = useState<string>("");
 
-  const inputRef_email =  useRef<HTMLInputElement>(null);
+  const inputRef_email = useRef<HTMLInputElement>(null);
   const inputRef_password = useRef<HTMLInputElement>(null);
 
   const handleSubmit = (e: React.FormEvent) => {
     e.preventDefault();
-    console.log("Email:", email);
-    console.log("Password:", password);
     if (email) {
       if (password) {
         userLogin({ username: email, password: password }).then((data) => {
           if (data.code == 0) {
             message.success("登录成功");
-            setUserSession(data.data.token);
+            setUserSession(data.data.token, data.data.expireTime);
             changeLoginShow(false);
           } else {
             message.error("用户名或密码错误");
@@ -45,155 +41,69 @@ export default function Login() {
     setPassword("");
   };
 
-  const showRegisterBox = () => {
-    setShowRegister(!showRegister);
-  };
-
   return (
     <>
       {loginShow && (
         <div className="login-box">
           <div className="login-mask"></div>
-          <div className="login-div dark:bg-gray-400">
-            <h4 className="text-2xl text-center">
-              {!showRegister ? "登 录" : "注 册"}
-            </h4>
+          <div className="login-div dark:bg-gray-800">
+            <h4 className="text-2xl text-center dark:text-blue-50">登 录</h4>
             <ul className="flex justify-around mt-1 mb-3"></ul>
 
-            {!showRegister && (
-              <div className="login pt-2 h-92.5 rounded-lg bg-white dark:bg-gray-200">
-                <div>
-                  <div className="mt-6 px-14">
-                    <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100">
-                      <input
-                        ref={inputRef_email}
-                        className="flex-1 h-full"
-                        type="email"
-                        placeholder="请输入邮箱"
-                        value={email}
-                        onChange={(e) => setEmail(e.target.value)}
-                        autoComplete="new-password"
-                      />
-                    </div>
-                  </div>
-                  <div className="mt-3 px-14">
-                    <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100">
-                      <input
-                        ref={inputRef_password}
-                        className="flex-1 h-full"
-                        type="password"
-                        placeholder="请输入密码"
-                        value={password}
-                        onChange={(e) => setPassword(e.target.value)}
-                        autoComplete="new-password"
-                      />
-                      <span className="text-gray-400 cursor-pointer hover:text-red-500">
-                        忘记密码
-                      </span>
-                    </div>
-                    <p className="h-5 mt-2 px-2.5 text-red-500"></p>
-                  </div>
-                  <div className="mt-4 text-center text-gray-400">
-                    同意{" "}
-                    <a href="/" className="text-gray-600 hover:text-red-500">
-                      用户协议
-                    </a>{" "}
-                    和{" "}
-                    <a href="/" className="text-gray-600 hover:text-red-500">
-                      隐私政策
-                    </a>
-                  </div>
-                  <div className="mt-2 px-14">
-                    <button
-                      className="block w-full h-12 rounded text-base text-white bg-red-300 hover:bg-red-500"
-                      onClick={handleSubmit}
-                    >
-                      登录
-                    </button>
-                  </div>
-                  <div className="mt-4 text-center text-gray-400">
-                    <a
-                      className="text-gray-600 hover:text-red-500 cursor-pointer"
-                      onClick={showRegisterBox}
-                    >
-                      立即注册
-                    </a>
+            <div className="login pt-2 h-92.5 rounded-lg bg-white dark:bg-gray-800 dark:opacity-75">
+              <div>
+                <div className="mt-8 px-14">
+                  <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100 dark:bg-gray-900">
+                    <input
+                      ref={inputRef_email}
+                      className="flex-1 h-full"
+                      type="text"
+                      placeholder="请输入登录账户"
+                      value={email}
+                      onChange={(e) => setEmail(e.target.value)}
+                      autoComplete="new-password"
+                    />
                   </div>
                 </div>
-              </div>
-            )}
-
-            {showRegister && (
-              <div className="login pt-2 h-95.5 rounded-lg bg-white dark:bg-gray-200">
-                <div>
-                  <div className="mt-6 px-14">
-                    <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100">
-                      <input
-                        className="flex-1 h-full"
-                        type="email"
-                        placeholder="请输入邮箱"
-                        value={email}
-                        onChange={(e) => setEmail(e.target.value)}
-                        autoComplete="new-password"
-                      />
-                    </div>
-                  </div>
-                  <div className="mt-3 px-14">
-                    <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100">
-                      <input
-                        className="flex-1 h-full"
-                        type="password"
-                        placeholder="请输入密码"
-                        value={password}
-                        onChange={(e) => setPassword(e.target.value)}
-                        autoComplete="new-password"
-                      />
-                    </div>
-                  </div>
-                  <div className="mt-3 px-14">
-                    <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100">
-                      <input
-                        className="flex-1 h-full"
-                        type="password"
-                        placeholder="请输入确认密码"
-                        value={confirmPassword}
-                        onChange={(e) => setConfirmPassword(e.target.value)}
-                        autoComplete="new-password"
-                      />
-                    </div>
-                    <p className="h-5 mt-2 px-2.5 text-red-500"></p>
-                  </div>
-                  <div className="mt-4 text-center text-gray-400">
-                    同意{" "}
-                    <a href="/" className="text-gray-600 hover:text-red-500">
-                      用户协议
-                    </a>{" "}
-                    和{" "}
-                    <a href="/" className="text-gray-600 hover:text-red-500">
-                      隐私政策
-                    </a>
-                  </div>
-                  <div className="mt-2 px-14">
-                    <button className="block w-full h-12 rounded text-base text-white bg-red-300 hover:bg-red-500">
-                      立即注册
-                    </button>
-                  </div>
-                  <div className="mt-4 text-center text-gray-400">
-                    <a
-                      className="text-gray-600 hover:text-red-500 cursor-pointer"
-                      onClick={showRegisterBox}
-                    >
-                      有账户,去登录
-                    </a>
+                <div className="mt-3 px-14">
+                  <div className="flex items-center justify-between h-13 px-2.5 py-2 rounded bg-gray-100 dark:bg-gray-900">
+                    <input
+                      ref={inputRef_password}
+                      className="flex-1 h-full"
+                      type="password"
+                      placeholder="请输入登录密码"
+                      value={password}
+                      onChange={(e) => setPassword(e.target.value)}
+                      autoComplete="new-password"
+                    />
                   </div>
+                  <p className="h-5 mt-2 px-2.5 text-red-500"></p>
+                </div>
+                <div className="mt-4 text-center text-gray-400">
+                  <label className="text-gray-500 dark:text-gray-700">同意</label>
+                  <a href="/" className="text-gray-600 hover:text-[#0061ff] mx-1">
+                    用户协议
+                  </a>
+                  <label className="text-gray-500 dark:text-gray-700">和</label>
+                  <a href="/" className="text-gray-600 hover:text-[#0061ff] mx-1">
+                    隐私政策
+                  </a>
+                </div>
+                <div className="mt-2 px-14">
+                  <button
+                    className="block w-full h-12 rounded text-base text-white global-bg-color focus:outline-none focus:ring sm:mt-6 transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300"
+                    onClick={handleSubmit}
+                  >
+                    登录
+                  </button>
                 </div>
               </div>
-            )}
+            </div>
 
-            <span
-              className="close absolute right-2.5 top-2.5 w-9 h-9 cursor-pointer transform duration-500 ease-in-out hover:scale-125"
+            <RiCloseFill
+              className="close absolute right-2.5 top-2.5 w-7 h-7 text-gray-600 dark:text-gray-300 cursor-pointer transform duration-500 ease-in-out hover:scale-110"
               onClick={closeLoginBox}
-            ></span>
+            ></RiCloseFill>
           </div>
         </div>
       )}

+ 139 - 17
web/src/components/Menu/index.tsx

@@ -1,5 +1,5 @@
-'use client';
-import { useRouter } from 'next/navigation';
+"use client";
+import { useRouter } from "next/navigation";
 import Image from "next/image";
 import { useGlobalContext } from "@/providers/GlobalProvider";
 
@@ -8,22 +8,144 @@ export default function Header() {
   const { menuIndex } = useGlobalContext();
 
   return (
-    <div className="flex flex-col items-center space-4 select-none">
-      <div className={`p-1 my-2 rounded-md text-center ${menuIndex == 1 ? 'bg-red-100 text-red-500' : 'text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800'} cursor-pointer`}  onClick={() => router.push('/dialogue')}>
-        <Image src="/icons/icon-talk.svg" alt="聊天" width={50} height={50} />
-        <p className="text-sm text-center py-1">对话</p>
+    <div className="relative h-full">
+      <div className="flex flex-col items-center space-4 select-none">
+        <div
+          className={`p-1 my-2 rounded-md text-center ${
+            menuIndex == 1
+              ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+              : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+          } cursor-pointer`}
+          onClick={() => router.push("/dialogue")}
+        >
+          <Image src="/icons/icon-talk.svg" alt="聊天" width={50} height={50} />
+          <p className="text-sm text-center py-1">对话</p>
+        </div>
+        <div
+          className={`p-1 my-2 rounded-md text-center ${
+            menuIndex == 2
+              ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+              : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+          } cursor-pointer`}
+          onClick={() => router.push("/")}
+        >
+          <Image src="/icons/icon-app.svg" alt="应用" width={50} height={50} />
+          <p className="text-sm text-center py-1">应用</p>
+        </div>
+        <div
+          className={`p-1 my-2 rounded-md text-center ${
+            menuIndex == 3
+              ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+              : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+          } cursor-pointer`}
+          onClick={() => router.push("/vip")}
+        >
+          <Image src="/icons/icon-vip.svg" alt="会员" width={50} height={50} />
+          <p className="text-sm text-center py-1">会员</p>
+        </div>
+        <div
+          className={`p-1 my-2 rounded-md text-center ${
+            menuIndex == 4
+              ? "bg-blue-50 dark:bg-blue-950 text-[#0061ff]"
+              : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800"
+          } cursor-pointer`}
+          onClick={() => router.push("/help")}
+        >
+          <Image
+            src="/icons/icon-question.svg"
+            alt="帮助"
+            width={50}
+            height={50}
+          />
+          <p className="text-sm text-center py-1">帮助</p>
+        </div>
       </div>
-      <div className={`p-1 my-2 rounded-md text-center ${menuIndex == 2 ? 'bg-red-100 text-red-500' : 'text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800'} cursor-pointer`} onClick={() => router.push('/')}>
-        <Image src="/icons/icon-app.svg" alt="应用" width={50} height={50} />
-        <p className="text-sm text-center py-1">应用</p>
-      </div>
-      <div className={`p-1 my-2 rounded-md text-center ${menuIndex == 3 ? 'bg-red-100 text-red-500' : 'text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800'} cursor-pointer`} onClick={() => router.push('/vip')}>
-        <Image src="/icons/icon-vip.svg" alt="会员" width={50} height={50} />
-        <p className="text-sm text-center py-1">会员</p>
-      </div>
-      <div className={`p-1 my-2 rounded-md text-center ${menuIndex == 4 ? 'bg-red-100 text-red-500' : 'text-gray-500 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-800'} cursor-pointer`} onClick={() => router.push('/help')}>
-        <Image src="/icons/icon-question.svg" alt="帮助" width={50} height={50} />
-        <p className="text-sm text-center py-1">帮助</p>
+      <div className="flex flex-col items-center space-4 select-none absolute inset-x-0 bottom-0">
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-1.svg"
+              alt="数字人"
+              width={40}
+              height={40}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">数字人</p>
+          </a>
+        </div>
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-2.svg"
+              alt="课程入驻"
+              width={40}
+              height={40}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">课程入驻</p>
+          </a>
+        </div>
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-3.svg"
+              alt="加入分销"
+              width={50}
+              height={50}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">加入分销</p>
+          </a>
+        </div>
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-4.svg"
+              alt="小程序推广"
+              width={40}
+              height={40}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">小程序推广</p>
+          </a>
+        </div>
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-5.svg"
+              alt="电商带货"
+              width={40}
+              height={40}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">电商带货</p>
+          </a>
+        </div>
+        <div
+          className={`w-full my-2 py-3 rounded-md text-center text-gray-900 dark:text-gray-500 bg-gray-50 dark:bg-gray-800 border border-slate-200 dark:border-slate-700`}
+        >
+          <a href="https://gpts.gkscrm.com/" target="_blank">
+            <Image
+              src="/icons/a-6.svg"
+              alt="生财有道 - 副业圈子"
+              width={40}
+              height={40}
+              className="mx-auto"
+            />
+            <p className="text-sm text-center">生财有道</p>
+            <p className="text-sm text-center">副业圈子</p>
+          </a>
+        </div>
       </div>
     </div>
   );

+ 36 - 3
web/src/providers/GlobalProvider.tsx

@@ -1,5 +1,6 @@
-'use client';
-import React, { createContext, useState, useContext, ReactNode } from "react";
+"use client";
+import React, { createContext, useState, useContext, ReactNode,useEffect } from "react";
+import { GetChatLastUsedForLocalStorage } from "@/utils/chat";
 
 const GlobalContext = createContext<any>(null);
 
@@ -10,16 +11,48 @@ interface GlobalProviderProps {
 export const GlobalProvider: React.FC<GlobalProviderProps> = ({ children }) => {
   const [menuIndex, setMenuIndex] = useState<number>(0);
   const [loginShow, setLoginShow] = useState<boolean>(false);
+
+  const [listType, setListType] = useState<number>(0); //0会话历史 1助手列表
+  const [agentId, setAgentId] = useState<number>(0); //助手ID
+  const [conversationId, setConversationId] = useState<string>(""); //会话ID
+
   const changeMenuIndex = (index: number) => {
     setMenuIndex(index);
   };
   const changeLoginShow = (show: boolean) => {
     setLoginShow(show);
   };
+  const changeListType = (listType: number) => {
+    setListType(listType);
+  };
+  const changeAgentId = (agentId: number) => {
+    setAgentId(agentId);
+  };
+  const changeConversationId = (conversationId: string) => {
+    setConversationId(conversationId);
+  };
+
+  useEffect(() => {
+    var lastUsed = GetChatLastUsedForLocalStorage();
+    setListType(lastUsed.listType);
+    setAgentId(lastUsed.agentId);
+    setConversationId(lastUsed.conversationId);
+  }, []);
 
   return (
     <GlobalContext.Provider
-      value={{ menuIndex, changeMenuIndex, loginShow, changeLoginShow }}
+      value={{
+        menuIndex,
+        changeMenuIndex,
+        loginShow,
+        changeLoginShow,
+        listType,
+        changeListType,
+        agentId,
+        changeAgentId,
+        conversationId,
+        changeConversationId,
+      }}
     >
       {children}
     </GlobalContext.Provider>

+ 2 - 2
web/src/providers/ThemeProvider.tsx

@@ -21,7 +21,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
           >
             <div
               className="shadow-md mx-2 bg-[#ffffff] dark:bg-[#202123] rounded-md p-2"
-              style={{ height: "calc(100vh - 5rem)", width: "5.2rem" }}
+              style={{ height: "calc(100vh - 5rem)", width: "7rem" }}
             >
               <Menu/>
             </div>
@@ -29,7 +29,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
               className="relative shadow-md mr-2 bg-[#ffffff] dark:bg-[#202123] rounded-md overflow-auto scroll-smooth"
               style={{
                 height: "calc(100vh - 5rem)",
-                width: "calc(100% - 5.2rem)",
+                width: "calc(100% - 7rem)",
               }}
             >
               <main>{children}</main>

+ 134 - 7
web/src/utils/chat.ts

@@ -1,4 +1,4 @@
-import { ChatSessionResult, ChatMessageResult } from "@/utils/clientsApis";
+import { ChatMessageResult, EmployeeSearchResult } from "@/utils/clientsApis";
 
 /**
  * 聊天记录
@@ -24,23 +24,23 @@ export interface ChatMessage {
 export function MessageChange(list: ChatMessageResult[]) {
     const chatMessageList: ChatMessage[] = [];
     list.forEach((item) => {
-        if(item.query){
+        if (item.query) {
             chatMessageList.push({
                 id: item.id,
                 role: 'user',
-                parentId:'',
-                model:'',
+                parentId: '',
+                model: '',
                 content: item.query,
                 createAt: item.createdAt,
                 updateAt: item.createdAt
             })
         }
-        if(item.answer){
+        if (item.answer) {
             chatMessageList.push({
                 id: item.id + '_assistant',
                 role: 'assistant',
-                parentId:item.id,
-                model:'',
+                parentId: item.id,
+                model: '',
                 content: item.answer,
                 createAt: item.createdAt,
                 updateAt: item.createdAt
@@ -50,3 +50,130 @@ export function MessageChange(list: ChatMessageResult[]) {
     return chatMessageList
 }
 
+const STORAGE_KEY_CHAT_MODEL_LIST = 'chat-model-list';
+/**
+ * 本地存储 - 助手列表
+ * 
+ * @interface ChatAiModel
+ * @property {number} id - ID
+ * @property {string} name - 名称
+ * @property {string} avatar - 头像
+ * @property {string} estimate - 描述
+ * @property {string} ConversationId - 会话ID
+ * @property {number} createdAt - 创建时间(时间戳)
+ */
+export interface ChatAiModel {
+    id: number;
+    name: string;
+    avatar: string;
+    estimate: string;
+    createdAt: number;
+    ConversationId: string;
+}
+
+export function SetChatModelListForLocalStorage(list: ChatAiModel[]) {
+    localStorage.setItem(STORAGE_KEY_CHAT_MODEL_LIST, JSON.stringify(list));
+}
+
+export const GetChatModelListForLocalStorage=(): ChatAiModel[] =>  {
+    let list = JSON.parse(localStorage.getItem(STORAGE_KEY_CHAT_MODEL_LIST) || '[]')as ChatAiModel[];
+    return list
+}
+
+export const LoadLocalChatList = (model: EmployeeSearchResult): ChatAiModel[] => {
+    var list = GetChatModelListForLocalStorage();
+    if (list === null) {
+        list = [
+            {
+                id: model.id,
+                name: model.title,
+                avatar: model.avatar,
+                estimate: model.estimate,
+                createdAt: model.createdAt,
+                ConversationId: "",
+            },
+        ];
+    } else {
+        var index = list.findIndex((v) => v.id == model.id);
+        if (index < 0) {
+            list = [
+                ...[
+                    {
+                        id: model.id,
+                        name: model.title,
+                        avatar: model.avatar,
+                        estimate: model.estimate,
+                        createdAt: model.createdAt,
+                        ConversationId: "",
+                    },
+                ],
+                ...list,
+            ];
+        }
+    }
+    SetChatModelListForLocalStorage(list);
+    return list;
+}
+
+export const UpdateLocalChatListForConversationId = (id:number,conversationId:string): ChatAiModel[] => {
+    var list = GetChatModelListForLocalStorage();
+    if (list !== null) {
+        var index = list.findIndex((v) => v.id == id);
+        if (index >= 0 && (list[index].ConversationId == '' || list[index].ConversationId.includes("guid_0000_"))) {
+            list[index].ConversationId = conversationId;
+        }
+    } 
+    SetChatModelListForLocalStorage(list);
+    return list;
+}
+
+export const GetLocalChatModelDetails = (id:number): ChatAiModel => {
+    var list = GetChatModelListForLocalStorage();
+    if (list !== null) {
+        var index = list.findIndex((v) => v.id == id);
+        if (index >= 0 ) {
+            return  list[index];
+        }
+    } 
+    return {id:0,name:"",avatar:"",estimate:"",createdAt:0,ConversationId:"" };
+}
+
+const STORAGE_KEY_CHAT_LAST_USED = 'chat-last-used';
+/**
+ * 本地存储 - 上次使用助手设置
+ * 
+ * @interface ChatLastUsed
+ * @property {number} listType - 助手列表类型 0历史列表 1助手列表
+ * @property {number} agentId - 智能体ID
+ * @property {string} ConversationId - 会话ID
+ */
+export interface ChatLastUsed {
+    listType: number;
+    agentId: number;
+    conversationId: string;
+}
+export const DEFAULT_CHATLASTUSED = {
+    listType: 0,
+    agentId: 0,
+    conversationId: '',
+};
+
+export const SetChatLastUsedForLocalStorage = (value: ChatLastUsed) => {
+    localStorage.setItem(STORAGE_KEY_CHAT_LAST_USED, JSON.stringify(value));
+}
+
+export const GetChatLastUsedForLocalStorage = (): ChatLastUsed => {
+    let model = DEFAULT_CHATLASTUSED;
+    if (typeof localStorage === 'undefined') return model;
+    const modelJson = localStorage.getItem(STORAGE_KEY_CHAT_LAST_USED);
+    if (modelJson) {
+        let savedModel = JSON.parse(modelJson) as ChatLastUsed;
+        model = Object.assign(model, savedModel);
+    }
+    return model;
+}
+
+
+
+
+

+ 10 - 0
web/src/utils/clientsApis.ts

@@ -161,15 +161,21 @@ export interface UserPasswordParams {
  * 
  * @interface UserInfoResult
  * @property {string} id - ID
+ * @property {string} avatar - 头像
  * @property {string} username - 用户名
  * @property {string} nickname - 昵称
  * @property {number} status - 状态(1代表正常,非1代表不可用)
+ * @property {string} mobile - 手机号
+ * @property {string} email - 邮箱
  */
 export interface UserInfoResult {
     id: string;
+    avatar: string;
     username: string;
     nickname: string;
     status: number;
+    mobile: string;
+    email: string;
 }
 
 /**
@@ -215,11 +221,13 @@ export interface ChatSubmitResult {
  * @property {string} conversationId - 会话ID
  * @property {string} firstId - 当前页第一条聊天记录的 ID,默认为空
  * @property {number} limit - 一次请求返回多少条聊天记录
+ * @property {number} agentId - 智能体ID
  */
 export interface ChatMessageParams {
     conversationId: string;
     firstId: string;
     limit: number;
+    agentId: number;
 }
 
 /**
@@ -246,10 +254,12 @@ export interface ChatMessageResult {
  * 聊天会话列表请求参数
  * 
  * @interface ChatSessionParams
+ * @property {number} agentId - 智能体ID
  * @property {string} last_id - 当前页最后面一条记录的 ID
  * @property {string} limit - 一次请求返回多少条记录
  */
 export interface ChatSessionParams {
+    agentId: number;
     last_id: string;
     limit: number;
 }

+ 8 - 4
web/src/utils/user.ts

@@ -48,14 +48,15 @@ export const redirectToHome = (ms?: number) => {
   ms ? setTimeout(toHome, ms) : toHome();
 };
 
-export const setUserSession = (sessionId: string) => {
-  let expires = new Date();
-  expires.setMinutes(expires.getMinutes() + 10080);
+export const setUserSession = (sessionId: string,expires:number) => {
+  //let expires = new Date();
+  //expires.setMinutes(expires.getMinutes() + 10080);
   localStorage.setItem(
     'session',
     JSON.stringify({
       sessionId,
-      expires: expires.getTime(),
+      expires: expires,
+      //expires: expires.getTime(),
     }),
   );
 };
@@ -71,3 +72,6 @@ export const getUserSession = (): string => {
 export const clearUserSession = () => {
   localStorage.removeItem('session');
 };
+
+
+

Some files were not shown because too many files changed in this diff