瀏覽代碼

feat: agent set list page

王晓东 3 周之前
父節點
當前提交
4fcd551f18
共有 79 個文件被更改,包括 1037 次插入2162 次删除
  1. 9 1
      project.private.config.json
  2. 14 10
      src/app.config.ts
  3. 13 37
      src/app.less
  4. 0 0
      src/components/chat-message/Message.tsx
  5. 0 0
      src/components/chat-message/MessageRobot.tsx
  6. 0 0
      src/components/chat-message/MessageRobotPlain.tsx
  7. 0 0
      src/components/chat-message/index.module.less
  8. 0 0
      src/components/chat-message/index.tsx
  9. 14 10
      src/components/leaf-tabs/index.module.less
  10. 3 1
      src/components/nav-bar-normal/index.tsx
  11. 0 0
      src/components/think-animation/index.module.less
  12. 0 0
      src/components/think-animation/index.tsx
  13. 7 4
      src/components/voice-player-bar/index.module.less
  14. 1 1
      src/components/wemeta-input/index.module.less
  15. 5 4
      src/components/wemeta-radio/index.module.less
  16. 41 0
      src/components/wemeta-tabs/index.module.less
  17. 22 24
      src/components/wemeta-tabs/index.tsx
  18. 22 2
      src/components/wemeta-textarea/index.tsx
  19. 1 1
      src/pages/agent-gen/components/step/StepPick.tsx
  20. 0 8
      src/pages/agent-gen/components/step/index.module.less
  21. 14 1
      src/pages/agent/components/AgentSetting/components/AgentSettingList/index.tsx
  22. 1 1
      src/pages/chat/components/chat-welcome/index.tsx
  23. 1 1
      src/pages/chat/index.tsx
  24. 0 103
      src/pages/editor-pages/editor-business-card/index.tsx
  25. 1 1
      src/pages/editor-pages/editor-greeting-questions/index.config.ts
  26. 0 0
      src/pages/editor-pages/editor-greeting-questions/index.module.less
  27. 112 0
      src/pages/editor-pages/editor-greeting-questions/index.tsx
  28. 1 1
      src/pages/editor-pages/editor-greeting/index.config.ts
  29. 2 0
      src/pages/editor-pages/editor-greeting/index.module.less
  30. 83 0
      src/pages/editor-pages/editor-greeting/index.tsx
  31. 1 1
      src/pages/editor-pages/editor-personality/index.config.ts
  32. 2 0
      src/pages/editor-pages/editor-personality/index.module.less
  33. 83 0
      src/pages/editor-pages/editor-personality/index.tsx
  34. 0 16
      src/pages/editor-pages/editor-store-home/index.module.less
  35. 0 120
      src/pages/editor-pages/editor-store-home/index.tsx
  36. 0 16
      src/pages/editor-pages/editor-store-product/index.module.less
  37. 0 164
      src/pages/editor-pages/editor-store-product/index.tsx
  38. 5 5
      src/pages/editor-pages/editor.module.less
  39. 2 0
      src/pages/index/components/welcome/index.tsx
  40. 0 4
      src/pages/knowledge-lesson/index.config.ts
  41. 0 0
      src/pages/knowledge-lesson/index.module.less
  42. 0 16
      src/pages/knowledge-lesson/index.tsx
  43. 9 0
      src/pages/knowledge/components/container-header/index.module.less
  44. 13 0
      src/pages/knowledge/components/container-header/index.tsx
  45. 0 32
      src/pages/knowledge/components/doc-icon/index.module.less
  46. 0 37
      src/pages/knowledge/components/doc-icon/index.tsx
  47. 0 35
      src/pages/knowledge/components/doc-tips/index.tsx
  48. 0 75
      src/pages/knowledge/components/uploading-list/RenderFileStatus.tsx
  49. 0 76
      src/pages/knowledge/components/uploading-list/file-uploader.tsx
  50. 0 62
      src/pages/knowledge/components/uploading-list/index.module.less
  51. 0 76
      src/pages/knowledge/components/uploading-list/index.tsx
  52. 32 188
      src/pages/knowledge/index.tsx
  53. 27 1
      src/pages/profile/components/top-bar/index.tsx
  54. 0 4
      src/pages/system-voice/index.config.ts
  55. 0 59
      src/pages/vip/components/price-cards/index.module.less
  56. 0 70
      src/pages/vip/components/price-cards/index.tsx
  57. 0 56
      src/pages/vip/components/vip-rights/index.module.less
  58. 0 96
      src/pages/vip/components/vip-rights/index.tsx
  59. 0 5
      src/pages/vip/index.config.ts
  60. 0 27
      src/pages/vip/index.module.less
  61. 0 144
      src/pages/vip/index.tsx
  62. 0 5
      src/pages/voice-tabs/index.config.ts
  63. 0 23
      src/pages/voice-tabs/index.module.less
  64. 0 97
      src/pages/voice-tabs/index.tsx
  65. 0 0
      src/pages/voice/components/my-voice/components/index.module.less
  66. 0 0
      src/pages/voice/components/my-voice/components/index.tsx
  67. 9 19
      src/pages/voice/components/my-voice/components/popup-recorder/index.module.less
  68. 1 2
      src/pages/voice/components/my-voice/components/popup-recorder/index.tsx
  69. 0 0
      src/pages/voice/components/my-voice/components/popup-tryout/index.module.less
  70. 0 0
      src/pages/voice/components/my-voice/components/popup-tryout/index.tsx
  71. 20 0
      src/pages/voice/components/my-voice/index.module.less
  72. 277 0
      src/pages/voice/components/my-voice/index.tsx
  73. 0 67
      src/pages/voice/components/popup-container/index.module.less
  74. 0 31
      src/pages/voice/components/popup-container/index.tsx
  75. 0 2
      src/pages/voice/components/system-voice/index.module.less
  76. 56 42
      src/pages/voice/components/system-voice/index.tsx
  77. 3 1
      src/pages/voice/index.config.ts
  78. 20 16
      src/pages/voice/index.module.less
  79. 110 261
      src/pages/voice/index.tsx

+ 9 - 1
project.private.config.json

@@ -2,11 +2,19 @@
   "projectname": "bluenote",
   "setting": {
     "compileHotReLoad": true,
-    "urlCheck": false
+    "urlCheck": false,
+    "skylineRenderEnable": true
   },
   "condition": {
     "miniprogram": {
       "list": [
+        {
+          "name": "pages/editor-pages/editor-greeting-questions/index",
+          "pathName": "pages/editor-pages/editor-greeting-questions/index",
+          "query": "",
+          "launchMode": "default",
+          "scene": null
+        },
         {
           "name": "pages/chat/index",
           "pathName": "pages/chat/index",

+ 14 - 10
src/app.config.ts

@@ -1,11 +1,15 @@
 export default defineAppConfig({
   pages: [
+    'pages/index/index',
     'pages/profile/index',
+    'pages/agent/index',
+    'pages/editor-pages/editor-personality/index',
+    'pages/editor-pages/editor-greeting/index',
+    'pages/editor-pages/editor-greeting-questions/index',
+    'pages/voice/index',
     'pages/chat/index',
     'pages/agent-gen/index',
-    'pages/agent/index',
     'pages/editor-contact/index',
-    'pages/index/index',
     'pages/login/index',
     'pages/test/index',
     'pages/component-library/index',
@@ -22,15 +26,11 @@ export default defineAppConfig({
     'pages/analysis/index',
     'pages/knowledge/index',
     'pages/member/index',
-    'pages/voice/index',
-    'pages/system-voice/index',
-    'pages/voice-tabs/index',
+    
     'pages/choose-contact/index',
-    'pages/vip/index',
     'pages/page/index',
     'pages/editor-pages/editor-media/index',
     
-    'pages/knowledge-lesson/index',
     'pages/editor-pages/editor-chat/index',
   
     'pages/editor-pages/editor-link/index',
@@ -39,8 +39,6 @@ export default defineAppConfig({
     'pages/editor-pages/editor-link-contact/index',
     'pages/editor-pages/editor-textarea/index',
     'pages/editor-pages/editor-title/index',
-    'pages/editor-pages/editor-store-product/index',
-    'pages/editor-pages/editor-store-home/index',
     'pages/contact/index',
     'pages/chat-history/index',
     
@@ -53,11 +51,17 @@ export default defineAppConfig({
     backgroundColor: '#f5f5f2',
     list: [
       {
-        pagePath: 'pages/test/index',
+        pagePath: 'pages/index/index',
         selectedIconPath: 'images/tabbar/home-actived.png',
         iconPath: 'images/tabbar/home.png',
         text: '主页'
       },
+      {
+        pagePath: 'pages/knowledge/index',
+        selectedIconPath: 'images/tabbar/contact-actived.png',
+        iconPath: 'images/tabbar/contact.png',
+        text: '知识库'
+      },
       {
         pagePath: 'pages/contact/index',
         selectedIconPath: 'images/tabbar/contact-actived.png',

+ 13 - 37
src/app.less

@@ -262,43 +262,6 @@
 }
 
 
-.tab-buttons{
-  display: flex; 
-  align-items: center;
-  width: 100%;
-  color: #999;
-  font-size: 14px;
-  height: 56px;
-  font-weight: 400;
-}
-.tab-button{
-  position: relative;
-  flex: 1;
-  display: flex; 
-  align-items: center;
-  height: 32px;
-  justify-content: center;
-  &::after{
-    display: none;
-    content: '';
-    position: absolute;
-    bottom: 0;
-    left: 50%;
-    width: 24px;
-    transform: translateX(-50%);
-    border-radius: 2px;
-    height: 3px;
-    background-color: var(--color-primary-dark);
-  }
-}
-.tabButton-actived{
-  .tab-button();
-  font-weight: 500;
-  color: var(--color-primary-dark);
-  &::after{
-    display: block;
-  }
-}
 
 // 居正中
 .flex-center{
@@ -319,4 +282,17 @@
   background-color: var(--color-bg-primary);
   padding-bottom: constant(safe-area-inset-bottom); /*兼容 IOS<11.2*/
 	padding-bottom: env(safe-area-inset-bottom); /*兼容 IOS>11.2*/
+}
+
+
+
+//global text
+
+.gradient-text{
+  color: transparent; /* 文字透明以显示背景渐变 */
+  display: inline-block; /* 确保渐变生效 */
+  background: linear-gradient(279.56deg, #317CFA 13.29%, #FF2DF8 92.34%);
+  -webkit-background-clip: text;
+  background-clip: text;
+  -webkit-text-fill-color: transparent;
 }

+ 0 - 0
src/pages/chat/components/chat-message/Message.tsx → src/components/chat-message/Message.tsx


+ 0 - 0
src/pages/chat/components/chat-message/MessageRobot.tsx → src/components/chat-message/MessageRobot.tsx


+ 0 - 0
src/pages/chat/components/chat-message/MessageRobotPlain.tsx → src/components/chat-message/MessageRobotPlain.tsx


+ 0 - 0
src/pages/chat/components/chat-message/index.module.less → src/components/chat-message/index.module.less


+ 0 - 0
src/pages/chat/components/chat-message/index.tsx → src/components/chat-message/index.tsx


+ 14 - 10
src/components/leaf-tabs/index.module.less

@@ -1,43 +1,47 @@
 // index.module.less
 .tabsContainer {
   width: 100%;
-  padding: 12px;
   position: sticky;
   top: 64px;
   left: 0;
   right: 0;
   z-index: 2;
-  background-color: #F5F5F2;
-  // background-color: red;
+  padding-top: 12px;
+  border-radius: 12px 12px 0 0;
 }
 
 .tabsHeader {
   position: relative;
+  padding-top: 16px;
+  background-color: white;
+  border-bottom: 1px solid rgba(#000, .05);
 }
 
 .tabsList {
   display: flex;
   position: relative;
+  background-color: white;
 }
 
 .tabButton {
-  flex: 1;
+  
   padding-bottom: 8px;
   text-align: center;
   color: #666;
+  font-weight: 500;
+  font-size: 14px;
+  line-height: 22px;
   transition: color 0.2s;
-
   &.active {
-    color: #9bbd00;
+    color: #000;
   }
 }
 
 .indicator {
   position: absolute;
   bottom: 0;
-  width: 20px;
-  height: 3px;
-  border-radius: 3px;
-  background-color: #9bbd00;
+  width: 28px;
+  height: 2px;
+  background-color: #000;
   transition: all 0.3s ease-in-out;
 }

+ 3 - 1
src/components/nav-bar-normal/index.tsx

@@ -2,6 +2,7 @@ import { useAppStore } from "@/store/appStore";
 import { Text, View } from "@tarojs/components";
 import Taro, { useUnload, usePageScroll } from "@tarojs/taro";
 import React, { useState } from "react";
+import IconArrowLeft from "@/components/icon/icon-arrow-left";
 import style from "./index.module.less";
 
 interface Props {
@@ -68,7 +69,8 @@ const Index: React.FC<Props> = ({
     // 文本返回按钮
     return (
       <View className={`${style.navBarBackButtonNormal}`}>
-        <View className={`${style.iconArrowLeftPure}`}></View>
+        <IconArrowLeft />
+        {/* <View className={`${style.iconArrowLeftPure}`}></View> */}
         <Text className={style.backText}>{backText}</Text>
       </View>
     );

+ 0 - 0
src/pages/chat/components/think-animation/index.module.less → src/components/think-animation/index.module.less


+ 0 - 0
src/pages/chat/components/think-animation/index.tsx → src/components/think-animation/index.tsx


+ 7 - 4
src/components/voice-player-bar/index.module.less

@@ -1,21 +1,24 @@
-.playerBarContainer{
+.playerBarContainer {
   padding-bottom: 24px;
   margin-bottom: 24px;
   border-bottom: 1px solid rgba(0,0,0,.04);
 }
-.playerBar{
+
+.playerBar {
   display: flex;
   align-items: center;
   gap: 12px;
   padding: 20px 16px;
-  border-radius: 20px;
+  border-radius: 12px;
   background-color: white;
 }
-.playStatus{
+
+.playStatus {
   display: flex;
   align-items: center;
   justify-content: center;
 }
+
 .icon{
   width: 24px;
   height: 24px;

+ 1 - 1
src/components/wemeta-input/index.module.less

@@ -1,6 +1,6 @@
 .inputContainer{
   padding: 14px 16px;
-  border-radius: 20px;
+  border-radius: 12px;
   background-color: white;
   border: 2px solid transparent;
 }

+ 5 - 4
src/components/wemeta-radio/index.module.less

@@ -2,8 +2,8 @@
   display: flex;
   align-items: center;
   justify-content: center;
-  width: 16px;
-  height: 16px;
+  width: 20px;
+  height: 20px;
   border: 1px solid rgba(#000, .45);
   border-radius: 100%;
   transition: all .3s;
@@ -19,12 +19,13 @@
   background: #EDEDED;
 }
 .radio.checked{
-  border: 1px solid var(--color-primary);
-  background-color: var(--color-primary);
+  border: 1px solid #000;
+  background-color: #000;
   .inner{
     opacity: 1;
   }
 }
+
 .innerCheckbox{
   display: flex;
   align-items: center;

+ 41 - 0
src/components/wemeta-tabs/index.module.less

@@ -4,6 +4,9 @@
 .visible{
   display: block;
 }
+
+
+// 矩形填充形
 .buttonTabs{
   border-radius: 20px;
   // background-color: #F5F6F7;
@@ -47,4 +50,42 @@
   .rounedButton();
   color: white;
   background-color: var(--color-primary);
+}
+
+
+// 外框型 outline
+.outlineTabButtons{
+  display: flex; 
+  padding: 16px 20px 0;
+  width: 100%;
+  color: rgba(#000, .65);
+  font-size: 14px;
+  font-weight: 500;
+  line-height: 22px;
+  border-bottom: 1px solid rgba(#000, .05);
+}
+
+.outlineTabButton{
+  position: relative;
+  flex: 1;
+  display: flex; 
+  align-items: center;
+  padding-bottom: 14px;
+  &::after{
+    display: none;
+    content: '';
+    position: absolute;
+    bottom: -1px;
+    width: 28px;
+    height: 2px;
+    background-color: #000;
+  }
+}
+.outlineTabButtonActived{
+  .outlineTabButton();
+  font-weight: 500;
+  color: #000;
+  &::after{
+    display: block;
+  }
 }

+ 22 - 24
src/components/wemeta-tabs/index.tsx

@@ -40,30 +40,28 @@ export default function Index({
     );
   };
 
-  // if (tabStyle === "outline") {
-  //   return (
-  //     <View>
-  //       <View className="tab-buttons">
-  //         {list.map((item) => {
-  //           return (
-  //             <View
-  //               key={item.key}
-  //               className={`tab-button  ${
-  //                 tabIndex === item.key ? "tabButton-actived" : ""
-  //               }
-  //                 ${full ? "flex flex-1 items-center justify-center" : ""}
-  //               `}
-  //               onClick={() => setTabIndex(item.key)}
-  //             >
-  //               {item.label}
-  //             </View>
-  //           );
-  //         })}
-  //       </View>
-  //       {renderTabContent()}
-  //     </View>
-  //   );
-  // }
+  if (tabStyle === "outline") {
+    return (
+      <View>
+        <View className={style.outlineTabButtons}>
+          {list.map((item) => {
+            return (
+              <View
+                key={item.key}
+                className={`${
+                  tabIndex === item.key ? style.outlineTabButtonActived : style.outlineTabButton
+                }`}
+                onClick={() => setTabIndex(item.key)}
+              >
+                {item.label}
+              </View>
+            );
+          })}
+        </View>
+        {renderTabContent()}
+      </View>
+    );
+  }
 
   // if (tabStyle === "button") {
   //   return (

+ 22 - 2
src/components/wemeta-textarea/index.tsx

@@ -2,6 +2,7 @@ import { View, Textarea, InputProps } from "@tarojs/components";
 import style from "./index.module.less";
 import { useState, useRef, useEffect } from "react";
 import { countCharacters, getStrByMaxLength } from "@/utils/index";
+import IconStarColor from "@/components/icon/icon-star-color";
 interface Props {
   placeholder?: string;
   value: string;
@@ -19,6 +20,7 @@ interface Props {
   autoFocus?: boolean;
   extraClass?: string;
   onConfirm?: (value: string) => void;
+  ai?: boolean;
 }
 let isInbox = false;
 const index = ({
@@ -38,6 +40,7 @@ const index = ({
   maxlength,
   extra,
   onConfirm,
+  ai,
 }: Props) => {
   const [focus, setFocus] = useState(false);
   const inputRef = useRef<HTMLInputElement>(null); // 创建一个 ref
@@ -77,7 +80,20 @@ const index = ({
     // setText(value);
     onInput && onInput(value);
   };
-  
+
+  const handleClick = ()=> {
+    console.log('请求服务端润色')
+  }
+
+  const renderAIButton = () => {
+    return (
+      <View className="flex items-center gap-4" onClick={handleClick}>
+        <IconStarColor></IconStarColor>{" "}
+        <View className="gradient-text">润色</View>
+      </View>
+    );
+  };
+
   return (
     <View
       className={`${
@@ -103,9 +119,13 @@ const index = ({
           autoFocus={autoFocus}
           cursorSpacing={cursorSpacing}
           maxlength={10000}
-          onConfirm={(e:any)=> {onConfirm && onConfirm(e.detail.value)}}
+          onConfirm={(e: any) => {
+            onConfirm && onConfirm(e.detail.value);
+          }}
         />
         <View className={`${style.textareaButtons} justify-end`}>
+          {ai && renderAIButton()}
+
           {extra && extra()}
           {/* <View className={`button-rounded-mini ${!value.length ? 'disabled' :''}`} onClick={handleClear}>清除</View> */}
           {maxlength && (

+ 1 - 1
src/pages/agent-gen/components/step/StepPick.tsx

@@ -42,7 +42,7 @@ export default React.memo(function Index({prev, next}:IProps) {
       <View className={style.pickContainer}>
         {/* <View className={`${style.pickAvatarCard} ${style.pickGenCard}`}>
           <View className="flex items-center gap-4">
-            <IconStarColor></IconStarColor> <View className={style.pickGenTips}>AI生成中</View>
+            <IconStarColor></IconStarColor> <View className="gradient-text">AI生成中</View>
           </View>
         </View> */}
 

+ 0 - 8
src/pages/agent-gen/components/step/index.module.less

@@ -50,14 +50,6 @@
   align-items: center;
   background: linear-gradient(162.45deg, rgba(206, 49, 250, 0.1) 1.4%, rgba(49, 124, 250, 0.2) 100.04%);
 }
-.pickGenTips{
-    color: transparent; /* 文字透明以显示背景渐变 */
-    display: inline-block; /* 确保渐变生效 */
-    background: linear-gradient(279.56deg, #317CFA 13.29%, #FF2DF8 92.34%);
-    -webkit-background-clip: text;
-    background-clip: text;
-    -webkit-text-fill-color: transparent;
-}
 
 .mySwiper{
   width: 300px;

+ 14 - 1
src/pages/agent/components/AgentSetting/components/AgentSettingList/index.tsx

@@ -9,9 +9,10 @@ import IconConversationColor from "@/components/icon/icon-conversation-color";
 import IconIdeaColor from "@/components/icon/icon-idea-color";
 
 import IconArrow from "@/components/icon/icon-arrow";
+import Taro from "@tarojs/taro";
 
 export default function Index() {
-
+  
   const IconArrowRight = () => {
     return (
       <View className="flex items-center">
@@ -27,6 +28,9 @@ export default function Index() {
             underline
             rightRenderer={IconArrowRight}
             leftRenderer={IconVoiceColor}
+            onClick={()=> {
+              Taro.navigateTo({url: '/pages/voice/index'})
+            }}
           >
             <View className="text-14 py-18 font-medium leading-22">声音</View>
           </CardListItem>
@@ -35,6 +39,9 @@ export default function Index() {
             underline
             rightRenderer={IconArrowRight}
             leftRenderer={IconPeopleColor}
+            onClick={()=> {
+              Taro.navigateTo({url: '/pages/editor-pages/editor-personality/index'})
+            }}
           >
             <View className="text-14 py-18 font-medium leading-22">人设</View>
           </CardListItem>
@@ -43,6 +50,9 @@ export default function Index() {
             underline
             rightRenderer={IconArrowRight}
             leftRenderer={IconConversationColor}
+            onClick={()=> {
+              Taro.navigateTo({url: '/pages/editor-pages/editor-greeting/index'})
+            }}
           >
             <View className="text-14 py-18 font-medium leading-22">开场白</View>
           </CardListItem>
@@ -50,6 +60,9 @@ export default function Index() {
             className="pl-16 pr-8"
             rightRenderer={IconArrowRight}
             leftRenderer={IconIdeaColor}
+            onClick={()=> {
+              Taro.navigateTo({url: '/pages/editor-pages/editor-greeting-questions/index'})
+            }}
           >
             <View className="text-14 py-18 font-medium leading-22">
               开场提问引导

+ 1 - 1
src/pages/chat/components/chat-welcome/index.tsx

@@ -2,7 +2,7 @@
 import { View } from '@tarojs/components'
 import style from './index.module.less'
 import PersonalCard from '../personal-card'
-import MessageRobotPlain from '../chat-message/MessageRobotPlain'
+import MessageRobotPlain from '@/components/chat-message/MessageRobotPlain'
 export default ()=> {
   return (
     <View className='pt-118'>

+ 1 - 1
src/pages/chat/index.tsx

@@ -3,7 +3,7 @@ import NavBarNormal from "@/components/nav-bar-normal/index";
 import PageCustom from "@/components/page-custom/index";
 import Taro, { useUnload } from "@tarojs/taro";
 import { useCurrentCharacter } from "@/store/characterStore";
-import ChatMessage from "./components/chat-message";
+import ChatMessage from "@/components/chat-message";
 import style from "./index.module.less";
 // import { useAppStore } from "@/store/appStore";
 import InputBar from "./components/input-bar";

+ 0 - 103
src/pages/editor-pages/editor-business-card/index.tsx

@@ -1,103 +0,0 @@
-import { useState } from "react";
-import { View } from "@tarojs/components";
-
-import PageCustom from "@/components/page-custom/index";
-import NavBarNormal from "@/components/nav-bar-normal/index";
-import WemetaInput from "@/components/wemeta-input/index";
-import editorStyle from "../editor.module.less";
-import Taro, { useUnload } from "@tarojs/taro";
-import { useCharacterStore } from "@/store/characterStore";
-import { useComponentStore } from "@/store/componentStore";
-
-export default function Index() {
-  const $instance = Taro.getCurrentInstance();
-  const id = $instance.router?.params.id;
-  const deltaNum = id ? 1 : 2;
-  let currentComponent = useComponentStore((state) => state.currentComponent);
-  const character = useCharacterStore((state) => state.character);
-  const { saveComponent } = useComponentStore();
-  const loading = useComponentStore((state) => state.loading);
-  const { job, department, company } = currentComponent?.data ?? {};
-  const [value, setValue] = useState({
-    job: job ?? "",
-    department: department ?? "",
-    company: company ?? "",
-  });
-
-  const onChange = (key: string, v: string) => {
-    value[key] = v;
-    setValue({
-      ...value,
-    });
-  };
-
-  const handleSave = async () => {
-    if (
-      !value.job?.length &&
-      !value.department?.length &&
-      !value.company?.length
-    ) {
-      return;
-    }
-    if (loading) {
-      return;
-    }
-    const c = {
-      data: {
-        job: value.job,
-        department: value.department,
-        company: value.company,
-      },
-      enabled: currentComponent?.enabled ?? true,
-      id: currentComponent?.id,
-      name: currentComponent?.name ?? "商务名片",
-      characterProfileId:
-        currentComponent?.characterProfileId ?? character?.profileId,
-      type: "businessCard",
-    };
-
-    await saveComponent(c);
-  };
-
-  const handleNavBack = async () => {
-    await handleSave();
-  };
-
-  return (
-    <PageCustom bgColor="global-light-green-bg">
-      <NavBarNormal backText="保存" navDelta={deltaNum} onNavBack={handleNavBack}>
-        商务名片
-      </NavBarNormal>
-      <View className="flex flex-col items-center w-full">
-        <View className={editorStyle.container}>
-          <View className={editorStyle.formContainer}>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>职位</View>
-              <WemetaInput
-                  value={value.job}
-                  onInput={(value: string) => onChange("job", value)}
-                  placeholder="请输入"
-                />
-            </View>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>部门</View>
-              <WemetaInput
-                  value={value.department}
-                  onInput={(value: string) => onChange("department", value)}
-                  placeholder="请输入"
-                />
-            </View>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>公司</View>
-              <WemetaInput
-                  value={value.company}
-                  onInput={(value: string) => onChange("company", value)}
-                  placeholder="请输入"
-                />
-            </View>
-          </View>
-        </View>
-      </View>
-    </PageCustom>
-  );
-}

+ 1 - 1
src/pages/editor-pages/editor-store-home/index.config.ts → src/pages/editor-pages/editor-greeting-questions/index.config.ts

@@ -1,5 +1,5 @@
 export default definePageConfig({
-  navigationBarTitleText: '填写视频信息',
+  navigationBarTitleText: '开场提问引导',
   "usingComponents": {},
   navigationStyle: 'custom'
 })

+ 0 - 0
src/pages/editor-pages/editor-business-card/index.module.less → src/pages/editor-pages/editor-greeting-questions/index.module.less


+ 112 - 0
src/pages/editor-pages/editor-greeting-questions/index.tsx

@@ -0,0 +1,112 @@
+import { useState } from "react";
+import { View } from "@tarojs/components";
+
+import PageCustom from "@/components/page-custom/index";
+import NavBarNormal from "@/components/nav-bar-normal/index";
+import WemetaInput from "@/components/wemeta-input/index";
+import editorStyle from "../editor.module.less";
+import Taro, { useUnload } from "@tarojs/taro";
+import { useCharacterStore } from "@/store/characterStore";
+import { useComponentStore } from "@/store/componentStore";
+
+export default function Index() {
+  const $instance = Taro.getCurrentInstance();
+  const id = $instance.router?.params.id;
+  const deltaNum = id ? 1 : 2;
+  let currentComponent = useComponentStore((state) => state.currentComponent);
+  const character = useCharacterStore((state) => state.character);
+  const { saveComponent } = useComponentStore();
+  const loading = useComponentStore((state) => state.loading);
+  const { job, department, company } = currentComponent?.data ?? {};
+  const [value, setValue] = useState({
+    job: job ?? "",
+    department: department ?? "",
+    company: company ?? "",
+  });
+
+  const onChange = (key: string, v: string) => {
+    value[key] = v;
+    setValue({
+      ...value,
+    });
+  };
+
+  const handleSave = async () => {
+    if (
+      !value.job?.length &&
+      !value.department?.length &&
+      !value.company?.length
+    ) {
+      return;
+    }
+    if (loading) {
+      return;
+    }
+    // const c = {
+    //   data: {
+    //     job: value.job,
+    //     department: value.department,
+    //     company: value.company,
+    //   },
+    //   enabled: currentComponent?.enabled ?? true,
+    //   id: currentComponent?.id,
+    //   name: currentComponent?.name ?? "商务名片",
+    //   characterProfileId:
+    //     currentComponent?.characterProfileId ?? character?.profileId,
+    //   type: "businessCard",
+    // };
+
+    // await saveComponent(c);
+  };
+
+  const handleNavBack = async () => {
+    await handleSave();
+  };
+
+  return (
+    <PageCustom>
+      <NavBarNormal
+        backText="开场提问引导"
+        navDelta={deltaNum}
+        onNavBack={handleNavBack}
+      ></NavBarNormal>
+      <View className="flex flex-col items-center w-full gap-16 p-16">
+        <View className="flex flex-col gap-6 w-full">
+          <View className="text-14 font-medium text-black leading-28">
+            问题一
+          </View>
+          <WemetaInput
+            value={value.job}
+            onInput={(value: string) => onChange("job", value)}
+            placeholder="请输入"
+          />
+        </View>
+        <View className="flex flex-col gap-6  w-full">
+          <View className="text-14 font-medium text-black leading-28">
+            问题一
+          </View>
+          <WemetaInput
+            value={value.job}
+            onInput={(value: string) => onChange("job", value)}
+            placeholder="请输入"
+          />
+        </View>
+        <View className="flex flex-col gap-6  w-full">
+          <View className="text-14 font-medium text-black leading-28">
+            问题一
+          </View>
+          <WemetaInput
+            value={value.job}
+            onInput={(value: string) => onChange("job", value)}
+            placeholder="请输入"
+          />
+        </View>
+      </View>
+      <View className="bottom-bar">
+        <View className="pt-12 px-20">
+          <View className={`button-rounded button-primary`}>保存</View>
+        </View>
+      </View>
+    </PageCustom>
+  );
+}

+ 1 - 1
src/pages/editor-pages/editor-business-card/index.config.ts → src/pages/editor-pages/editor-greeting/index.config.ts

@@ -1,5 +1,5 @@
 export default definePageConfig({
-  navigationBarTitleText: '章节标题',
+  navigationBarTitleText: '开场白',
   "usingComponents": {},
   navigationStyle: 'custom'
 })

+ 2 - 0
src/pages/editor-pages/editor-greeting/index.module.less

@@ -0,0 +1,2 @@
+
+

+ 83 - 0
src/pages/editor-pages/editor-greeting/index.tsx

@@ -0,0 +1,83 @@
+import { useState } from "react";
+import { View } from "@tarojs/components";
+
+import PageCustom from "@/components/page-custom/index";
+import NavBarNormal from "@/components/nav-bar-normal/index";
+import editorStyle from "../editor.module.less";
+import { useComponentStore } from '@/store/componentStore'
+import { useCharacterStore } from '@/store/characterStore'
+import WemetaTextarea from "@/components/wemeta-textarea/index";
+import Taro, {useRouter, useUnload} from "@tarojs/taro";
+export default function Index() {
+  const router = useRouter();
+  const { id } = router.params;
+  let currentComponent = useComponentStore((state)=>  state.currentComponent);
+  const character = useCharacterStore((state)=> state.character);
+  const loading = useComponentStore((state)=> state.loading);
+  const { saveComponent } = useComponentStore();
+  const [value, setValue] = useState(currentComponent?.data?.text ?? '');
+  
+  const handleSave = async () => {
+    if(loading){
+      return
+    }
+    if(value.length <= 0){
+      return;
+    }
+    // const c = {
+    //   data: {
+    //     text: value,
+    //     layout: radioValue,
+    //   },
+    //   enabled: currentComponent?.enabled ?? true,
+    //   id: currentComponent?.id,
+    //   name: currentComponent?.name ?? "姓名",
+    //   index: currentComponent?.index,
+    //   characterProfileId: currentComponent?.characterProfileId ?? character?.profileId,
+    //   type: "title",
+    // };
+
+    
+    // await saveComponent(c)
+  };
+
+  const onChange = (e: any) => {
+    setValue(e);
+  };
+  
+
+  
+
+  const handleNavBack = async () => {
+    await handleSave()
+  }
+  
+  
+
+  return (
+    <PageCustom>
+      <NavBarNormal backText="开场白"></NavBarNormal>
+      <View className="flex flex-col items-center w-full">
+        <View className={editorStyle.container}>
+          <View
+            className={`${editorStyle.textInputContainer} ${editorStyle.underline}`}
+          >
+            <WemetaTextarea
+              value={value}
+              onBlur={(value: string) => onChange(value)}
+              onInput={(value: string) => onChange(value)}
+              placeholder="碰到TA之后,第一句话想说什么?"
+              autoFocus
+              ai
+            />
+          </View>
+        </View>
+        <View className="bottom-bar">
+          <View className="pt-12 px-20">
+            <View className={`button-rounded button-primary`}>保存</View>
+          </View>
+        </View>
+      </View>
+    </PageCustom>
+  );
+}

+ 1 - 1
src/pages/editor-pages/editor-store-product/index.config.ts → src/pages/editor-pages/editor-personality/index.config.ts

@@ -1,5 +1,5 @@
 export default definePageConfig({
-  navigationBarTitleText: '填写视频信息',
+  navigationBarTitleText: '人设',
   "usingComponents": {},
   navigationStyle: 'custom'
 })

+ 2 - 0
src/pages/editor-pages/editor-personality/index.module.less

@@ -0,0 +1,2 @@
+
+

+ 83 - 0
src/pages/editor-pages/editor-personality/index.tsx

@@ -0,0 +1,83 @@
+import { useState } from "react";
+import { View } from "@tarojs/components";
+
+import PageCustom from "@/components/page-custom/index";
+import NavBarNormal from "@/components/nav-bar-normal/index";
+import editorStyle from "../editor.module.less";
+import { useComponentStore } from '@/store/componentStore'
+import { useCharacterStore } from '@/store/characterStore'
+import WemetaTextarea from "@/components/wemeta-textarea/index";
+import Taro, {useRouter, useUnload} from "@tarojs/taro";
+export default function Index() {
+  const router = useRouter();
+  const { id } = router.params;
+  let currentComponent = useComponentStore((state)=>  state.currentComponent);
+  const character = useCharacterStore((state)=> state.character);
+  const loading = useComponentStore((state)=> state.loading);
+  const { saveComponent } = useComponentStore();
+  const [value, setValue] = useState(currentComponent?.data?.text ?? '');
+  
+  const handleSave = async () => {
+    if(loading){
+      return
+    }
+    if(value.length <= 0){
+      return;
+    }
+    // const c = {
+    //   data: {
+    //     text: value,
+    //     layout: radioValue,
+    //   },
+    //   enabled: currentComponent?.enabled ?? true,
+    //   id: currentComponent?.id,
+    //   name: currentComponent?.name ?? "姓名",
+    //   index: currentComponent?.index,
+    //   characterProfileId: currentComponent?.characterProfileId ?? character?.profileId,
+    //   type: "title",
+    // };
+
+    
+    // await saveComponent(c)
+  };
+
+  const onChange = (e: any) => {
+    setValue(e);
+  };
+  
+
+  
+
+  const handleNavBack = async () => {
+    await handleSave()
+  }
+  
+  
+
+  return (
+    <PageCustom>
+      <NavBarNormal backText="人设"></NavBarNormal>
+      <View className="flex flex-col items-center w-full">
+        <View className={editorStyle.container}>
+          <View
+            className={`${editorStyle.textInputContainer} ${editorStyle.underline}`}
+          >
+            <WemetaTextarea
+              value={value}
+              onBlur={(value: string) => onChange(value)}
+              onInput={(value: string) => onChange(value)}
+              placeholder="示例:你是一位汽车销售人员,拥有专业的汽车相关知识,善于耐心的解答客户提出的每一个问题,并会主动邀请客户上门试驾。"
+              autoFocus
+              ai
+            />
+          </View>
+        </View>
+        <View className="bottom-bar">
+          <View className="pt-12 px-20">
+            <View className={`button-rounded button-primary`}>保存</View>
+          </View>
+        </View>
+      </View>
+    </PageCustom>
+  );
+}

+ 0 - 16
src/pages/editor-pages/editor-store-home/index.module.less

@@ -1,16 +0,0 @@
-.coverContainer{
-  display: flex;
-  flex-direction: column;
-  gap: 12;
-  padding: 10px;
-  border-radius: 10px;
-  background-color: #fff;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 180px;
-}
-.iconAdd{
-  width: 20px;
-  height: 20px;
-}

+ 0 - 120
src/pages/editor-pages/editor-store-home/index.tsx

@@ -1,120 +0,0 @@
-import { useState } from "react";
-import { View, Text, Image } from "@tarojs/components";
-import Taro, { useRouter } from "@tarojs/taro";
-import PageCustom from "@/components/page-custom/index";
-import NavBarNormal from "@/components/nav-bar-normal/index";
-import Popup from "@/components/popup/popup";
-import editorStyle from "../editor.module.less";
-import { useCharacterStore } from "@/store/characterStore";
-import { useComponentStore } from "@/store/componentStore";
-import WemetaInput from "@/components/wemeta-input/index";
-
-export default function Index() {
-  const router = useRouter();
-  const { id } = router.params;
-  const deltaNum = id ? 1 : 2;
-  let currentComponent = useComponentStore((state) => state.currentComponent);
-  const character = useCharacterStore((state) => state.character);
-  const { saveComponent } = useComponentStore();
-  const loading = useComponentStore((state) => state.loading);
-  const { value } = currentComponent?.data ?? {};
-  const [storeProductValue, setStoreProductValue] = useState<{
-    appid: string;
-  }>(value ?? {});
-
-  const [show, setShow] = useState(false);
-
-  const handleSave = async () => {
-    if (loading) {
-      return;
-    }
-    if (!storeProductValue.appid) {
-      return;
-    }
-    const c = {
-      data: {
-        value: storeProductValue,
-        layout: 'mini',
-      },
-      enabled: currentComponent?.enabled ?? true,
-      id: currentComponent?.id,
-      name: currentComponent?.name ?? "微信小店",
-      characterProfileId:
-        currentComponent?.characterProfileId ?? character?.profileId,
-      type: "storeHome",
-    };
-    console.log(c);
-    await saveComponent(c);
-  };
-
-  const handleNavBack = async () => {
-    await handleSave();
-  };
-
-  const setValueByKey = (key: string, v: string) => {
-    setStoreProductValue((prev)=> {
-      return {
-        ...prev,
-        [key]: v
-      }
-    });
-  };
-
-  const showTips = () => () => {
-    setShow(true);
-  };
-
-  return (
-    <PageCustom>
-      <NavBarNormal
-        backText="保存"
-        navDelta={deltaNum}
-        onNavBack={handleNavBack}
-      >
-        填写小店商品信息
-      </NavBarNormal>
-      <View className="flex flex-col items-center w-full pt-20">
-        <View className={editorStyle.container}>
-          <View className={editorStyle.formContainer}>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>
-                <View className="flex-1">小店ID</View>
-                <View className="text-green" onClick={showTips()}>
-                  如何获取?
-                </View>
-              </View>
-              <WemetaInput
-                value={storeProductValue.appid}
-                onInput={(value: string) => setValueByKey("appid", value)}
-                placeholder="长按粘贴小店ID..."
-              />
-            </View>      
-          </View>
-        </View>
-      </View>
-
-      <Popup
-        title="获取小店ID"
-        show={show}
-        setShow={setShow}
-      >
-        <View className="flex flex-col gap-16 mb-44">
-          <View
-            className={`flex flex-col p-12 gap-10 text-14 rounded-20 leading-22 text-gray-65 bg-gray-f8 overflow-hidden`}
-          >
-            <View>
-              🌟 <Text className="font-medium">小绿提示:</Text>
-              <Text>通过微信小店后台 - 商品管理 - 商品列表 - 规格/编码获取。</Text>
-            </View>
-            <Image
-              className="w-full"
-              mode="widthFix"
-              src="https://cdn.wehome.cn/cmn/png/214/META-H8UKXHWU-PIWQ5G6H7F9LO74FT7QR2-9SOBIJ4M-KC.png"
-            ></Image>
-          </View>
-          <View className='button-rounded-big mt-20' onClick={() => setShow(false)}>知道了</View>
-        </View>
-      </Popup>
-    </PageCustom>
-  );
-}

+ 0 - 16
src/pages/editor-pages/editor-store-product/index.module.less

@@ -1,16 +0,0 @@
-.coverContainer{
-  display: flex;
-  flex-direction: column;
-  gap: 12;
-  padding: 10px;
-  border-radius: 10px;
-  background-color: #fff;
-  align-items: center;
-  justify-content: center;
-  width: 100%;
-  height: 180px;
-}
-.iconAdd{
-  width: 20px;
-  height: 20px;
-}

+ 0 - 164
src/pages/editor-pages/editor-store-product/index.tsx

@@ -1,164 +0,0 @@
-import { useState } from "react";
-import { View, Text, Image } from "@tarojs/components";
-import Taro, { useRouter } from "@tarojs/taro";
-import PageCustom from "@/components/page-custom/index";
-import NavBarNormal from "@/components/nav-bar-normal/index";
-import Popup from "@/components/popup/popup";
-import editorStyle from "../editor.module.less";
-import { useCharacterStore } from "@/store/characterStore";
-import { useComponentStore } from "@/store/componentStore";
-import WemetaInput from "@/components/wemeta-input/index";
-
-export default function Index() {
-  const router = useRouter();
-  const { id } = router.params;
-  const deltaNum = id ? 1 : 2;
-  let currentComponent = useComponentStore((state) => state.currentComponent);
-  const character = useCharacterStore((state) => state.character);
-  const { saveComponent } = useComponentStore();
-  const loading = useComponentStore((state) => state.loading);
-  const { value } = currentComponent?.data ?? {};
-  const [storeProductValue, setStoreProductValue] = useState<{
-    appid: string;
-    productId: string;
-  }>(value ?? {});
-
-  const [show, setShow] = useState(false);
-  const [tipsType, setTipsType] = useState(1);
-
-  const handleSave = async () => {
-    if (loading) {
-      return;
-    }
-    if (!storeProductValue.appid || !storeProductValue.productId) {
-      return;
-    }
-    const c = {
-      data: {
-        value: storeProductValue,
-        layout: 'mini',
-      },
-      enabled: currentComponent?.enabled ?? true,
-      id: currentComponent?.id,
-      name: currentComponent?.name ?? "微信小店商品",
-      characterProfileId:
-        currentComponent?.characterProfileId ?? character?.profileId,
-      type: "storeProduct",
-    };
-    console.log(c);
-    await saveComponent(c);
-  };
-
-  const handleNavBack = async () => {
-    await handleSave();
-  };
-
-  const setValueByKey = (key: string, v: string) => {
-    setStoreProductValue((prev)=> {
-      return {
-        ...prev,
-        [key]: v
-      }
-    });
-  };
-
-  const showTips = (type: number) => () => {
-    setTipsType(type);
-    setShow(true);
-  };
-
-  
-
-  
-  const renderTips = (type: number) => {
-    if (type === 1) {
-      return (
-        <>
-          <View>
-            🌟 <Text className="font-medium">小绿提示:</Text>
-            <Text>通过微信小店后台 - 商品管理 - 商品列表 - 规格/编码获取。</Text>
-          </View>
-          <Image
-            className="w-full"
-            mode="widthFix"
-            src="https://cdn.wehome.cn/cmn/png/214/META-H8UKXHWU-PIWQ5G6H7F9LO74FT7QR2-9SOBIJ4M-KC.png"
-          ></Image>
-        </>
-      );
-    }
-    return (
-      <>
-        <View>
-          🌟 <Text className="font-medium">小绿提示:</Text>
-          <Text>
-          通过微信小店后台 - 商品管理 - 商品规格/编码
-          </Text>
-        </View>
-        <Image
-          className="w-full"
-          mode="widthFix"
-          src="https://cdn.wehome.cn/cmn/png/39/META-H8UKXHWU-PIWQ5G6H7F9LO74FT7QR2-DVYBIJ4M-LC.png"
-        ></Image>
-      </>
-    );
-  };
-
-  return (
-    <PageCustom>
-      <NavBarNormal
-        backText="保存"
-        navDelta={deltaNum}
-        onNavBack={handleNavBack}
-      >
-        填写小店商品信息
-      </NavBarNormal>
-      <View className="flex flex-col items-center w-full pt-20">
-        <View className={editorStyle.container}>
-          <View className={editorStyle.formContainer}>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>
-                <View className="flex-1">小店ID</View>
-                <View className="text-green" onClick={showTips(1)}>
-                  如何获取?
-                </View>
-              </View>
-              <WemetaInput
-                value={storeProductValue.appid}
-                onInput={(value: string) => setValueByKey("appid", value)}
-                placeholder="长按粘贴小店ID..."
-              />
-            </View>
-            <View className={editorStyle.formItem}>
-              <View className={editorStyle.formItemLabel}>
-                <View className="flex-1">商品ID</View>
-                <View className="text-green" onClick={showTips(2)}>
-                  如何获取?
-                </View>
-              </View>
-              <WemetaInput
-                value={storeProductValue.productId}
-                onInput={(value: string) => setValueByKey("productId", value)}
-                placeholder="长按粘贴商品ID..."
-              />
-            </View>            
-          </View>
-        </View>
-      </View>
-
-      <Popup
-        title={tipsType === 1 ? "获取小店ID" : "获取商品ID"}
-        show={show}
-        setShow={setShow}
-      >
-        <View className="flex flex-col gap-16 mb-44">
-          <View
-            className={`flex flex-col p-12 gap-10 text-14 rounded-20 leading-22 text-gray-65 bg-gray-f8 overflow-hidden`}
-          >
-            {renderTips(tipsType)}
-          </View>
-          <View className='button-rounded-big mt-20' onClick={() => setShow(false)}>知道了</View>
-        </View>
-      </Popup>
-    </PageCustom>
-  );
-}

+ 5 - 5
src/pages/editor-pages/editor.module.less

@@ -92,16 +92,16 @@
 
 .formLabel{
   color: #000;
-  font-size: 12px;
+  font-size: 14px;
   font-style: normal;
   font-weight: 500;
-  line-height: 22px; /* 183.333% */
+  line-height: 28px; /* 183.333% */
 }
 .formLabelRequired{
   color: rgba(0, 0, 0, 0.45);
   font-family: "PingFang SC";
-  font-size: 12px;
+  font-size: 14px;
   font-style: normal;
-  font-weight: 400;
-  line-height: 22px;
+  font-weight: 500;
+  line-height: 28px;
 }

+ 2 - 0
src/pages/index/components/welcome/index.tsx

@@ -1,10 +1,12 @@
 import { View } from "@tarojs/components";
 import style from "./index.module.less";
+import Taro from "@tarojs/taro";
 
 export default function Index() {
 
   const go = ()=> {
     console.log('gogogo')
+    Taro.navigateTo({url: '/pages/agent-gen/index'})
   }
 
   return (

+ 0 - 4
src/pages/knowledge-lesson/index.config.ts

@@ -1,4 +0,0 @@
-export default definePageConfig({
-  navigationBarTitleText: '使用教程',
-  "usingComponents": {},
-})

+ 0 - 0
src/pages/knowledge-lesson/index.module.less


+ 0 - 16
src/pages/knowledge-lesson/index.tsx

@@ -1,16 +0,0 @@
-/**
- * 知识库使用教程
- */
-
-import { Text, View } from "@tarojs/components";
-
-import DocTips from '@/pages/knowledge/components/doc-tips/index'
-import style from "./index.module.less";
-import Taro, { useDidShow, useReady } from "@tarojs/taro";
-
-export default function Index() {
-  
-  return (
-    <DocTips></DocTips>
-  );
-}

+ 9 - 0
src/pages/knowledge/components/container-header/index.module.less

@@ -0,0 +1,9 @@
+.container{
+  width: 100%;
+  border-top-left-radius: 24px;
+  border-top-right-radius: 24px;
+  padding: 20px 16px;
+  border-top-width: 1px;
+  border-top: 1px solid #FFFFFF;
+  backdrop-filter: blur(38px);
+}

+ 13 - 0
src/pages/knowledge/components/container-header/index.tsx

@@ -0,0 +1,13 @@
+import { View } from '@tarojs/components';
+import style from './index.module.less'
+
+const Index = () => {
+  
+  return (
+    <View className={style.container}>
+
+    </View>
+  )
+}
+
+export default Index;

+ 0 - 32
src/pages/knowledge/components/doc-icon/index.module.less

@@ -1,32 +0,0 @@
-.icon{
-  width: 36px;
-  height: 36px;
-}
-.bgSizeFull{
-  background-size: 100%;
-}
-.iconDoc{
-  .icon();
-  background: url("https://cdn.wehome.cn/cmn/png/57/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-L2E7WI2M-K91.png");
-  .bgSizeFull();
-}
-.iconTxt{
-  .icon();
-  background: url("https://cdn.wehome.cn/cmn/png/88/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-O2E7WI2M-M91.png");
-  .bgSizeFull();
-}
-.iconXls{
-  .icon();
-  background: url("https://cdn.wehome.cn/cmn/png/51/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-U2E7WI2M-771.png");
-  .bgSizeFull();
-}
-.iconPdf{
-  .icon();
-  background: url("https://cdn.wehome.cn/cmn/png/108/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-M2E7WI2M-671.png");
-  .bgSizeFull();
-}
-.iconPpt{
-  .icon();
-  background: url("https://cdn.wehome.cn/cmn/png/89/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-M2E7WI2M-L91.png");
-  .bgSizeFull();
-}

+ 0 - 37
src/pages/knowledge/components/doc-icon/index.tsx

@@ -1,37 +0,0 @@
-import { View } from '@tarojs/components'
-import style from './index.module.less'
-interface Props{
-  type: string
-}
-export default ({type}:Props)=> {
-  const renderIcon = () => {
-    if(type === 'doc' || type === 'docx'){
-      return (
-        <View className={style.iconDoc}></View>
-      )
-    }
-    if(type === 'xls' || type === 'xlsx'){
-      return (
-        <View className={style.iconXls}></View>
-      )
-    }
-    if(type === 'pdf'){
-      return (
-        <View className={style.iconPdf}></View>
-      )
-    }
-    if(type === 'txt'){
-      return (
-        <View className={style.iconTxt}></View>
-      )
-    }
-    if(type === 'ppt'){
-      return (
-        <View className={style.iconPpt}></View>
-      )
-    }
-  }
-  return <>
-    {renderIcon()}
-  </>
-}

+ 0 - 35
src/pages/knowledge/components/doc-tips/index.tsx

@@ -1,35 +0,0 @@
-import { View } from '@tarojs/components'
-export default () => {
-  return (
-    <View className='flex flex-col items-center w-full p-24 gap-32'>
-        <View className='flex flex-col gap-10 w-full'>
-          <View className='flex gap-8 w-full'>
-            <View className='icon-24-fill icon-icon_24_introduce'></View>
-            <View className='text-black font-medium text-16 leading-24'>功能解说</View>
-          </View>
-          <View className='text-14 text-gray-sub leading-28 text-justify'>
-            通过学习您上传的材料,AI将获得对应领域的专业知识,并可以在对话中解答相关问题。(请注意AI的回答无法保证严格的准确性)
-          </View>
-        </View>
-
-        <View className='flex flex-col gap-10 w-full'>
-          <View className='flex gap-8 w-full'>
-              <View className='icon-24-fill icon-icon_24_file'></View>
-              <View className='text-black font-medium text-16 leading-24'>格式要求</View>
-          </View>
-          <View className='text-14 text-gray-sub leading-28 text-justify'>
-            目前支持从微信文件中导入ppt/pdf/txt/word/xls形式的文件,单个不超过 50M。
-          </View>
-        </View>
-        <View className='flex flex-col gap-10 w-full'>
-          <View className='flex gap-8 w-full'>
-            <View className='icon-24-fill icon-icon_24_file'></View>
-            <View className='text-black font-medium text-16 leading-24'>格式要求</View>
-          </View>
-          <View className='text-14 text-gray-sub leading-28 text-justify'>
-            本功能为高级功能,需付费解锁使用。
-          </View>
-        </View>
-      </View>
-  )
-}

+ 0 - 75
src/pages/knowledge/components/uploading-list/RenderFileStatus.tsx

@@ -1,75 +0,0 @@
-import { View } from '@tarojs/components'
-import { TFileItem } from '@/types/file-types'
-import style from './index.module.less'
-import FileUploader from './file-uploader'
-import { useDocumentStore  } from '@/store/documentStore'
-import { useCharacterStore  } from '@/store/characterStore'
-interface Props {
-  fileItem: TFileItem
-  onStart: (r: Taro.UploadTask.UploadTaskPromise, fileItem: TFileItem)=> void
-  onUploaded: (fileItem: TFileItem)=> void
-}
-export default  ({fileItem, onStart, onUploaded}: Props) => {
-  const { createDocument, setUploadListItem} = useDocumentStore()
-  const character = useCharacterStore((state)=> state.character)
-  
-  const handleFileUploaded = async (res:any, fileItem: TFileItem) => {
-    setTimeout(async ()=> {
-    // setState(2)
-      setUploadListItem({...fileItem, status: 2})
-      if(!res.data){
-        return
-      }
-      if(!character?.profileId){
-        return;
-      }
-      const d = await createDocument({
-        filename: fileItem.file.name,
-        characterProfileId: character.profileId,
-        link: res.data,
-        size: fileItem.file.size,
-      })
-      if(d){
-        onUploaded(fileItem)
-      }
-      console.log(d)
-    }, 600)
-    
-  }
-
-  const handleFileUploadStart = (task: Taro.UploadTask.UploadTaskPromise) => {
-    const f = {...fileItem, status: 3}
-    setUploadListItem(f)
-    onStart(task, f)
-    // setState(1)
-    // // 将开始上传的任务句柄存入 list 内
-    // // item.taskHandler
-    // const i = list.findIndex(item=> item.file.path === fileItem.file.path);
-    // if(i > -1){
-    //   const _item = {...list[i], taskHandler: fileItem.taskHandler}
-    //   const _list = [...list.slice(0, i), _item, ...list.slice(i)]
-    //   setList(_list)
-    // }
-    
-  }
-  const renderFileStatus = () => {
-    if(fileItem.status === 2){
-      return <></>
-    }
-    if(fileItem.status === 1){
-      return (
-        <View className={style.wait}>等待上传...</View>
-      )
-    }
-    return (
-      <View className='py-5'>
-        <FileUploader file={fileItem} onUploaded={handleFileUploaded} onStart={handleFileUploadStart}></FileUploader>
-      </View>
-    )
-  }
-  return (
-    <>
-      {renderFileStatus()}
-    </>
-  )
-}

+ 0 - 76
src/pages/knowledge/components/uploading-list/file-uploader.tsx

@@ -1,76 +0,0 @@
-import { View } from "@tarojs/components";
-import Taro from "@tarojs/taro";
-import { useEffect, useState } from "react";
-import style from "./index.module.less";
-
-import { TFileItem } from '@/types/file-types'
-import { uploadFileManager } from '@/utils/http'
-import { EUploadFileScene } from '@/types'
-
-interface Props {
-  file: TFileItem;
-  onUploaded: (res: any, file: TFileItem) => void;
-  onStart: (
-    task: Taro.UploadTask.UploadTaskPromise | null,
-    fileItem: TFileItem
-  ) => void;
-}
-
-export default ({ file, onUploaded, onStart }: Props) => {
-  const [fileProgress, setFileProgress] = useState(0);
-  const uploadAction = (fileItem: TFileItem) => {
-    
-    
-    let uploadTask: Taro.UploadTask.UploadTaskPromise | null;
-    if (process.env.TARO_ENV == "h5") {
-      uploadTask = uploadFileManager(
-        fileItem.file.name,
-        EUploadFileScene.DOC,
-        (res) => {
-          console.log("h5 文件已经上传");
-          onUploaded(res, fileItem);
-          return {
-            code: 0,
-            data: "",
-            msg: "",
-          };
-        },
-        {
-          file: fileItem.file as File,
-        }
-      );
-    } else {
-      uploadTask = uploadFileManager(fileItem.file.path, EUploadFileScene.DOC, (res:any) => {
-        console.log(res)
-        onUploaded(res, fileItem)
-      })
-    }
-
-    uploadTask?.onProgressUpdate((res) => {
-      setFileProgress(res.progress);
-      // console.log('上传进度', res.progress)
-      // console.log('已经上传的数据长度', res.totalBytesSent)
-      // console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend)
-    });
-
-
-
-    onStart(uploadTask, fileItem);
-    // uploadTask.abort() // 取消上传任务
-  };
-
-  useEffect(() => {
-    if (file.status === 0) {
-      uploadAction(file);
-    }
-  }, [file]);
-
-  return (
-    <View className={style.progressBar}>
-      <View
-        className={style.progress}
-        style={{ width: `${fileProgress}%` }}
-      ></View>
-    </View>
-  );
-};

+ 0 - 62
src/pages/knowledge/components/uploading-list/index.module.less

@@ -1,62 +0,0 @@
-.fileItem{
-  width: 100%;
-  box-sizing: border-box;
-  display: flex;
-  padding: 18px 12px;
-  align-items: center;
-  gap: 12px;
-  border-radius: 12px;
-  border: 1px solid #F2F4F5;
-  background: #F5F5F5;
-}
-.icon{
-  width: 36px;
-  height: 36px;
-}
-.bgSizeFull{
-  background-size: 100%;
-}
-
-
-.fileName{
-  color: #000;
-  font-size: 14px;
-  line-height: 22px;
-}
-
-.progressBar{
-  position: relative;
-  width: 100%;
-  height: 4px;
-  background: rgba(217, 217, 217, 0.60);
-}
-.progress{
-  position: absolute;
-  left: 0;
-  top: 0;
-  z-index: 1;
-  height: 100%;
-  width: 0;
-  background-color: var(--color-primary-dark);
-}
-.cancelButton{
-  display: flex;
-  padding: 2px 14px;
-  justify-content: center;
-  align-items: center;
-  border-radius: 20px;
-  border: 1px solid rgba(0, 0, 0, 0.45);
-  text-align: center;
-  font-size: 12px;
-  color: rgba(0, 0, 0, 0.45);
-  line-height: 20px;
-}
-.deleteButton{
-  color: rgba(#000, .2);
-}
-
-.wait{
-  color: rgba(0, 0, 0, 0.45);
-  font-size: 10px;
-  line-height: 12px;
-}

+ 0 - 76
src/pages/knowledge/components/uploading-list/index.tsx

@@ -1,76 +0,0 @@
-import { View } from "@tarojs/components";
-import Taro from "@tarojs/taro";
-import style from "./index.module.less";
-import { getSuffix } from '@/utils'
-import { TFileItem } from "@/types/file-types";
-import RenderFileStatus from "./RenderFileStatus";
-import RenderIcon from "../doc-icon/index";
-import { useDocumentStore } from "@/store/documentStore";
-interface Props {
-  uploadList: TFileItem[];
-  onUploadComplete: ()=> void;
-}
-
-export default ({ uploadList }: Props) => {
-  const uploadingList = useDocumentStore((state) => state.uploadingList);
-
-  const handleCancel = (item: TFileItem) => {
-    item.taskHandler?.abort();
-  };
-
-  const onStart = (
-    task: Taro.UploadTask.UploadTaskPromise,
-    fileItem: TFileItem
-  ) => {
-    console.log(task, fileItem);
-    // const _list = list.map((item) => {
-    //   if (item.file.name === fileItem.file.name) {
-    //     console.log(fileItem.file.name,99999)
-    //     return {
-    //       ...item,
-    //       taskHandler: task,
-    //       status: 1,
-    //     };
-    //   }
-    //   return item;
-    // });
-    // setList(_list);
-  };
-
-  // 上传文件成功
-  const onUploaded = (fileItem: TFileItem)=> {
-    
-  }
-
-  return (
-    <>
-      {uploadingList.map((item, index) => {
-        return (
-          <View className={style.fileItem} key={index}>
-            <RenderIcon type={getSuffix(item.file.name)}></RenderIcon>
-            <View className="flex flex-col flex-1 gap-2">
-              <View className={`${style.fileName} truncate`}>{item.file.name}</View>
-              {(item.status === 0||item.status===3) && (
-                <RenderFileStatus
-                  fileItem={item}
-                  onStart={onStart}
-                  onUploaded={onUploaded}
-                ></RenderFileStatus>
-              )}
-            </View>
-            {item.status === 1 && (
-              <View
-                className={style.cancelButton}
-                onClick={() => {
-                  handleCancel(item);
-                }}
-              >
-                取消
-              </View>
-            )}
-          </View>
-        );
-      })}
-    </>
-  );
-};

+ 32 - 188
src/pages/knowledge/index.tsx

@@ -9,203 +9,47 @@ import PageCustom from "@/components/page-custom/index";
 import NavBarNormal from "@/components/nav-bar-normal/index";
 import style from "./index.module.less";
 import Taro, { useDidShow, useReady } from "@tarojs/taro";
-import DocTips from "./components/doc-tips/index";
-import UploadingDocList from "./components/uploading-list/index";
-import RenderIcon from "./components/doc-icon/index";
-import { useDocumentStore } from "@/store/documentStore";
-import { useCharacterStore } from "@/store/characterStore";
-import { useAppStore } from "@/store/appStore";
-import { TFileItem } from "@/types/file-types";
-import { IEntityDocument } from "@/types/index";
-import { getSuffix } from "@/utils";
-const fileTypes = ["doc", "docx", "xls", "xlsx", "pdf", "txt", "ppt"];
+import WemetaTabs from "@/components/wemeta-tabs/index";
+import ContainerHeader from './components/container-header'
 
 
 export default function Index() {
-  const documents = useDocumentStore((state) => state.documents);
-  const uploadingList = useDocumentStore((state) => state.uploadingList);
-  const vip = useAppStore((state) => state.vip);
-  const {setVipPopupVisible} = useAppStore()
-  const { fetchDocumentList, deleteDocument, setUploadingList } =
-    useDocumentStore();
-  const character = useCharacterStore((state) => state.character);
-  const [uploadList, setUploadList] = useState<TFileItem[]>([]);
-  const inputRef = useRef<HTMLInputElement>(null);
-  const handleH5FileChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const selectedFiles = Array.from(e.target.files || []);
-    if (selectedFiles.length > 10) {
-      Taro.showToast({
-        title: "最多只能上传10个文件",
-        icon: "error",
-      });
-      return;
-    }
 
-
-    const files = selectedFiles.map((file):TFileItem => {
-      return {
-        file,
-        status: 0,
-        fileProgress: {
-          progress: 0,
-          totalBytesSent: 0,
-          totalBytesExpectedToSend: 0,
-        },
-      };
-    });
-    setUploadingList(files);
-
-  };
-
-
-  const handleClick = () => {
-    if (process.env.TARO_ENV == "h5") {
-      inputRef.current?.click();
-      return;
-    }
-    Taro.chooseMessageFile({
-      count: 10,
-      type: "file",
-      extension: fileTypes.map((f) => `${f}`),
-      success(res) {
-        const tempFilePaths = res.tempFiles;
-        console.log("tempFilePaths", tempFilePaths);
-        const files = tempFilePaths.map((file) => {
-          return {
-            file,
-            status: 0,
-            fileProgress: {
-              progress: 0,
-              totalBytesSent: 0,
-              totalBytesExpectedToSend: 0,
-            },
-          };
-        });
-        setUploadingList(files);
-        // setUploadList(files);
-        console.log(tempFilePaths);
-      },
-    });
-  };
-
-  const extractType = (filename: string) => {
-    return getSuffix(filename) ?? "";
-  };
-
-  const onUploadComplete = () => {
-    setUploadList([]);
-  };
-
-  const handleDelete = (document: IEntityDocument) => {
-    Taro.showModal({
-      title: "提示",
-      content: "确定删除该文件吗?",
-      success(res) {
-        if (res.confirm) {
-          document.id && deleteDocument(document.id);
-        } else if (res.cancel) {
-          console.log("用户点击取消");
-        }
-      },
-    });
-  };
-
-  useReady(() => {
-    fetchDocumentList({
-      page: 1,
-      pageSize: 20,
-      profileId: character?.profileId,
-    });
-  });
-
-  const navTo = () => {
-    Taro.navigateTo({
-      url: "/pages/knowledge-lesson/index",
-    });
-  };
-
-  const showTips = () => {
-    return !documents.length && !uploadingList.length;
-  };
-
-  const renderDocList = () => {
-    if (!uploadingList.length) {
-      return <></>;
-    }
-    return (
-      <UploadingDocList
-        uploadList={uploadList}
-        onUploadComplete={onUploadComplete}
-      ></UploadingDocList>
-    );
-  };
-
-  
-
-  useDidShow(()=> {
-    if(vip?.name === 'Free'){
-      setVipPopupVisible(true)
-    }
-  })
-
-  return (
-    <PageCustom bgColor="global-light-green-bg">
-      <NavBarNormal>专业知识</NavBarNormal>
-      <View className="flex flex-col items-center w-full py-32 px-24">
-        {process.env.TARO_ENV == "h5" && (
-          <input
-            type="file"
-            multiple
-            ref={inputRef}
-            onChange={handleH5FileChange}
-            accept=".ppt,.pptx,.pdf,.txt,.doc,.docx,.xls,.xlsx"
-            style={{
-              display: "none",
-            }}
-          />
-        )}
-        <View className={style.importCardButton} onClick={handleClick}>
-          <View className={style.createButton}>
-            <Image src='https://cdn.wehome.cn/cmn/png/100/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-VDZCWI2M-O91.png' className={style.iconAdd}></Image>
+  const tabList = [
+    {
+      key: "1",
+      label: "个人知识",
+      children: (
+        <>
+          <View className="pt-12">
+            <ContainerHeader />
           </View>
-          <View className={style.importTips}>
-            <View>导入微信文件</View>
+        </>
+      ),
+    },
+    {
+      key: "2",
+      label: "企业知识",
+      children: (
+        <>
+          <View className="pt-12">
+            
           </View>
+        </>
+      ),
+    },
+  ];
+  return (
+    <PageCustom >
+      <NavBarNormal backText="知识库"></NavBarNormal>
+      <View className="w-full">
+        <View className="pt-8 px-16">
+          <WemetaTabs current="1" list={tabList}></WemetaTabs>
         </View>
-      </View>
-      <View className="w-full px-28">
-        <View className="w-full border-t border-gray-light">
-          <View className="flex flex-col items-center w-full gap-12">
-            {!showTips() && (
-              <View className="flex pt-32 items-center w-full px-4">
-                <View className="text-black font-medium text-12 leading-24 flex-1">
-                  专业知识
-                </View>
-                <View className="primary-color-dark text-12 " onClick={navTo}>
-                  查看教程
-                </View>
-              </View>
-            )}
-            {renderDocList()}
+        
+
 
-            {documents.map((item, index) => {
-              return (
-                <View className={style.fileItem} key={index}>
-                  <RenderIcon type={extractType(item.filename)}></RenderIcon>
-                  <View className="flex flex-col flex-1 gap-2">
-                    <View className={style.fileName}>{item.filename}</View>
-                  </View>
-                  <View
-                    className={`iconfont icon-icon_24_delet icon-24 ${style.deleteButton}`}
-                    onClick={() => handleDelete(item)}
-                  ></View>
-                </View>
-              );
-            })}
-          </View>
-        </View>
       </View>
-      <View className="pb-40">{showTips() && <DocTips></DocTips>}</View>
     </PageCustom>
   );
 }

+ 27 - 1
src/pages/profile/components/top-bar/index.tsx

@@ -17,11 +17,13 @@ import AgentSwap from "../agent-swap/index";
 import AgentQRCode from "../agent-qrcode/index";
 import SharePopup from "@/components/custom-share/share-popup";
 import Taro from "@tarojs/taro";
+import PopupSheets from "@/components/popup/popup-sheets";
 
 export default () => {
   const headerHeight = useAppStore((state) => state.headerHeight);
   const [showAgentSwap, setShowAgentSwap] = useState(false);
   const [showAgentQRcode, setShowAgentQRcode] = useState(false);
+  const [showPopup, setShowPopup] = useState(false);
   const [showShare, setShowShare] = useState(false);
 
   // 自定义背景样式
@@ -96,7 +98,9 @@ export default () => {
               >
                 <IconEditBlack />
               </View>
-              <View className={style.boxButton}>
+              <View className={style.boxButton} onClick={()=> {
+                setShowPopup(true)
+              }}>
                 <IconMoreBlack />
               </View>
             </View>
@@ -137,6 +141,28 @@ export default () => {
         setShow={setShowShare}
         character={null}
       ></SharePopup>
+      <PopupSheets 
+          setShow={setShowPopup} 
+          show={showPopup}
+          content={[
+            {item: <View className="text-12 font-normal text-gray-45">删除后所有数据将被清空,且无法恢复</View>}, 
+            {item:'删除', type: 'warn', onClick: ()=> {
+              Taro.showModal({
+                content: '确认删除该智能体?',
+                success: function (res) {
+                  if (res.confirm) {
+                    console.log('用户点击确定')
+                  } else if (res.cancel) {
+                    console.log('用户点击取消')
+                  }
+                  setShowPopup(false);
+                }
+              })
+              
+            }},
+          ]}
+        >
+        </PopupSheets>
     </View>
   );
 };

+ 0 - 4
src/pages/system-voice/index.config.ts

@@ -1,4 +0,0 @@
-export default definePageConfig({
-  navigationBarTitleText: '系统声音',
-  "usingComponents": {},
-})

+ 0 - 59
src/pages/vip/components/price-cards/index.module.less

@@ -1,59 +0,0 @@
-.priceCards{
-  width: calc(100% - 100px);
-  margin-left: 28px;
-}
-.card{
-  padding: 36px 8px 8px;
-  flex-direction: column;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  border-radius: 20px;
-  background: white;
-}
-.type{
-  margin-bottom: 4px;
-  color: #000;
-  font-size: 16px;
-  font-weight: 500;
-  line-height: 24px;
-}
-.moneySign{
-  color: #000;
-  font-size: 14px;
-  font-weight: 600;
-  line-height: 26px;
-}
-.priceRow{
-  margin-bottom: 25px;
-  display: flex;
-  align-items: flex-end;
-}
-.price{
-  color: #000;
-  font-size: 32px;
-  font-weight: 600;
-  line-height: 40px; 
-}
-.perDay{
-  display: flex;
-  padding: 4px 8px;
-  width: 108px;
-  justify-content: center;
-  align-items: center;
-  border-radius: 48px;
-  background-color: #ededed;
-  color: rgba(#000, .65);
-  font-size: 10px;
-  font-weight: 400;
-  line-height: 15px;
-}
-.cardActived{
-  .card();
-  border: 3px solid #000;
-}
-.perDayActived{
-  .perDay();
-  font-weight: 500;
-  background: var(--color-bg-primary);
-}

+ 0 - 70
src/pages/vip/components/price-cards/index.tsx

@@ -1,70 +0,0 @@
-import { View } from "@tarojs/components";
-import style from "./index.module.less";
-import { useState } from "react";
-import { TModelVipPrice } from "@/types/index";
-interface Props {
-  prices: TModelVipPrice[]
-  onSelected: (priceId: number) => void;
-}
-
-export default ({ prices, onSelected }: Props) => {
-  const [index, setIndex] = useState(0);
-  
-  // console.log(prices, appConfig?.vips)
-  if (!prices) return <></>;
-  const handleClick = (_index: number) => {
-    setIndex(_index);
-    if (prices) {
-      onSelected(prices[_index].id);
-    }
-  };
-  return (
-    <View className={style.priceCards}>
-      {prices.map((price, _index) => {
-        return (
-          <View
-            className={`inline-block mr-12 ${(prices.length-1) === _index ? 'mr-28' : ''}`}
-            onClick={() => {
-              handleClick(_index);
-            }}
-          >
-            <View className={index === _index ? style.cardActived : style.card}>
-              <View className={style.type}>{price.name}</View>
-              <View className={style.priceRow}>
-                <View className={style.moneySign}>¥</View>
-                <View className={style.price}>{price.unitAmount / 100}</View>
-              </View>
-              <View
-                className={
-                  index === _index ? style.perDayActived : style.perDay
-                }
-              >
-                每天仅需¥{price.costPerDay}
-              </View>
-            </View>
-          </View>
-        );
-      })}
-      {/* <View className="inline-block mr-12">
-          <View className={index === 1 ? style.cardActived : style.card}>
-            <View className={style.type}>季卡</View>
-            <View className={style.priceRow}>
-              <View className={style.moneySign}>¥</View>
-              <View className={style.price}>88</View>
-            </View>
-            <View className={index === 1 ? style.perDayActived: style.perDay}>每天仅需¥0.63</View>
-          </View>
-      </View> 
-      <View className="inline-block mr-12">
-          <View className={index === 1 ? style.cardActived : style.card}>
-            <View className={style.type}>年卡</View>
-            <View className={style.priceRow}>
-              <View className={style.moneySign}>¥</View>
-              <View className={style.price}>120</View>
-            </View>
-            <View className={index === 1 ? style.perDayActived: style.perDay}>每天仅需¥0.63</View>
-          </View>
-      </View> */}
-    </View>
-  );
-};

+ 0 - 56
src/pages/vip/components/vip-rights/index.module.less

@@ -1,56 +0,0 @@
-.wrap{
-  display: flex;
-  width: 100%;
-  box-sizing: border-box;
-  padding: 24px 12px;
-  flex-direction: column;
-  border-radius: 16px;
-  background: white;
-}
-.item{
-  width: 140px;
-}
-.disabled{
-  opacity: .5;
-}
-
-.centerIcon{
-  position: relative;
-  width: 18px;
-  height: 14px;
-}
-.centerIconSmallSquare{
-  position: absolute;
-  left: 0;
-  top: 3px;
-  z-index: 1;
-  width: 6px;
-  height: 6px;
-  transform-origin: 50% 50%;
-  transform: rotate(45deg);
-  background-color: #262626;
-}
-.centerIconSquare{
-  position: absolute;
-  z-index: 2;
-  left: 5px;
-  top: 2px;
-  z-index: 1;
-  width: 10px;
-  height: 10px;
-  transform-origin: 50% 50%;
-  transform: rotate(45deg);
-  background-color: var(--color-primary);
-}
-.rightIcon{
-  .centerIcon();
-  .centerIconSmallSquare{
-    left: initial;
-    right: 0
-  };
-  .centerIconSquare{
-    right: 5px;
-    left: initial;
-  }
-  
-}

+ 0 - 96
src/pages/vip/components/vip-rights/index.tsx

@@ -1,96 +0,0 @@
-import { View, Image, Text } from "@tarojs/components";
-import style from './index.module.less'
-import { TModelAppConfig } from '@/types/index'
-import { APP_NAME_TEXT } from '@/config'
-
-interface Props {
-  appConfig: TModelAppConfig;
-}
-
-export default ({appConfig}: Props) => {
-  const vipMetadata = appConfig?.vips?.find((item)=> item.id === 'vip_plus')?.metadata
-  if(!vipMetadata){
-    return <></>
-  }
-  return (
-    <View className={style.wrap}>
-      <View className="flex items-center justify-center w-full mb-30">
-        <View className={style.centerIcon}>
-          <View className={style.centerIconSmallSquare}></View>
-          <View className={style.centerIconSquare}></View>
-        </View>
-        <View className="text-16 font-medium leading-24">尊享六大权益</View>
-        <View className={style.rightIcon}>
-          <View className={style.centerIconSmallSquare}></View>
-          <View className={style.centerIconSquare}></View>
-        </View>
-      </View>
-      <View className="flex flex-col w-full gap-26">
-        <View className="flex flex-wrap justify-between items-center  w-full">
-          <View className={`flex gap-8 ${style.item}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/70/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-OXMJWI2M-971.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">创建多个{APP_NAME_TEXT}</Text>
-              <Text className="text-11 text-gray-45 leading-18 whitespace-nowrap">最大支持创建{vipMetadata.maxCharacterNum}个</Text>
-            </View>
-          </View>
-          
-          <View className={`flex gap-8 ${style.item} ${style.disabled}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/43/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-3YMJWI2M-A71.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">自定义背景</Text>
-              <Text className="text-11 text-gray-45 leading-18">支持图片上传</Text>
-            </View>
-          </View>
-        </View>
-
-        <View className="flex flex-wrap justify-between items-center w-full">
-          <View className={`flex gap-8 ${style.item}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/254/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-YXMJWI2M-P91.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">克隆音色对话</Text>
-              <Text className="text-11 text-gray-45 leading-12 whitespace-nowrap">{vipMetadata.cloneVoiceChatDuration/60}分钟/天时长</Text>
-            </View>
-          </View>
-          <View className={`flex gap-8 ${style.item}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/131/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-BYMJWI2M-B71.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">搭建知识库</Text>
-              <Text className="text-11 text-gray-45 leading-18">{vipMetadata.maxDocSize}M文档上传容量</Text>
-            </View>
-          </View>
-        </View>
-        
-        <View className="flex flex-wrap justify-between items-center w-full">
-          <View className={`flex gap-8 ${style.item}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/150/META-H8UKVHWU-KIGP3BIL7M5AYC6XHNUA2-3YMJWI2M-Q91.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">AI 对话记录</Text>
-              <Text className="text-11 text-gray-45 leading-18 whitespace-nowrap">支持查看聊天内容</Text>
-            </View>
-          </View>
-          
-          <View className={`flex gap-8 ${style.item}`}>
-            <View className="w-40 h-40">
-              <Image src='https://cdn.wehome.cn/cmn/png/190/META-H8UK0IWU-LIGPDI0B88LDCAMAO6LN3-BYMJWI2M-C71.png' mode="widthFix" className="w-40 h-40"></Image>
-            </View>
-            <View className="flex flex-col">
-              <Text className="text-12 text-black leading-18">访问数据统计</Text>
-              <Text className="text-11 text-gray-45 leading-18 whitespace-nowrap">支持查看访问数据</Text>
-            </View>
-          </View>
-        </View>
-      </View>
-    </View>
-  );
-};

+ 0 - 5
src/pages/vip/index.config.ts

@@ -1,5 +0,0 @@
-export default definePageConfig({
-  navigationBarTitleText: '升级会员',
-  "usingComponents": {},
-  "navigationStyle": 'custom',
-})

+ 0 - 27
src/pages/vip/index.module.less

@@ -1,27 +0,0 @@
-
-.tab{
- 
-}
-.tabBarContainer{
-  display: flex;
-  border-radius: 12px 12px 0px 0px;
-  // background: linear-gradient(176deg, #E4FFDC 28.77%, #FFF 98.3%);
-}
-.tabBar{
-  padding: 14px 0;
-  border-radius: 12px 12px 0px 0px;
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center
-  
-}
-.tabBarActived{
-  .tabBar();
-  background-color: white;
-}
-.tabContent{
-  padding: 28px 0px;
-  min-height: 100vh;
-  background-color: var(--color-bg-gray);
-}

+ 0 - 144
src/pages/vip/index.tsx

@@ -1,144 +0,0 @@
-import { View, ScrollView, Text } from "@tarojs/components";
-import Taro, { useReady } from "@tarojs/taro";
-import NavBarNormal from "@/components/nav-bar-normal/index";
-import { useAppStore } from "@/store/appStore";
-import { useUserStore } from "@/store/userStore";
-import style from "./index.module.less";
-import { useState } from "react";
-import PriceCards from "./components/price-cards/index";
-import VipRights from "./components/vip-rights/index";
-import PopupPaySuccess from "@/components/popup/popup-pay-success/index";
-import { payVip } from "@/service/pay";
-import { TPaySign } from "@/types/index";
-import PopupMp from "@/components/popup/popup-mp";
-import {  navToWebView } from '@/utils/index'
-import { APP_AGREEMENT_URL } from '@/config'
-import {reportPageVisit} from '@/utils/report'
-export default () => {
-  const headerHeight = useAppStore((state) => state.headerHeight);
-  const appConfig = useAppStore((state) => state.appConfig);
-  const isIos = useAppStore((state) => state.isIos);
-  const [visible, setVisible] = useState(false)
-  let paying = false;
-  const [showSuccess, setShowSuccess] = useState(false);
-  const { fetchUser } = useUserStore();
-  const prices = appConfig?.vips?.find(
-    (item) => item.id === "vip_plus"
-  )?.prices;
-  // const [tabIndex, setTabIndex] = useState(0);
-  let priceId: number | undefined = prices?.[0].id;
-  const { fetchAppConfig } = useAppStore();
-  const handleSelected = (_priceId: number) => {
-    console.log(_priceId);
-    priceId = _priceId;
-  };
-  const goPay = async () => {
-    if(isIos){
-      Taro.showModal({
-        content: '根据微信规定,iOS平台暂不提供支付功能。',
-        showCancel: false,
-      });
-      // setVisible(true)
-      return
-    }
-    if (paying) {
-      return;
-    }
-    if (!priceId) {
-      return;
-    }
-    paying = true;
-    const res = await payVip(priceId);
-    if (!res?.data?.sign) {
-      paying = false;
-      return;
-    }
-    const sign = JSON.parse(res.data.sign) as TPaySign;
-    console.log(res);
-    //@ts-ignore
-    Taro.requestPayment({
-      ...sign,
-      success(res) {
-        console.log(res);
-        fetchUser();
-        setShowSuccess(true);
-        paying = false
-        // Taro.showToast({ title: '支付成功', icon: 'success'});
-      },
-      fail(res) {
-        Taro.showToast({ title: "支付失败", icon: "error" });
-        paying = false
-        console.log(res);
-      },
-    });
-  };
-  useReady(() => {
-    fetchAppConfig();
-  });
-
-  reportPageVisit()
-  
-  
-  return (
-    // 有横向滚动,页面需要特殊处理
-    <View className="relative">
-      <View
-        className={`global-color flex flex-col index`}
-        style={{ paddingTop: headerHeight }}
-      >
-        <NavBarNormal>升级会员</NavBarNormal>
-        <PopupMp show={visible} onClose={()=> setVisible(false)}></PopupMp>
-        <PopupPaySuccess
-          show={showSuccess}
-          onClose={() => setShowSuccess(false)}
-        ></PopupPaySuccess>
-        
-        {appConfig && (
-          <View className={style.tab}>
-            {/* <View className={style.tabBarContainer}>
-              <View
-                className={`${
-                  tabIndex === 0 ? style.tabBarActived : style.tabBar
-                }`}
-              >
-                <View>高级会员</View>
-              </View>
-              <View className={`${style.tabBar}`}>
-                <View>超级会员</View>
-              </View>
-            </View> */}
-            <View className={`${style.tabContent}`}>
-              <View className={style.priceScrollView}>
-                <ScrollView
-                  scroll-x="true"
-                  enable-flex
-                  className="whitespace-nowrap w-full mb-26"
-                >
-                  {(!isIos && prices) && <PriceCards
-                    onSelected={handleSelected}
-                    prices={prices}
-                  ></PriceCards>}
-                </ScrollView>
-              </View>
-              <View className="px-28">
-                <View
-                  className="full-button-rounded"
-                  onClick={() => {
-                    goPay();
-                  }}
-                >
-                  申请开通会员
-                </View>
-                <View className="text-center pt-16 pb-36 text-12 leading-20" onClick={()=> navToWebView(APP_AGREEMENT_URL)}>
-                  <Text className="text-6">开通前阅读并同意</Text>
-                  <Text className="primary-color-dark">《会员服务协议》</Text>
-                </View>
-                <VipRights appConfig={appConfig}></VipRights>
-              </View>
-            </View>
-          </View>
-        )}
-      </View>
-    </View>
-  );
-};

+ 0 - 5
src/pages/voice-tabs/index.config.ts

@@ -1,5 +0,0 @@
-export default definePageConfig({
-  navigationBarTitleText: '对话声音',
-  "usingComponents": {},
-
-})

+ 0 - 23
src/pages/voice-tabs/index.module.less

@@ -1,23 +0,0 @@
-.container {
-  background-color: #f5f5f2;
-  padding: 0px 16px 16px 16px;
-  position: relative;
-  width: 100%;
-}
-
-.playContainer {
-  position: sticky;
-  height: 64px;
-  width: 100%;
-  top: 0;
-  left: 0;
-  right: 0;
-  z-index: 8;
-  background-color:#f5f5f2;
-
-}
-
-page {
-  min-height: 100vh;
-  background-color: #f5f5f2;
-}

+ 0 - 97
src/pages/voice-tabs/index.tsx

@@ -1,97 +0,0 @@
-import LeafTabs from "@/components/leaf-tabs/index";
-import VoicePlayerBar, { IVoicePlayerBar } from "@/components/voice-player-bar";
-import SystemVoice from "@/pages/system-voice/index";
-import CloneVoice from "@/pages/voice/index";
-import { TServiceAudioModel } from "@/types";
-import { View } from "@tarojs/components";
-import { useRouter } from "@tarojs/taro";
-import React, { useMemo, useRef, useState } from "react";
-import PageWrapper from '@/components/page-wrapper'
-import styles from "./index.module.less";
-
-interface Props {}
-type ExtendedTServiceAudioModel = TServiceAudioModel & { checked: boolean };
-const VoiceTabs: React.FC<Props> = ({}) => {
-  const playerRef = useRef<IVoicePlayerBar>(null);
-  const [sysVoice, setSysVoice] = useState<ExtendedTServiceAudioModel | null>(
-    null,
-  );
-
-  const [voiceName, setVoiceName] = useState("");
-  const [voiceAlias, setVoiceAlias] = useState("");
-  const [voiceIdx, setVoiceIdx] = useState(-1);
-  const router = useRouter();
-  const profileId = router.params.profileId || "";
-  const [tabs] = useState([
-    {
-      label: "克隆声音",
-      id: 0,
-    },
-    {
-      label: "系统声音",
-      id: 1,
-    },
-  ]);
-  const [activeKey, setActiveKey] = useState(0);
-
-  const tabsMemo = useMemo(() => {
-    return tabs;
-  }, [tabs]);
-
-  const handleTabClickAction = (id: number) => {
-    setActiveKey(id);
-  };
-
-  const handlePlayAction = (voiceItem: any, type: "system" | "cloned") => {
-    if (type == "system") {
-      setVoiceName("");
-      setVoiceAlias("");
-      setVoiceIdx(-1);
-
-      setSysVoice(voiceItem);
-      playerRef.current && playerRef.current.play(voiceItem.voice);
-    } else {
-      setSysVoice(null);
-      setVoiceName(voiceItem.voiceName);
-      setVoiceAlias(voiceItem.voiceAlias)
-      setVoiceIdx(voiceItem.voiceIndex);
-      playerRef.current && playerRef.current.play(voiceItem.voiceName);
-    }
-  };
-
-  return (
-    <PageWrapper>
-      <View className={styles.container}>
-        <View className={styles.playContainer}>
-          <VoicePlayerBar
-            ref={playerRef}
-            voiceItem={sysVoice}
-            voiceName={voiceName}
-            voiceNameText={voiceIdx == -1 ? "" : voiceAlias}
-          />
-        </View>
-        <LeafTabs tabs={tabsMemo} onTabChange={handleTabClickAction} />
-        <View className={styles.content}>
-          {activeKey == 0 && (
-            <CloneVoice
-              profileId={profileId}
-              onPlay={(item) => {
-                handlePlayAction(item, "cloned");
-              }}
-            ></CloneVoice>
-          )}
-          {activeKey == 1 && (
-            <SystemVoice
-              profileId={profileId}
-              onPlay={(item) => {
-                handlePlayAction(item, "system");
-              }}
-            ></SystemVoice>
-          )}
-        </View>
-      </View>
-    </PageWrapper>
-  );
-};
-
-export default VoiceTabs;

+ 0 - 0
src/pages/voice/components/index.module.less → src/pages/voice/components/my-voice/components/index.module.less


+ 0 - 0
src/pages/voice/components/index.tsx → src/pages/voice/components/my-voice/components/index.tsx


+ 9 - 19
src/pages/voice/components/popup-recorder/index.module.less → src/pages/voice/components/my-voice/components/popup-recorder/index.module.less

@@ -1,7 +1,6 @@
 .container{
-  padding: 32px 16px 48px;
   height: 100%;
-  background-color: white;
+  background-color: transparent !important;
 }
 .content{
   display: flex;
@@ -32,6 +31,7 @@
 }
 .actionButtonContainer{
   display: flex;
+  align-items: center;
   justify-content: center;
   flex-direction: column;
   gap: 8px;
@@ -52,31 +52,21 @@
 .actionButton{
   position: relative;
   display: flex;
-  width: 343px;
-  height: 54px;
+  width: 316px;
+  height: 48px;
   justify-content: center;
   align-items: center;
-  border-radius: 30px;
-  background: #CBF706;
+  border-radius: 8px;
+  background: white;
+  box-shadow: 0px 2px 16px 0px #00000026;
   overflow: hidden
 }
 .actionButtonActived{
   .actionButton();
-  background: #F3F706;
+  background: var(--color-primary);
+  box-shadow: none;
 }
 
-.vBarContainer{
-  display: flex;
-  align-items: center;
-  gap: 4px;
-  height: 7px;
-}
-.vBar{
-  width: 2px;
-  height: 7px;
-  background-color: #000;
-  border-radius: 2px;
-}
 .speechButton{
   display: flex;
   position: absolute;

+ 1 - 2
src/pages/voice/components/popup-recorder/index.tsx → src/pages/voice/components/my-voice/components/popup-recorder/index.tsx

@@ -203,8 +203,7 @@ export default ({
     <View className={popupStyle.container}>
       <View className={popupStyle.content}>
         <View className={popupStyle.title}>
-          <View>在安静的环境下朗读</View>
-          <View>保持语言的自然和畅通</View>
+          <View>请朗读</View>
         </View>
         <View className="flex-1 flex items-center">
           <View className={popupStyle.demoText}>

+ 0 - 0
src/pages/voice/components/popup-tryout/index.module.less → src/pages/voice/components/my-voice/components/popup-tryout/index.module.less


+ 0 - 0
src/pages/voice/components/popup-tryout/index.tsx → src/pages/voice/components/my-voice/components/popup-tryout/index.tsx


+ 20 - 0
src/pages/voice/components/my-voice/index.module.less

@@ -0,0 +1,20 @@
+.listIcon{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 56px;
+  height: 56px;
+  border-radius: 12px;
+  background-color: rgba(#E7FE55, .3);
+}
+.iconImage{
+  width: 20px;
+  height: 20px;
+}
+
+.addButton {
+  position: fixed;
+  bottom: 24px;
+  left: 16px;
+  right: 16px;
+}

+ 277 - 0
src/pages/voice/components/my-voice/index.tsx

@@ -0,0 +1,277 @@
+import EmptyData from "@/components/empty-data";
+import CardListItem from "@/components/list/card-list-item/index";
+import CardList from "@/components/list/card-list/index";
+import { IVoicePlayerBar } from "@/components/voice-player-bar/index";
+import WemetaRadio from "@/components/wemeta-radio/index";
+import { CloneVoiceStatus } from "@/consts/enum";
+import IconWave from "@/images/icon-wave-20.png";
+import { voiceCloneConfirm } from "@/service/character";
+import { useAppStore } from "@/store/appStore";
+import { useCharacterStore } from "@/store/characterStore";
+import { ICharacter, TEntityVoiceCloneRecord } from "@/types";
+import { formatDateFull, getCloneVoiceIdentifier } from "@/utils/index";
+import { Image, View } from "@tarojs/components";
+import { useEffect, useRef, useState } from "react";
+import Popup from "@/components/popup/popup";
+import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
+import PopupTryout from "./components/popup-tryout/index";
+import style from "./index.module.less";
+
+interface Props {
+  value?: ICharacter;
+  setValue?: (value: ICharacter) => void;
+  profileId: string;
+  onPlay?: (voice: any) => void;
+}
+
+export default ({ profileId, onPlay }: Props) => {
+  const playerRef = useRef<IVoicePlayerBar>(null);
+  const intervalRef = useRef<NodeJS.Timeout | null>(null);
+  const [show, setShow] = useState(false);
+  const [popupType, setPopupType] = useState<"clone" | "try" | "reclone">(
+    "clone",
+  );
+
+  const [voiceName, setVoiceName] = useState("");
+  const [voiceIndex, setVoiceIndex] = useState(-1);
+
+  const { saveCharacter, fetchCharacter, fetchVoiceCloneHistory } =
+    useCharacterStore();
+  const character = useCharacterStore((state) => state.character);
+  const voiceList = useCharacterStore((state) => state.voiceList);
+  const appConfig = useAppStore((state) => state.appConfig);
+
+  const createVoiceNameText = (voiceName: string) => {
+    const i = voiceList.findIndex((item) => item.voiceName === voiceName);
+    console.log(voiceName, i);
+    if (i === -1) return "";
+    return `克隆声音 0${i + 1}`;
+  };
+  // 克隆列表操作
+  const renderRightColumn = (item?: TEntityVoiceCloneRecord) => {
+    if (item?.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
+      return <WemetaRadio disabled></WemetaRadio>;
+    }
+
+    if (item?.status == CloneVoiceStatus.CloneVoiceStatusSuccess) {
+      const notSystemVoice = (character?.voice !== character?.defaultSystemVoice)
+      const isSameVoice = (item.voiceName === character?.voice)
+      return (
+        <WemetaRadio
+          checked={ isSameVoice && notSystemVoice}
+        ></WemetaRadio>
+      );
+    }
+
+    if (item?.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
+      return (
+        <View className="text-14 bg-primary text-black leading-22 px-12 py-6 rounded-28 active:pressed-button">
+          试听
+        </View>
+      );
+    }
+    return <></>;
+  };
+
+  // 克隆状态
+  const renderCloneStatus = (item: TEntityVoiceCloneRecord) => {
+    if (item.status === CloneVoiceStatus.CloneVoiceStatusSuccess) {
+      return (
+        <View className={`text-12 leading-20 text-gray-45`}>
+          {item.createdAt && formatDateFull(new Date(item.createdAt))}
+        </View>
+      );
+    }
+    if (item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
+      return <View className={`text-12 leading-20 text-orange`}>试听确认</View>;
+    }
+
+    if (item.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
+      return <View className={`text-12 leading-20 text-orange`}>已过期</View>;
+    }
+    return <View>当前时间</View>;
+  };
+
+  const renderCloneList = () => {
+    if (!voiceList.length) {
+      return <EmptyData></EmptyData>;
+    }
+    return (
+      <CardList>
+        {voiceList.map((item, _index) => {
+          return (
+            <CardListItem
+              rightRenderer={() => {
+                return renderRightColumn(item);
+              }}
+              onClick={() => handleSelect(item, _index)}
+            >
+              <View className="flex items-center gap-16">
+                <View className={style.listIcon}>
+                  <Image src={IconWave} className={style.iconImage}></Image>
+                </View>
+                <View className="flex flex-col gap-4">
+                  <View className={style.ListItemText}>
+                    {/* deprecated getCloneVoiceIdentifier */}
+                    {item.voiceAlias ?? getCloneVoiceIdentifier(_index + 1)}
+                  </View>
+                  {renderCloneStatus(item)}
+                </View>
+              </View>
+            </CardListItem>
+          );
+        })}
+      </CardList>
+    );
+  };
+
+  const handleSelect = (item: TEntityVoiceCloneRecord, index: number) => {
+    setVoiceIndex(index);
+
+    if (item.status == CloneVoiceStatus.CloneVoiceStatusSuccess) {
+      if (item.voiceName) {
+        setVoiceName(item.voiceName);
+        onPlay &&
+          onPlay({
+            voiceName: item.voiceName,
+            voiceAlias: item.voiceAlias,
+            voiceIndex: index,
+          });
+      }
+      saveCharacter({
+        profileId: profileId,
+        voice: item.voiceName,
+      });
+    }
+
+    if (item.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
+      // 未确认,弹出试听框
+      setPopupType("try");
+      setShow(true);
+    }
+  };
+
+  // 克隆按钮状态
+  const handleCloneStatus = (status: ECloneStatus) => {
+    console.log(status);
+  };
+
+  const fetchVoiceList = async () => {
+    if (profileId) {
+      const r = await fetchVoiceCloneHistory(profileId);
+      const result = r.find((item) => item.status === "pending");
+      if (result) {
+        intervalRef.current = setTimeout(() => fetchVoiceList(), 3000);
+      } else {
+        stopTimer();
+      }
+    }
+  };
+
+  const handleSureAction = async () => {
+    setShow(false);
+    await voiceCloneConfirm(voiceList[voiceIndex].voiceName!);
+    fetchVoiceList();
+  };
+
+  const handleRecloneAction = () => {
+    setPopupType("reclone");
+    setShow(true);
+  };
+
+  // 声音录制完成
+  const onRecordEnd = (r: string) => {
+    console.log("onRecordEnd:", r);
+    fetchVoiceList();
+  };
+
+  const stopTimer = () => {
+    if (intervalRef.current !== null) {
+      clearTimeout(intervalRef.current);
+      intervalRef.current = null;
+    }
+  };
+
+  const calcRemainCloneNum = () => {
+    if (appConfig?.maxCloneNum) {
+      const clonedNum = voiceList.filter(
+        (item) =>
+          item.status === CloneVoiceStatus.CloneVoiceStatusSuccess ||
+          item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed,
+      ).length;
+      const remainNum = appConfig.maxCloneNum - clonedNum;
+      return remainNum < 0 ? 0 : remainNum;
+    }
+    return voiceList.length;
+  };
+
+  const initPage = async (profileId: string) => {
+    await fetchCharacter(profileId);
+    fetchVoiceCloneHistory(profileId);
+    character?.voice && setVoiceName(character.voice);
+  };
+
+  useEffect(() => {
+    profileId && initPage(profileId);
+  }, [profileId]);
+
+  // 清除定时器
+  useEffect(() => {
+    return () => {
+      stopTimer();
+    };
+  }, []);
+  return (
+    <View className={`flex flex-col`}>
+      <View className="flex-1">
+        {/* <VoicePlayerBar
+          ref={playerRef}
+          voiceName={voiceName}
+          voiceNameText={createVoiceNameText(voiceName)}
+        /> */}
+
+        {renderCloneList()}
+      </View>
+
+      <Popup show={show} setShow={setShow}>
+        <View>
+          {(popupType == "clone" || popupType == "reclone") && (
+            <PopupRecorder
+              onRecordEnd={onRecordEnd}
+              show={show}
+              setShow={setShow}
+              setCloneStatus={(status) => handleCloneStatus(status)}
+              voiceName={
+                popupType == "reclone" ? voiceList[voiceIndex].voiceName! : ""
+              }
+            ></PopupRecorder>
+          )}
+          {popupType == "try" && (
+            <PopupTryout
+              show={show}
+              onSure={handleSureAction}
+              onReclone={handleRecloneAction}
+              voiceName={voiceList[voiceIndex].voiceName!}
+              showName={getCloneVoiceIdentifier(voiceIndex + 1)}
+            ></PopupTryout>
+          )}
+        </View>
+      </Popup>
+
+      <View
+        className={style.addButton}
+        onClick={() => {
+          setPopupType("clone");
+          setShow(true);
+        }}
+      >
+        <View className="button-rounded-big font-medium">
+          <View>添加克隆声音</View>
+          <View className="font-normal text-12 leading-0">
+            (剩{calcRemainCloneNum()}次)
+          </View>
+        </View>
+      </View>
+    </View>
+  );
+};

+ 0 - 67
src/pages/voice/components/popup-container/index.module.less

@@ -1,67 +0,0 @@
-.container {
-  padding: 32px 16px 48px;
-  height: 100%;
-  background-color: white;
-}
-.content {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  align-items: center;
-}
-.title {
-  color: #000;
-  text-align: center;
-  font-size: 24px;
-  font-style: normal;
-  font-weight: 500;
-  line-height: 32px; /* 133.333% */
-}
-.desc {
-  color: var(---45, rgba(0, 0, 0, 0.45));
-  text-align: center;
-  text-overflow: ellipsis;
-  font-size: 12px;
-  font-style: normal;
-  font-weight: 400;
-  line-height: 20px; /* 166.667% */
-  margin-top: 8px;
-}
-.iconBg {
-  border-radius: 24px;
-  background: rgba(231, 254, 85, 0.3);
-  width: 120px;
-  height: 120px;
-  margin-top: 36px;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: center;
-  .icon {
-    height: 40px;
-    width: 40px;
-  }
-}
-
-.button {
-  height: 54px;
-  border-radius: 100px;
-
-  text-align: center;
-  font-size: 15px;
-  font-weight: 500;
-  line-height: 54px; /* 160% */
-  letter-spacing: 1.5px;
-}
-.actionButton {
-  .button();
-  background: var(--, #cbf706);
-  color: var(--000100, #000);
-  margin-bottom: 8px;
-}
-
-.recloneButton {
-  .button();
-  background: var(--, #f5f5f2);
-  color: var(--000100, #000);
-}

+ 0 - 31
src/pages/voice/components/popup-container/index.tsx

@@ -1,31 +0,0 @@
-import { PageContainer } from "@tarojs/components";
-
-interface Props {
-  setShow: (show: boolean) => void;
-  show: boolean;
-}
-
-export default ({
-  setShow,
-  show,
-  children,
-}: React.PropsWithChildren<Props>) => {
-  const handlePopupLeave = () => {
-    setShow(false);
-  };
-
-  return (
-    <PageContainer
-      show={show}
-      round
-      overlay
-      overlayStyle='background-color: rgba(0, 0, 0, 0.65)'
-      duration={300}
-      position='bottom'
-      customStyle='height: 70%;'
-      onAfterLeave={handlePopupLeave}
-    >
-      {children}
-    </PageContainer>
-  );
-};

+ 0 - 2
src/pages/system-voice/index.module.less → src/pages/voice/components/system-voice/index.module.less

@@ -7,8 +7,6 @@
   display: flex;
   align-items: center;
   gap: 12px;
-  padding: 20px 16px;
-  border-radius: 20px;
   background-color: white;
 }
 .playStatus{

+ 56 - 42
src/pages/system-voice/index.tsx → src/pages/voice/components/system-voice/index.tsx

@@ -4,16 +4,22 @@ import { useCharacterStore } from "@/store/characterStore";
 import { TServiceAudioModel } from "@/types/index";
 import { Image, View } from "@tarojs/components";
 import { useEffect, useState } from "react";
+import CardList from "@/components/list/card-list";
+import CardListItem from "@/components/list/card-list-item";
 
 type ExtendedTServiceAudioModel = TServiceAudioModel & { checked: boolean };
-export default function Index({ profileId,onPlay }: { profileId: string,onPlay: (voice: any) => void }) {
-
-
+export default function Index({
+  profileId,
+  onPlay,
+}: {
+  profileId: string;
+  onPlay: (voice: any) => void;
+}) {
   const { saveCharacter } = useCharacterStore();
   const character = useCharacterStore((state) => state.character);
   const [list, setList] = useState<ExtendedTServiceAudioModel[]>([]);
   const [sysVoice, setSysVoice] = useState<ExtendedTServiceAudioModel | null>(
-    null,
+    null
   );
 
   const initPage = async () => {
@@ -51,55 +57,63 @@ export default function Index({ profileId,onPlay }: { profileId: string,onPlay:
       });
     }
     // playerRef.current && playerRef.current.play(voiceItem.voice);
-    onPlay && onPlay(voiceItem)
+    onPlay && onPlay(voiceItem);
   };
 
   useEffect(() => {
     console.log("系统声音页面显示");
     initPage();
-  },[profileId]);
+  }, [profileId]);
 
   return (
-    <View className='w-full'>
-      {/* <View className='sticky  left-0 right-0 ' style={{
-        top:'50px',
-        backgroundColor:'#F5F5F2'
-      }}>
-        <VoicePlayerBar ref={playerRef} voiceItem={sysVoice} />
-      </View> */}
-      <View className='px-16 bg-white rounded-20'>
-        <View className='flex flex-col w-full gap-28 px-16 py-24 rounded-20 '>
-          {list.map((item: ExtendedTServiceAudioModel) => {
-            // 与克隆语音是否相同
-            const isEqualToClonedVoice = (item.voice === character?.voice)
-            return (
-              <View
-                className='flex gap-16 w-full'
-                key={item.id}
-                onClick={() => {
-                  handleSelect(item);
-                }}
-              >
-                <Image
-                  src={item.avatar}
-                  mode='widthFix'
-                  className='w-56 h-56 rounded-12 shrink-0'
-                ></Image>
-                <View className='flex flex-1 flex-col gap-4 overflow-hidden'>
-                  <View className='text-14 leading-22 font-medium text-black'>
-                    {item.name}
-                  </View>
-                  <View className='flex-1 text-12 leading-20 font-medium text-gray-45 truncate'>
-                    {item.gender === 1 ? "男" : "女"} {item.style}
-                  </View>
+    <View className="w-full">
+      <View className="flex flex-col w-full gap-28">
+      <CardList>
+          <View className="flex flex-col gap-12">
+        {list.map((item: ExtendedTServiceAudioModel, index) => {
+          // 与克隆语音是否相同
+          const isEqualToClonedVoice = item.voice === character?.voice;
+          return (
+            <CardListItem
+            underline
+            rightRenderer={()=> {
+              return <View className="flex items-center">
+                <WemetaRadio
+                  // checked={item.checked && isEqualToClonedVoice}
+                  checked={ index === 0}
+                ></WemetaRadio>
+              </View>
+            }}
+            leftRenderer={()=> {
+              return <Image
+                src={item.avatar}
+                mode="widthFix"
+                className="w-56 h-56 rounded-12 shrink-0"
+              ></Image>
+            }}
+          >
+            <View
+              className="flex gap-16 w-full"
+              key={item.id}
+              onClick={() => {
+                handleSelect(item);
+              }}
+            >
+              
+              <View className="flex flex-1 flex-col gap-4 overflow-hidden">
+                <View className="text-14 leading-22 font-medium text-black">
+                  {item.name}
                 </View>
-                <View className='flex items-center'>
-                  <WemetaRadio checked={item.checked && isEqualToClonedVoice}></WemetaRadio>
+                <View className="flex-1 text-12 leading-20 font-medium text-gray-45 truncate">
+                  {item.gender === 1 ? "男" : "女"} {item.style}
                 </View>
               </View>
-            );
-          })}
+            </View>
+          </CardListItem>
+          );
+        })}
         </View>
+        </CardList>
       </View>
     </View>
   );

+ 3 - 1
src/pages/voice/index.config.ts

@@ -1,4 +1,6 @@
 export default definePageConfig({
-  navigationBarTitleText: '克隆声音',
+  navigationBarTitleText: '声音',
+  navigationStyle: 'custom',
   "usingComponents": {},
+
 })

+ 20 - 16
src/pages/voice/index.module.less

@@ -1,20 +1,24 @@
-.listIcon{
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 56px;
-  height: 56px;
-  border-radius: 12px;
-  background-color: rgba(#E7FE55, .3);
+page {
+  min-height: 100vh;
+}
+.container {
+  padding: 0px 16px 16px 16px;
+  position: relative;
+  width: 100%;
 }
-.iconImage{
-  width: 20px;
-  height: 20px;
+
+.playContainer {
+  height: 64px;
+  width: 100%;
 }
 
-.addButton {
-  position: fixed;
-  bottom: 24px;
-  left: 16px;
-  right: 16px;
+
+.voiceTab{
+  margin-top: 12px;
+  border-radius: 12px;
+  overflow: hidden;
+  background-color: white;
+}
+.tabContent{
+  height: calc(100vh - 310px);
 }

+ 110 - 261
src/pages/voice/index.tsx

@@ -1,275 +1,124 @@
-import EmptyData from "@/components/empty-data";
-import CardListItem from "@/components/list/card-list-item/index";
-import CardList from "@/components/list/card-list/index";
-import { IVoicePlayerBar } from "@/components/voice-player-bar/index";
-import WemetaRadio from "@/components/wemeta-radio/index";
-import { CloneVoiceStatus } from "@/consts/enum";
-import IconWave from "@/images/icon-wave-20.png";
-import { voiceCloneConfirm } from "@/service/character";
-import { useAppStore } from "@/store/appStore";
-import { useCharacterStore } from "@/store/characterStore";
-import { ICharacter, TEntityVoiceCloneRecord } from "@/types";
-import { formatDateFull, getCloneVoiceIdentifier } from "@/utils/index";
-import { Image, View } from "@tarojs/components";
-import { useEffect, useRef, useState } from "react";
-import PopupContainer from "./components/popup-container";
-import PopupRecorder, { ECloneStatus } from "./components/popup-recorder/index";
-import PopupTryout from "./components/popup-tryout/index";
+import WemetaTabs from "@/components/wemeta-tabs/index";
+import VoicePlayerBar, { IVoicePlayerBar } from "@/components/voice-player-bar";
+import SystemVoice from "./components/system-voice";
+import MyVoice from "./components/my-voice/index";
+import { TServiceAudioModel } from "@/types";
+import { View, ScrollView } from "@tarojs/components";
+import { useRouter } from "@tarojs/taro";
+import React, { useRef, useState } from "react";
+import NavBarNormal from "@/components/nav-bar-normal/index";
 import style from "./index.module.less";
-
-interface Props {
-  value?: ICharacter;
-  setValue?: (value: ICharacter) => void;
-  profileId: string;
-  onPlay?: (voice: any) => void;
-}
-
-export default ({ profileId, onPlay }: Props) => {
-  const playerRef = useRef<IVoicePlayerBar>(null);
-  const intervalRef = useRef<NodeJS.Timeout | null>(null);
-  const [show, setShow] = useState(false);
-  const [popupType, setPopupType] = useState<"clone" | "try" | "reclone">(
-    "clone",
+import PageCustom from "@/components/page-custom/index";
+
+interface Props {}
+type ExtendedTServiceAudioModel = TServiceAudioModel & { checked: boolean };
+const VoiceTabs: React.FC<Props> = ({}) => {
+const playerRef = useRef<IVoicePlayerBar>(null);
+const [sysVoice, setSysVoice] = useState<ExtendedTServiceAudioModel | null>(
+    null
   );
 
   const [voiceName, setVoiceName] = useState("");
-  const [voiceIndex, setVoiceIndex] = useState(-1);
-
-  const { saveCharacter, fetchCharacter, fetchVoiceCloneHistory } =
-    useCharacterStore();
-  const character = useCharacterStore((state) => state.character);
-  const voiceList = useCharacterStore((state) => state.voiceList);
-  const appConfig = useAppStore((state) => state.appConfig);
-
-  const createVoiceNameText = (voiceName: string) => {
-    const i = voiceList.findIndex((item) => item.voiceName === voiceName);
-    console.log(voiceName, i);
-    if (i === -1) return "";
-    return `克隆声音 0${i + 1}`;
-  };
-  // 克隆列表操作
-  const renderRightColumn = (item?: TEntityVoiceCloneRecord) => {
-    if (item?.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
-      return <WemetaRadio disabled></WemetaRadio>;
-    }
-
-    if (item?.status == CloneVoiceStatus.CloneVoiceStatusSuccess) {
-      const notSystemVoice = (character?.voice !== character?.defaultSystemVoice)
-      const isSameVoice = (item.voiceName === character?.voice)
-      return (
-        <WemetaRadio
-          checked={ isSameVoice && notSystemVoice}
-        ></WemetaRadio>
-      );
-    }
-
-    if (item?.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
-      return (
-        <View className="text-14 bg-primary text-black leading-22 px-12 py-6 rounded-28 active:pressed-button">
-          试听
+  const [voiceAlias, setVoiceAlias] = useState("");
+  const [voiceIdx, setVoiceIdx] = useState(-1);
+  const router = useRouter();
+  const profileId = router.params.profileId || "";
+
+  const tabList = [
+    {
+      label: "我的",
+      key: "1",
+      children: (
+        <View className={style.tabContent}>
+          <MyVoice profileId={profileId}></MyVoice>
         </View>
-      );
-    }
-    return <></>;
-  };
-
-  // 克隆状态
-  const renderCloneStatus = (item: TEntityVoiceCloneRecord) => {
-    if (item.status === CloneVoiceStatus.CloneVoiceStatusSuccess) {
-      return (
-        <View className={`text-12 leading-20 text-gray-45`}>
-          {item.createdAt && formatDateFull(new Date(item.createdAt))}
+      ),
+    },
+    {
+      label: "全部",
+      key: "2",
+      children: (
+        <View className={style.tabContent}>
+          <ScrollView
+            scrollY
+            id="scrollView"
+            style={{
+              flex: 1,
+              height: "100%", // 高度自适应
+            }}
+          >
+            <View className="px-16 py-12">
+              <SystemVoice
+                profileId={profileId}
+                onPlay={(item) => {
+                  handlePlayAction(item, "system");
+                }}
+              ></SystemVoice>
+            </View>
+          </ScrollView>
         </View>
-      );
-    }
-    if (item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
-      return <View className={`text-12 leading-20 text-orange`}>试听确认</View>;
-    }
-
-    if (item.status == CloneVoiceStatus.CloneVoiceStatusExpired) {
-      return <View className={`text-12 leading-20 text-orange`}>已过期</View>;
-    }
-    return <View>当前时间</View>;
-  };
-
-  const renderCloneList = () => {
-    if (!voiceList.length) {
-      return <EmptyData></EmptyData>;
-    }
-    return (
-      <CardList>
-        {voiceList.map((item, _index) => {
-          return (
-            <CardListItem
-              rightRenderer={() => {
-                return renderRightColumn(item);
-              }}
-              onClick={() => handleSelect(item, _index)}
-            >
-              <View className="flex items-center gap-16">
-                <View className={style.listIcon}>
-                  <Image src={IconWave} className={style.iconImage}></Image>
-                </View>
-                <View className="flex flex-col gap-4">
-                  <View className={style.ListItemText}>
-                    {/* deprecated getCloneVoiceIdentifier */}
-                    {item.voiceAlias ?? getCloneVoiceIdentifier(_index + 1)}
-                  </View>
-                  {renderCloneStatus(item)}
-                </View>
-              </View>
-            </CardListItem>
-          );
-        })}
-      </CardList>
-    );
-  };
-
-  const handleSelect = (item: TEntityVoiceCloneRecord, index: number) => {
-    setVoiceIndex(index);
-
-    if (item.status == CloneVoiceStatus.CloneVoiceStatusSuccess) {
-      if (item.voiceName) {
-        setVoiceName(item.voiceName);
-        onPlay &&
-          onPlay({
-            voiceName: item.voiceName,
-            voiceAlias: item.voiceAlias,
-            voiceIndex: index,
-          });
-      }
-      saveCharacter({
-        profileId: profileId,
-        voice: item.voiceName,
-      });
-    }
-
-    if (item.status == CloneVoiceStatus.CloneVoiceStatusUnconfirmed) {
-      // 未确认,弹出试听框
-      setPopupType("try");
-      setShow(true);
-    }
-  };
-
-  // 克隆按钮状态
-  const handleCloneStatus = (status: ECloneStatus) => {
-    console.log(status);
-  };
-
-  const fetchVoiceList = async () => {
-    if (profileId) {
-      const r = await fetchVoiceCloneHistory(profileId);
-      const result = r.find((item) => item.status === "pending");
-      if (result) {
-        intervalRef.current = setTimeout(() => fetchVoiceList(), 3000);
-      } else {
-        stopTimer();
-      }
-    }
-  };
-
-  const handleSureAction = async () => {
-    setShow(false);
-    await voiceCloneConfirm(voiceList[voiceIndex].voiceName!);
-    fetchVoiceList();
-  };
-
-  const handleRecloneAction = () => {
-    setPopupType("reclone");
-    setShow(true);
-  };
-
-  // 声音录制完成
-  const onRecordEnd = (r: string) => {
-    console.log("onRecordEnd:", r);
-    fetchVoiceList();
-  };
-
-  const stopTimer = () => {
-    if (intervalRef.current !== null) {
-      clearTimeout(intervalRef.current);
-      intervalRef.current = null;
-    }
-  };
-
-  const calcRemainCloneNum = () => {
-    if (appConfig?.maxCloneNum) {
-      const clonedNum = voiceList.filter(
-        (item) =>
-          item.status === CloneVoiceStatus.CloneVoiceStatusSuccess ||
-          item.status === CloneVoiceStatus.CloneVoiceStatusUnconfirmed,
-      ).length;
-      const remainNum = appConfig.maxCloneNum - clonedNum;
-      return remainNum < 0 ? 0 : remainNum;
+      ),
+    },
+    {
+      label: "女声",
+      key: "3",
+      children: (
+        <View className={style.tabContent}>
+          <View>我的</View>
+        </View>
+      ),
+    },
+    {
+      label: "男声",
+      key: "4",
+      children: (
+        <View className={style.tabContent}>
+          <View>我的</View>
+        </View>
+      ),
+    },
+  ];
+
+  const handlePlayAction = (voiceItem: any, type: "system" | "cloned") => {
+    if (type == "system") {
+      setVoiceName("");
+      setVoiceAlias("");
+      setVoiceIdx(-1);
+
+      setSysVoice(voiceItem);
+      playerRef.current && playerRef.current.play(voiceItem.voice);
+    } else {
+      setSysVoice(null);
+      setVoiceName(voiceItem.voiceName);
+      setVoiceAlias(voiceItem.voiceAlias);
+      setVoiceIdx(voiceItem.voiceIndex);
+      playerRef.current && playerRef.current.play(voiceItem.voiceName);
     }
-    return voiceList.length;
   };
 
-  const initPage = async (profileId: string) => {
-    await fetchCharacter(profileId);
-    fetchVoiceCloneHistory(profileId);
-    character?.voice && setVoiceName(character.voice);
-  };
-
-  useEffect(() => {
-    profileId && initPage(profileId);
-  }, [profileId]);
-
-  // 清除定时器
-  useEffect(() => {
-    return () => {
-      stopTimer();
-    };
-  }, []);
   return (
-    <View className={`flex flex-col`}>
-      <View className="flex-1">
-        {/* <VoicePlayerBar
-          ref={playerRef}
-          voiceName={voiceName}
-          voiceNameText={createVoiceNameText(voiceName)}
-        /> */}
-
-        {renderCloneList()}
-      </View>
-
-      <PopupContainer show={show} setShow={setShow}>
-        {(popupType == "clone" || popupType == "reclone") && (
-          <PopupRecorder
-            onRecordEnd={onRecordEnd}
-            show={show}
-            setShow={setShow}
-            setCloneStatus={(status) => handleCloneStatus(status)}
-            voiceName={
-              popupType == "reclone" ? voiceList[voiceIndex].voiceName! : ""
-            }
-          ></PopupRecorder>
-        )}
-        {popupType == "try" && (
-          <PopupTryout
-            show={show}
-            onSure={handleSureAction}
-            onReclone={handleRecloneAction}
-            voiceName={voiceList[voiceIndex].voiceName!}
-            showName={getCloneVoiceIdentifier(voiceIndex + 1)}
-          ></PopupTryout>
-        )}
-      </PopupContainer>
-
-      <View
-        className={style.addButton}
-        onClick={() => {
-          setPopupType("clone");
-          setShow(true);
-        }}
-      >
-        <View className="button-rounded-big font-medium">
-          <View>添加克隆声音</View>
-          <View className="font-normal text-12 leading-0">
-            (剩{calcRemainCloneNum()}次)
-          </View>
+    <PageCustom>
+      <NavBarNormal backText="声音"></NavBarNormal>
+      <View className={style.container}>
+        <View className={style.playContainer}>
+          <VoicePlayerBar
+            ref={playerRef}
+            voiceItem={sysVoice}
+            voiceName={voiceName}
+            voiceNameText={voiceIdx == -1 ? "" : voiceAlias}
+          />
+        </View>
+        <View className={style.voiceTab}>
+          <WemetaTabs
+            full
+            list={tabList}
+            current="1"
+            tabStyle="outline"
+          ></WemetaTabs>
         </View>
       </View>
-    </View>
+    </PageCustom>
   );
 };
+
+export default VoiceTabs;