refactor: 重构项目目录结构并优化前后端代码
将项目结构调整为前后端分离的清晰目录布局,优化了代码组织和可维护性。主要变更包括: - 将前端代码移至frontend目录 - 将后端代码移至backend目录 - 更新README.md以反映新的目录结构 - 优化.gitignore文件 - 修复部分文件路径问题
This commit is contained in:
11
frontend/hwICSUiSdk/HwICSUiSdk.css
Normal file
11
frontend/hwICSUiSdk/HwICSUiSdk.css
Normal file
File diff suppressed because one or more lines are too long
478
frontend/hwICSUiSdk/HwICSUiSdk.d.ts
vendored
Normal file
478
frontend/hwICSUiSdk/HwICSUiSdk.d.ts
vendored
Normal file
@@ -0,0 +1,478 @@
|
||||
export declare class HwICSUiSdk {
|
||||
static version: string;
|
||||
static buildTime: string;
|
||||
static checkBrowserSupport(strictCheckBrowser?: boolean): Promise<boolean>;
|
||||
static checkTransparentBgSupport(): Promise<boolean>;
|
||||
static create(param: CreateParam): Promise<void>;
|
||||
static setConfig(config: ConfigMap): Promise<void>;
|
||||
static setLogLevel(logLevel: LogLevel): void;
|
||||
static addEventListeners(eventMap: EventMap): void;
|
||||
static destroy(): Promise<void>;
|
||||
static getJobInfo(): Promise<JobInfo>;
|
||||
static startSpeak(): Promise<UISdkResult>;
|
||||
static stopSpeak(): Promise<UISdkResult>;
|
||||
static startUserSpeak(param?: StartUserSpeakParam): Promise<UISdkResult>;
|
||||
static stopUserSpeak(): Promise<UISdkResult>;
|
||||
static unmuteRemoteAudio(): Promise<boolean>;
|
||||
static muteRemoteAudio(): Promise<boolean>;
|
||||
static interruptSpeaking(): Promise<UISdkResult>;
|
||||
static sendTextQuestion(param: TextQuestionParam): Promise<TextQuestionResult>;
|
||||
static startChat(param: {
|
||||
interactionMode: InteractionMode;
|
||||
wakeupType?: WakeupType;
|
||||
preCheckPermissions?: boolean;
|
||||
}): Promise<ChatResult>;
|
||||
static stopChat(): Promise<ChatResult>;
|
||||
static activeInteractionMode(): Promise<InteractionModeResult>;
|
||||
static interactionModeSwitch(param: {
|
||||
interactionMode: InteractionMode;
|
||||
}): Promise<InteractionModeResult>;
|
||||
static getLanguageInfo(): LanguageInfo;
|
||||
static changeLanguage(param: {
|
||||
langCode: LanguageCode;
|
||||
}): Promise<UISdkResult>;
|
||||
static sendDrivenText(param: TextDrivenParam): Promise<ChatResult>;
|
||||
static startLocalWakeupRecord(): Promise<void>;
|
||||
static stopLocalWakeupRecord(): Promise<void>;
|
||||
static initResourcePath(param: ResourcePath): Promise<void>;
|
||||
static setThirdPartyConfig(config: ThirdPartyConfig): Promise<UISdkResult>;
|
||||
static sendCustomLocalAudio(audioData: AudioData): Promise<void>;
|
||||
static playRemoteVideo(): Promise<UISdkResult | void>;
|
||||
static startStreamRecord(): Promise<void>;
|
||||
static stopStreamRecord(): Promise<void>;
|
||||
static subscribe(): Promise<void>;
|
||||
static unSubscribe(): Promise<void>;
|
||||
}
|
||||
export declare enum RetCodes {
|
||||
SUCCESS = "MSS.00000000",
|
||||
NORMAL_FAILED = "MSS.00000001",
|
||||
SERVER_ERROR = "MSS.00000002",
|
||||
ERROR_PARAM = "MSS.00000003",
|
||||
AUTH_FAILED = "MSS.00000004",
|
||||
DIALOG_NOT_EXIST = "MSS.47010001",
|
||||
RPC_ACTION_NOT_EXIST = "MSS.47010002",
|
||||
ICS_EXCEPTION = "MSS.47010003",
|
||||
REQUEST_TOO_FREQUENTLY = "MSS.47010004",
|
||||
GET_DAC_ASSERT_FAIL = "MSS.47010005",
|
||||
GET_DHM_STYLE_FAIL = "MSS.47010006",
|
||||
GET_SFS_CACHE_FAIL = "MSS.47010007",
|
||||
TENANT_CLEANUP_JOB_STOPPED = "MSS.47010008",
|
||||
NO_PERMISSION_USE = "MSS.47010009",
|
||||
CHAT_SERVICE_IS_ACTIVED = "MSS.47010010",
|
||||
CHAT_SERVICE_IS_INACTIVED = "MSS.47010011",
|
||||
DIALOG_HAS_FINISH = "MSS.47010012",
|
||||
CHAT_SERVICE_CONCURRENCY_NOT_ENOUGH = "MSS.47010013",
|
||||
DIALOG_STATE_UNAVAILABLE = "MSS.47010014",
|
||||
SEND_EVENT_CALLBACK_FAIL = "MSS.47010015",
|
||||
DIALOG_WAIT_IS_NULL = "MSS.47010016",
|
||||
SKILL_NAME_EXIST = "MSS.47010020",
|
||||
SKILL_IDENTIFY_EXIST = "MSS.47010021",
|
||||
SKILL_NOT_EXIST = "MSS.47010022",
|
||||
SKILL_NO_PERMISSION = "MSS.47010023",
|
||||
INTENT_NAME_EXIST = "MSS.47010024",
|
||||
INTENT_IDENTIFY_CREATE_FAILED = "MSS.47010025",
|
||||
INTENT_COUNT_EXCEED_MAX_VALUE = "MSS.47010026",
|
||||
INTENT_NOT_EXIST = "MSS.47010027",
|
||||
INTENT_NO_PERMISSION = "MSS.47010028",
|
||||
QUESTION_EXIST = "MSS.47010029",
|
||||
QUESTION_NOT_EXIST = "MSS.47010030",
|
||||
QUESTION_NO_PERMISSION = "MSS.47010031",
|
||||
QUESTION_BATCH_LIST_EMPTY = "MSS.47010032",
|
||||
ROBOT_NAME_EXIST = "MSS.47010033",
|
||||
ROBOT_NOT_EXIST = "MSS.47010034",
|
||||
ROBOT_NO_PERMISSION = "MSS.47010035",
|
||||
EXPORT_SKILL_FAILED = "MSS.47010036",
|
||||
START_SMART_CHAT_FAILED = "MSS.47010037",
|
||||
STOP_SMART_CHAT_FAILED = "MSS.47010038",
|
||||
SHOW_SMART_CHAT_FAILED = "MSS.47010039",
|
||||
DIALOG_CONCURRENT_EXCEED_MAX_VALUE = "MSS.47010040",
|
||||
ACTIVE_CODE_AUTH_FAILED = "MSS.47010043",
|
||||
ACTIVE_CODE_AUTH_LOCKED = "MSS.47010044",
|
||||
ACTIVE_CODE_AUTH_EXPIRED = "MSS.47010045",
|
||||
ONCE_CODE_AUTH_FAILED = "MSS.47010046",
|
||||
ONCE_CODE_AUTH_LOCKED = "MSS.47010047",
|
||||
GENERATE_SLIDE_VERIFICATON_FAILED = "MSS.47010048",
|
||||
SLIDE_VERIFICATON_NOT_EXIST = "MSS.47010049",
|
||||
SLIDE_VERIFICATON_VERIFY_FAILED = "MSS.47010050",
|
||||
SLIDE_VERIFICATON_VERIFY_FAILED_AND_EXCEED_MAX_TIMES = "MSS.47010051",
|
||||
DIALOG_NO_PERMISSION = "MSS.47010063",
|
||||
FAILED_TO_QUERY_TENANT_ID = "MSS.47010065",
|
||||
HOT_QUESTION_NOT_EXIST = "MSS.47010066",
|
||||
WS_INTERNAL_ERROR = "MSS.47010100",
|
||||
MESSAGE_PARSE_ERROR = "MSS.47010101",
|
||||
WS_ROBOT_ID_IS_EMPTY = "MSS.47010102",
|
||||
UNKNOWN_ACTION = "MSS.47010103",
|
||||
CONNECT_AIUI_TIMEOUT = "MSS.47010104",
|
||||
CONNECT_APP_TIMEOUT = "MSS.47010104",
|
||||
AUTHTOKEN_INVALID = "MSS.47010105",
|
||||
QUERYPARAM_INVALID = "MSS.47010106",
|
||||
CLIENT_ERROR_MESSAGE = "MSS.47010107",
|
||||
WEBCLIENT_START_CHAT_WORKER_DISCONNECT = "MSS.47010111",
|
||||
CHANNEL_RECORED_NOT_FOUND = "MSS.47010119",
|
||||
INTERACTIVE_NUMBER_IS_INSUFFICIENT = "MSS.47010120",
|
||||
TOKEN_NUMBER_IS_INSUFFICIENT = "MSS.47010121",
|
||||
RPC_CALL_FAILED = "MSS.47010122",
|
||||
AIUI_UNAUTHORIZED = "MSS.47010124",
|
||||
SPARK_UNAUTHORIZED = "MSS.47010125",
|
||||
MOBVOI_QUANTITY_NOT_ENOUGH = "MSS.47010126",
|
||||
MOBVOI_UNAUTHORIZED = "MSS.47010127",
|
||||
MOBVOI_ROLE_ID_IS_INVALID = "MSS.47010128",
|
||||
THIRD_PARTY_LANGUAGE_MODEL_EMPTY = "MSS.47010130",
|
||||
THIRD_PARTY_LANGUAGE_MODEL_URL_EMPTY = "MSS.47010131",
|
||||
THIRD_PARTY_LANGUAGE_MODEL_URL_INVALID = "MSS.47010134",
|
||||
IFLY_APPLICATION_SCENE_CONFIG_ERROR = "MSS.47010136",
|
||||
SIS_AUTHORIZATION_FAILED = "MSS.47010141",
|
||||
CURRENT_LANGUAGE_NOT_SUPPORT_RECOGNITION = "MSS.47010143",
|
||||
ICS_NOT_IN_TEXT_MODE = "MSS.47010144",
|
||||
ICS_TEXT_QUESTION_LENGTH_LIMITED = "MSS.47010145",
|
||||
ICS_TEXT_QUESTION_IS_EMPTY = "MSS.47010146",
|
||||
ICS_TEXT_QUESTION_NOT_IN_WAIT_QUESTION_STATUS = "MSS.47010147",
|
||||
ICS_INVALID_INTERACTION_MODE = "MSS.47010148",
|
||||
MOBVOI_REQUEST_FLOW_LIMITED = "MSS.47010150",
|
||||
UPLOAD_LOG_COUNT_EXCEED_MAX_COUNT = "MSS.47010151",
|
||||
ICS_EXTEND_PARAM_INVALID = "MSS.47010152",
|
||||
PACIFY_WORDS_EXIST = "MSS.47010154",
|
||||
PACIFY_WORDS_COUNT_EXCEED_MAX_VALUE = "MSS.47010155",
|
||||
PACIFY_WORDS_NOT_EXIST = "MSS.47010156",
|
||||
PACIFY_WORDS_NO_PERMISSION = "MSS.47010157",
|
||||
PACIFY_WORDS_INTENT_NOT_SUPPORT = "MSS.47010158",
|
||||
ICS_ROBOT_ROOM_ID_EMPTY = "MSS.47010163",
|
||||
SINGLE_TENANT_WEBSOCKET_LINKS_EXCEEDS = "MSS.47015005",
|
||||
WEBSOCKET_LINKS_EXCEEDS = "MSS.47015006",
|
||||
ICS_UNMATCHED_APP_TYPE_AIUI = "MSS.47015008",
|
||||
ICS_UNMATCHED_APP_TYPE_SPARK = "MSS.47015009",
|
||||
ICS_THIRD_PARTY_DRIVER_NOT_ALLOWED_WEB_START_CHAT = "MSS.47015010",
|
||||
ICS_MEDIA_SERVICE_ADDR_IS_EMPTY = "MSS.47015011",
|
||||
ICS_MEDIA_NEGOTIATE_FAILED = "MSS.47015012",
|
||||
ICS_SIS_FROZEN_ERROR = "MSS.47015015",
|
||||
ICS_THIRD_PARTY_DRIVE_NOT_ALLOWED_THIS_WEB_MESSAGE = "MSS.47015017",
|
||||
ICS_MOBVOI_ASR_UNAUTHORIZED = "MSS.47015018",
|
||||
ICS_ASR_WAIT_VOICE_TIME_OUT = "MSS.47015019",
|
||||
ICS_SIS_SERVICE_NOT_SUBSCRIBE = "MSS.47015028",
|
||||
ICS_AUDIO_REPORT_TASK_EXIST = "MSS.47015029",
|
||||
ICS_CREATE_AUDIO_RECORD_TASK_FAILED = "MSS.47015030",
|
||||
ICS_AUDIO_RECORD_NOT_ENABLE = "MSS.47015031"
|
||||
}
|
||||
export declare enum MPSRetCodes {
|
||||
SUCCESS = "00000000",
|
||||
FAILED = "00000001",
|
||||
INTERNAL_ERROR = "00000002",
|
||||
INVALID_PARAMETER = "00000003",
|
||||
ILLEGAL_ACCESS = "00000004",
|
||||
MPS_NAME_INCLUDE_INVALID_SYMBOL = "20010001",
|
||||
NO_MATCH_TEMPLATE = "20010003",
|
||||
REFRESH_ASSET_FAILED = "20010010",
|
||||
NO_ENOUGH_RESOURCE = "20010035",
|
||||
NATS_PUBLISH_FAILURE = "20010040",
|
||||
NATS_SUBSCRIBE_FAILURE = "20010041",
|
||||
NATS_DELETE_SUBJECT_FAILURE = "20010042",
|
||||
NATS_INTERNAL_ERROR = "20010043",
|
||||
DIGITAL_HUMAN_NOT_AVAILABLE = "20010044",
|
||||
MPS_ROOM_INFO_NOT_FOUND = "20010045",
|
||||
ROOM_ID_JOB_EXIST = "20010046",
|
||||
ASSET_NOT_EXIST = "20010047",
|
||||
ASSET_INFO_INVALID = "20010050",
|
||||
INVALID_COMMAND = "20010053",
|
||||
QUOTA_INSUFFICIENT = "20010056",
|
||||
REQUEST_TMS_UNAVAILABLE = "20010060",
|
||||
LIVE_QUOTA_INSUFFICIENT = "20010065",
|
||||
MPS_CHANGE_FINISHED_JOB_STATE_FAILED = "20010066",
|
||||
AUDIO_NOT_FOUND_URL = "20010067",
|
||||
MPS_FIELD_VERIFY_FAIL = "20010070",
|
||||
MPS_ASSET_ID_UNAVAILABLE = "20010071",
|
||||
MPS_JOB_NOT_EXISTED = "20010074",
|
||||
CHAT_QUOTA_INSUFFICIENT = "20010075",
|
||||
MPS_TASK_EXPIRED = "20010076",
|
||||
CONVERSATION_RESOURCE_FULL = "20010089",
|
||||
TONE_RESOURCE_ERROR = "20010131",
|
||||
CHAT_JOBS_NUM_IS_OVER_CONCURRENCY = "20010135",
|
||||
CHAT_SUM_CONCURRENCY_OR_JOBS_NUM_IS_OVER_RESOURCES_NUM = "20010139",
|
||||
MPS_ASSET_ID_INVALID = "20010140",
|
||||
MPS_JOB_HEARTBEAT_FAILED = "20010146",
|
||||
MPS_ROOM_NAME_DUPLICATE = "20010148"
|
||||
}
|
||||
export declare enum WorkerRetCodes {
|
||||
DHICIS_ERROR = "47000001",
|
||||
DHICIS_INFER_SERVICE_ERROR = "47000002",
|
||||
DHICIS_TASK_READY_ERROR = "47000003",
|
||||
DHICIS_CREATE_INFER_MODEL_ERROR = "47000004",
|
||||
DHICIS_WORKER_JOIN_ROOM_FAIL = "47000005",
|
||||
DHICIS_MANAGER_SERVICE_ERROR = "47000006",
|
||||
DHICIS_JOB_STOP_BY_COMMAND = "47000007",
|
||||
DHICIS_FSM_STATE_TIME_OUT = "47000009",
|
||||
DHICIS_ICS_ERROR = "47000010",
|
||||
DHICIS_RTSA_ROOM_EMPTY = "47000011",
|
||||
DHICIS_RTSA_REPORT_LEAVE_ROOM = "47000012",
|
||||
DHICIS_MODEL_FILE_DOWN_FAIL = "47000013",
|
||||
DHICIS_BACKGROUND_FILE_DOWN_FAIL = "47000014",
|
||||
DHICIS_WORKER_JOIN_ROOM_TIMEOUT = "47000015",
|
||||
DHICIS_JOIN_ROOM_TASK_FAIL = "47000016",
|
||||
DHICIS_TTS_PREHEAT_TASK_FAIL = "47000017",
|
||||
DHICIS_DATA_PACKAGE_IS_NOT_ZIP = "47000018",
|
||||
DHICIS_TOO_MANY_ENTRIES_IN_PACKAGE = "47000019",
|
||||
DHICIS_PACKAGE_TOO_LARGE = "47000020",
|
||||
DHICIS_FILE_IN_PACKAGE_TOO_LARGE = "47000021",
|
||||
DHICIS_UNEXPECTED_DIRECTORY_IN_PACKAGE = "47000022",
|
||||
DHICIS_PACKAGE_ENTRY_ILLEGAL_NAME = "47000023",
|
||||
DHICIS_FILE_NAME_INVALID = "47000024",
|
||||
DHICIS_JOB_INFO_PARA_INVALID = "47000025",
|
||||
DHICIS_FSM_STATE_ERROR = "47000027",
|
||||
DHICIS_TTS_CONVERT_FAIL = "47000029",
|
||||
DHICIS_NO_IDLE_WORK = "47002000",
|
||||
DHICIS_NO_RESOURCE = "47002001",
|
||||
DHICIS_RESOURCE_LIMITED = "47002002"
|
||||
}
|
||||
export declare enum WBWRetCodes {
|
||||
WBW_AGENT_ROLE_NOT_EXISTED = "47050001",
|
||||
WBW_LLM_NOT_EXISTED = "47050002",
|
||||
WBW_LLM_INVALID_MODEL = "47050003",
|
||||
WBW_LLM_ACCESS_FAILED = "47050004"
|
||||
}
|
||||
export declare enum WBMRetCodes {
|
||||
WBM_LLM_CONFIG_NOT_EXIST = "47030103",
|
||||
WBM_LLM_CONFIG_NO_PERMISSION = "47030104",
|
||||
WBM_PLUGIN_CONFIG_NOT_EXIST = "47030114",
|
||||
WBM_PLUGIN_CONFIG_NO_PERMISSION = "47030115"
|
||||
}
|
||||
export declare enum WBCRetCodes {
|
||||
WBC_ROLE_NOT_EXIST = "47040041",
|
||||
WBC_CONFIG_NO_PERMISSION = "47040042"
|
||||
}
|
||||
export declare enum TTSCRetCodes {
|
||||
TTS_ACCESS_CONFIG_ERROR = "20050038",
|
||||
TTS_INSUFFICIENT_ACCOUNT_BALANCE = "20050039",
|
||||
TTS_INVALID_TEXT = "20050040",
|
||||
TTS_NO_ACCESS_CONFIG = "20050029",
|
||||
TTS_TO_LONG_WORD_TEXT = "20051001",
|
||||
TTS_VOICE_ASSET_UNACTIVATED = "20050042",
|
||||
TTS_UNSUPPORTED_LANGUAGE_TEXT = "20050064"
|
||||
}
|
||||
export declare enum SDKErrorCode {
|
||||
MPS_JOB_STATE_NULL = "999000001",
|
||||
MPS_JOB_STATE_ERROR = "999000002",
|
||||
RTC_PROMISE_ERROR = "999100001",
|
||||
RTC_CATCH_ERROR = "999100002",
|
||||
RTC_KICK_OUT = "999100003",
|
||||
RTC_AUDIO_DEVICE_ERROR = "999100004",
|
||||
RTC_PEER_LEAVE = "999100005",
|
||||
RTC_KICK_ROOM = "999100006",
|
||||
RTC_NOT_INIT = "999100007",
|
||||
RTC_JOIN_TIMEOUT = "999100008",
|
||||
WEBSOCKET_CONNECT_ERROR = "999200001",
|
||||
WEBSOCKET_RESPONSE_TIMEOUT = "999200002",
|
||||
WEBCLIENT_WEBSOCKET_CHANNEL_ABNORMAL = "999200003",
|
||||
WEBSOCKET_NETWORK_ERROR = "999200004",
|
||||
WEBSOCKET_KICK_OUT = "999200005",
|
||||
WEBSOCKET_CONNECT_TIMEOUT = "999200006",
|
||||
UI_NO_PARENT = "999300001",
|
||||
UI_PARAM_ERROR = "999300002",
|
||||
UI_LANGUAGE_LIST_EMPTY = "999300003",
|
||||
UI_LANGUAGE_CODE_NOT_SUPPORT = "999300004",
|
||||
METHOD_PROHIBITED_EXECUTION = "999400001",
|
||||
METHOD_NOT_ALLOW_NOT_AUTH = "999400002",
|
||||
WORKER_JOB_STOP_BY_MUTE_OVERTIME = "999400003",
|
||||
METHOD_REPEAT_CALLS = "999400004",
|
||||
JOB_NOT_READY = "999400005"
|
||||
}
|
||||
export declare enum ICSEventKey {
|
||||
ERROR = "error",
|
||||
JOB_INFO_CHANGE = "jobInfoChange",
|
||||
SPEAKING_START = "speakingStart",
|
||||
SPEAKING_STOP = "speakingStop",
|
||||
SPEECH_RECOGNIZED = "speechRecognized",
|
||||
SEMANTIC_RECOGNIZED = "semanticRecognized",
|
||||
ENTER_SLEEP = "enterSleep",
|
||||
ENTER_ACTIVE = "enterActive",
|
||||
LANGUAGE_INFO_CHANGE = "languageInfoChange",
|
||||
JOB_END_MENTION = "jobEndMention",
|
||||
JOB_END = "jobEnd"
|
||||
}
|
||||
export declare enum WakeupType {
|
||||
VOICE = "VOICE",
|
||||
RADAR = "RADAR",
|
||||
INFRARED = "INFRARED",
|
||||
CAMERA = "CAMERA"
|
||||
}
|
||||
export declare enum LanguageCode {
|
||||
ZH_CN = "zh_CN",
|
||||
EN_US = "en_US"
|
||||
}
|
||||
export declare enum VoiceLanguageCode {
|
||||
CN = "CN",
|
||||
EN = "EN"
|
||||
}
|
||||
export declare enum ConfigKey {
|
||||
ENABLE_CAPTION = "enableCaption",
|
||||
ENABLE_CHAT_BTN = "enableChatBtn",
|
||||
ENABLE_HOT_ISSUES = "enableHotIssues",
|
||||
ENABLE_BUSINESS_TRACK = "enableBusinessTrack",
|
||||
ENABLE_WEAK_ERROR_INFO = "enableWeakErrorInfo",
|
||||
ENABLE_JOB_CACHE = "enableJobCache",
|
||||
USE_DEFAULT_BACKGROUND = "useDefaultBackground",
|
||||
ENABLE_NETWORK_CHECK = "enableNetworkCheck",
|
||||
ENABLE_DEVICE_SETTING = "enableDeviceSetting",
|
||||
SHOW_LANGUAGE_UI = "showLanguageUI",
|
||||
DESTROY_BEFORE_CLOSE = "destroyBeforeClose",
|
||||
ENABLE_LOCAL_WAKEUP = "enableLocalWakeup",
|
||||
ENABLE_VAD_INTERRUPT = "enableVadInterrupt",
|
||||
FIRST_CREATE_LOCAL_STREAM = "firstCreateLocalStream",
|
||||
ENABLE_STORAGE_CACHE = "enableStorageCache",
|
||||
ENABLE_MEDIA_VIEWER = "enableMediaViewer",
|
||||
DEFAULT_MUTE_REMOTE_AUDIO = "defaultMuteRemoteAudio",
|
||||
AUTO_PLAY_REMOTE_VIDEO = "autoPlayRemoteVideo",
|
||||
ENABLE_COLLECT_AUDIO_DEMAND = "enableCollectAudioDemand",
|
||||
ENABLE_ORIGIN_CUT_ALGORITHM = "enableOriginCutAlgorithm",
|
||||
ENABLE_VERBATIM = "enableVerbatim",
|
||||
USE_WORKER_WAKEUP = "useWorkerWakeup",
|
||||
PRE_CHECK_PERMISSIONS = "preCheckPermissions"
|
||||
}
|
||||
export declare enum SparkRTCErrorCode {
|
||||
RTC_ERR_CODE_SUCCESS = "0",
|
||||
RTC_ERR_CODE_RTC_SDK_ERROR = "90000001",
|
||||
RTC_ERR_CODE_WAIT_RSP_TIMEOUT = "90000004",
|
||||
RTC_ERR_CODE_INVALID_PARAMETER = "90000005",
|
||||
RTC_ERR_CODE_INVALID_OPERATION = "90100001",
|
||||
RTC_ERR_CODE_NOT_SUPPORT_MEDIA_DEVICES = "90100002",
|
||||
RTC_ERR_CODE_NO_AVAILABLE_DEVICES = "90100003",
|
||||
RTC_ERR_CODE_NO_AVAILABLE_VIDEO_INPUT_DEVICES = "90100004",
|
||||
RTC_ERR_CODE_NO_AVAILABLE_AUDIO_INPUT_DEVICES = "90100005",
|
||||
RTC_ERR_CODE_NO_AVAILABLE_AUDIO_OUTPUT_DEVICES = "90100006",
|
||||
RTC_ERR_CODE_STATUS_ERROR = "90100007",
|
||||
RTC_ERR_CODE_WEBSOCKET_NOT_CONNECTED = "90100008",
|
||||
RTC_ERR_CODE_WAIT_CONFIG_FAIL = "90100009",
|
||||
RTC_ERR_CODE_PUBLISH_RESPONSE_FAIL = "90100010",
|
||||
RTC_ERR_CODE_REGION_NOT_COVERED = "90100011",
|
||||
RTC_ERR_CODE_WEBSOCKET_CONNECT_TIMEOUT = "90100012",
|
||||
RTC_ERR_CODE_WEBSOCKET_RECONNECT_TIMEOUT = "90100013",
|
||||
RTC_ERR_CODE_WEBSOCKET_NOT_OPEN = "90100014",
|
||||
RTC_ERR_CODE_WEBSOCKET_INTERRUPTED = "90100015",
|
||||
RTC_ERR_CODE_WEBSOCKET_CONNECT_ERROR = "90100016",
|
||||
RTC_ERR_CODE_CAPTURE_PERMISSION_DENIED = "90100017",
|
||||
RTC_ERR_CODE_CAPTURE_OVER_CONSTRAINED = "90100018",
|
||||
RTC_ERR_CODE_CAPTURE_DEVICE_NOT_FOUND = "90100019",
|
||||
RTC_ERR_CODE_CAPTURE_DEVICE_NOT_READABLE = "90100020",
|
||||
RTC_ERR_CODE_PLAY_NOT_ALLOW = "90100021",
|
||||
RTC_ERR_CODE_ROLE_NO_PERMISSION = "90100022",
|
||||
RTC_ERR_CODE_ANSWER_SDP_INVALID = "90100023",
|
||||
RTC_ERR_CODE_MEDIA_UPSTREAM_UNSUPPORTED = "90100024",
|
||||
RTC_ERR_CODE_WEBRTC_UNSUPPORTED = "90100025",
|
||||
RTC_ERR_CODE_MEDIA_NETWORK_ERROR = "90100026",
|
||||
RTC_ERR_CODE_CLIENT_RELAY_ROOM_OVER_MAXNUM = "90100027",
|
||||
RTC_ERR_CODE_CLIENT_RELAY_JOINER_OVER_MAXNUM = "90100028",
|
||||
RTC_ERR_CODE_ROOM_STREAM_STATUS_PAUSED = "90100029",
|
||||
RTC_ERR_CODE_SIGNATURE_EXPIRED = "90100030",
|
||||
RTC_ERR_CODE_SIGNATURE_INVALID = "90100031",
|
||||
RTC_ERR_CODE_WINDOW_OR_NAVIGATOR_UNSUPPORTED = "90100032",
|
||||
RTC_ERR_CODE_CHROME_VERSION_UNSUPPORTED = "90100033",
|
||||
RTC_ERR_CODE_WEBSOCKET_UNSUPPORTED = "90100034",
|
||||
RTC_ERR_CODE_WEBRTC_AVC_ENCODE_UNSUPPORTED = "90100035",
|
||||
RTC_ERR_CODE_WEBRTC_AVC_DECODE_UNSUPPORTED = "90100036",
|
||||
RTC_ERR_CODE_WEBRTC_WECHAT_UNSUPPORTED = "90100037",
|
||||
RTC_ERR_CODE_AUDIO_ERROR = "90100038",
|
||||
RTC_ERR_CODE_RTC_ACS = "90100100",
|
||||
RTC_ERR_CODE_RTC_CONTROL_ERROR = "90100200",
|
||||
RTC_ERR_CODE_SFU_ERROR = "90100600",
|
||||
RTC_WEBSOCKET_ERR_NEED_REJOIN = "4005"
|
||||
}
|
||||
export declare enum InteractionMode {
|
||||
AUDIO = "AUDIO",
|
||||
TEXT = "TEXT"
|
||||
}
|
||||
export type HttpErrorCode = MPSRetCodes | RetCodes | WorkerRetCodes | WBWRetCodes | SDKErrorCode | TTSCRetCodes;
|
||||
export declare enum MediaType {
|
||||
VIDEO = "VIDEO",
|
||||
IMAGE = "IMAGE"
|
||||
}
|
||||
export type EventMap = {
|
||||
[key in ICSEventKey]?: Function;
|
||||
};
|
||||
export type ConfigMap = {
|
||||
[key in ConfigKey]?: boolean;
|
||||
};
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
|
||||
export interface InnerInitParam {
|
||||
robotId?: string;
|
||||
serverAddress: string;
|
||||
taskUrl?: string;
|
||||
config?: ConfigMap;
|
||||
containerId: string;
|
||||
logLevel?: LogLevel;
|
||||
eventListeners?: EventMap;
|
||||
extendParamStr?: string;
|
||||
}
|
||||
export interface CreateParam extends InnerInitParam {
|
||||
onceCode?: string;
|
||||
}
|
||||
export interface UISdkResult {
|
||||
result: boolean;
|
||||
errorCode?: SparkRTCErrorCode | SDKErrorCode | HttpErrorCode;
|
||||
errorMsg?: string;
|
||||
}
|
||||
export interface InteractionModeResult extends UISdkResult {
|
||||
interactionMode?: InteractionMode;
|
||||
}
|
||||
export interface TextQuestionResult extends UISdkResult {
|
||||
chatId: string;
|
||||
}
|
||||
export interface ChatResult extends UISdkResult {
|
||||
chatId: string;
|
||||
sessionId?: string;
|
||||
}
|
||||
export interface JobInfo {
|
||||
jobId: string;
|
||||
websocketAddr: string;
|
||||
jobInfo?: SmartChat | SmartChatState | null;
|
||||
}
|
||||
export interface LanguageInfo extends UISdkResult {
|
||||
languageList?: Array<VoiceLanguageItem>;
|
||||
currentLanguage?: LanguageCode;
|
||||
}
|
||||
export interface JobEndMentionInfo {
|
||||
jobId: string;
|
||||
restTime: number;
|
||||
}
|
||||
export interface JobEndInfo {
|
||||
jobId: string;
|
||||
reason: string;
|
||||
}
|
||||
export interface TextDrivenParam {
|
||||
text: string;
|
||||
isLast?: boolean;
|
||||
}
|
||||
export interface TextQuestionParam {
|
||||
text: string;
|
||||
questionParam?: string;
|
||||
}
|
||||
export interface ResourcePath {
|
||||
wasmPath: string;
|
||||
dataPath: string;
|
||||
initModel?: boolean;
|
||||
useWorker?: boolean;
|
||||
}
|
||||
export interface UHanConfig {
|
||||
origin?: string;
|
||||
enable?: boolean;
|
||||
sleepDelay?: number;
|
||||
showVideoUi?: boolean;
|
||||
host?: string;
|
||||
}
|
||||
export interface CustomRecordConfig {
|
||||
enable?: boolean;
|
||||
}
|
||||
export interface ThirdPartyConfig {
|
||||
uHan?: UHanConfig;
|
||||
uHanV2?: UHanConfig;
|
||||
customRecord?: CustomRecordConfig;
|
||||
}
|
||||
export interface AudioData {
|
||||
base64Data?: string;
|
||||
uint8Arr?: Uint8Array;
|
||||
time?: number;
|
||||
}
|
||||
export interface VoiceLanguageItem {
|
||||
language: LanguageCode;
|
||||
language_desc: string;
|
||||
}
|
||||
export interface StartUserSpeakParam {
|
||||
preCheckPermissions?: boolean;
|
||||
}
|
||||
1
frontend/hwICSUiSdk/HwICSUiSdk.esm.js
Normal file
1
frontend/hwICSUiSdk/HwICSUiSdk.esm.js
Normal file
File diff suppressed because one or more lines are too long
1
frontend/hwICSUiSdk/HwICSUiSdk.js
Normal file
1
frontend/hwICSUiSdk/HwICSUiSdk.js
Normal file
File diff suppressed because one or more lines are too long
BIN
frontend/hwICSUiSdk/images/aiChatImg.png
Normal file
BIN
frontend/hwICSUiSdk/images/aiChatImg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
BIN
frontend/hwICSUiSdk/images/bg.png
Normal file
BIN
frontend/hwICSUiSdk/images/bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 808 KiB |
BIN
frontend/hwICSUiSdk/images/bg_mobile.png
Normal file
BIN
frontend/hwICSUiSdk/images/bg_mobile.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 351 KiB |
1
frontend/hwICSUiSdk/modelData.js
Normal file
1
frontend/hwICSUiSdk/modelData.js
Normal file
File diff suppressed because one or more lines are too long
6
frontend/hwICSUiSdk/package.json
Normal file
6
frontend/hwICSUiSdk/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "HwICSUiSdk",
|
||||
"version": "5.0.4",
|
||||
"main": "HwICSUiSdk.js",
|
||||
"types": "HwICSUiSdk.d.ts"
|
||||
}
|
||||
1
frontend/hwICSUiSdk/wasmData.js
Normal file
1
frontend/hwICSUiSdk/wasmData.js
Normal file
File diff suppressed because one or more lines are too long
146
frontend/index.html
Normal file
146
frontend/index.html
Normal file
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>华为交互数字人 - HwICSUiSdk 演示</title>
|
||||
<link rel="stylesheet" href="./styles/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container">
|
||||
<!-- 头部 -->
|
||||
<header class="header">
|
||||
<div class="logo">
|
||||
<span class="logo-icon">🎭</span>
|
||||
<h1>华为交互数字人</h1>
|
||||
</div>
|
||||
<div class="status-bar">
|
||||
<span id="connectionStatus" class="status disconnected">未连接</span>
|
||||
<span id="sdkVersion" class="version"></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main-content">
|
||||
<!-- 配置面板 -->
|
||||
<section class="config-panel">
|
||||
<h2>📋 连接配置</h2>
|
||||
|
||||
<!-- 基本配置 -->
|
||||
<div class="config-section">
|
||||
<h3>基本配置</h3>
|
||||
<div class="form-group">
|
||||
<label for="serverAddress">服务器地址</label>
|
||||
<input type="text" id="serverAddress" placeholder="请输入服务器地址" value="metastudio-api.cn-north-4.myhuaweicloud.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="robotId">机器人ID</label>
|
||||
<input type="text" id="robotId" placeholder="请输入机器人ID" value="2c9084bf9c9cc1c0019db2c4ca2a5c89">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 授权码配置 -->
|
||||
<div class="config-section">
|
||||
<h3>授权码</h3>
|
||||
<div class="form-group">
|
||||
<label for="onceCode">一次性授权码</label>
|
||||
<div class="input-with-button">
|
||||
<input type="text" id="onceCode" placeholder="请输入一次性授权码或点击获取" value="" readonly>
|
||||
<button id="getOnceCodeBtn" class="btn btn-secondary">🔑 获取授权码</button>
|
||||
</div>
|
||||
<small class="field-hint">有效期5分钟,只能使用一次</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions main-actions">
|
||||
<button id="initBtn" class="btn btn-primary">初始化 SDK</button>
|
||||
<button id="destroyBtn" class="btn btn-danger" disabled>销毁 SDK</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 数字人显示区 -->
|
||||
<section class="digital-human-container">
|
||||
<h2>🎬 数字人显示</h2>
|
||||
<div id="digitalHumanBox" class="digital-human-box">
|
||||
<div class="placeholder">
|
||||
<span class="placeholder-icon">🤖</span>
|
||||
<p>请先初始化 SDK 并创建数字人</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<div class="control-panel">
|
||||
<div class="control-group">
|
||||
<h3>对话控制</h3>
|
||||
<div class="button-row">
|
||||
<button id="startChatBtn" class="btn btn-success" disabled>
|
||||
<span class="icon">▶️</span> 开始对话
|
||||
</button>
|
||||
<button id="stopChatBtn" class="btn btn-warning" disabled>
|
||||
<span class="icon">⏹️</span> 停止对话
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<h3>语音交互</h3>
|
||||
<div class="button-row">
|
||||
<button id="startSpeakBtn" class="btn btn-info" disabled>
|
||||
<span class="icon">🎤</span> 开始说话
|
||||
</button>
|
||||
<button id="stopSpeakBtn" class="btn btn-secondary" disabled>
|
||||
<span class="icon">🛑</span> 停止说话
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<h3>文本交互</h3>
|
||||
<div class="text-input-row">
|
||||
<input type="text" id="textInput" placeholder="输入文本消息..." disabled>
|
||||
<button id="sendTextBtn" class="btn btn-primary" disabled>
|
||||
<span class="icon">📤</span> 发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<h3>其他控制</h3>
|
||||
<div class="button-row">
|
||||
<button id="interruptBtn" class="btn btn-warning" disabled>
|
||||
<span class="icon">⚡</span> 打断说话
|
||||
</button>
|
||||
<button id="toggleAudioBtn" class="btn btn-secondary" disabled>
|
||||
<span class="icon">🔊</span> 静音/取消静音
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 日志面板 -->
|
||||
<section class="log-panel">
|
||||
<h2>📜 事件日志</h2>
|
||||
<div class="log-toolbar">
|
||||
<button id="clearLogBtn" class="btn btn-small">清空日志</button>
|
||||
<select id="logLevel">
|
||||
<option value="debug">Debug</option>
|
||||
<option value="info" selected>Info</option>
|
||||
<option value="warn">Warn</option>
|
||||
<option value="error">Error</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="logContainer" class="log-container"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 底部 -->
|
||||
<footer class="footer">
|
||||
<p>Powered by HwICSUiSdk | 华为交互数字人解决方案</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- SDK 引入 -->
|
||||
<script type="module" src="./scripts/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
18
frontend/scripts/api.js
Normal file
18
frontend/scripts/api.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const API_BASE = 'http://localhost:3001';
|
||||
|
||||
export async function fetchOnceCode() {
|
||||
const response = await fetch(`${API_BASE}/api/once-code`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error_msg || '获取授权码失败');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.once_code;
|
||||
}
|
||||
494
frontend/scripts/main.js
Normal file
494
frontend/scripts/main.js
Normal file
@@ -0,0 +1,494 @@
|
||||
/**
|
||||
* 华为交互数字人 - HwICSUiSdk 集成示例
|
||||
*
|
||||
* 功能说明:
|
||||
* 1. SDK 初始化和销毁
|
||||
* 2. 数字人创建和显示
|
||||
* 3. 语音/文本对话交互
|
||||
* 4. 事件监听和日志记录
|
||||
*/
|
||||
|
||||
// 导入 HwICSUiSdk
|
||||
import { HwICSUiSdk, ICSEventKey, InteractionMode, ConfigKey } from '../hwICSUiSdk/HwICSUiSdk.esm.js';
|
||||
import { fetchOnceCode } from './api.js';
|
||||
|
||||
// ===== 全局状态 =====
|
||||
const state = {
|
||||
isInitialized: false,
|
||||
isChatting: false,
|
||||
isSpeaking: false,
|
||||
isMuted: false,
|
||||
jobInfo: null,
|
||||
currentInteractionMode: InteractionMode.AUDIO
|
||||
};
|
||||
|
||||
// ===== DOM 元素引用 =====
|
||||
const elements = {
|
||||
// 配置输入
|
||||
serverAddress: document.getElementById('serverAddress'),
|
||||
robotId: document.getElementById('robotId'),
|
||||
onceCode: document.getElementById('onceCode'),
|
||||
|
||||
// 按钮
|
||||
initBtn: document.getElementById('initBtn'),
|
||||
destroyBtn: document.getElementById('destroyBtn'),
|
||||
getOnceCodeBtn: document.getElementById('getOnceCodeBtn'),
|
||||
startChatBtn: document.getElementById('startChatBtn'),
|
||||
stopChatBtn: document.getElementById('stopChatBtn'),
|
||||
startSpeakBtn: document.getElementById('startSpeakBtn'),
|
||||
stopSpeakBtn: document.getElementById('stopSpeakBtn'),
|
||||
sendTextBtn: document.getElementById('sendTextBtn'),
|
||||
interruptBtn: document.getElementById('interruptBtn'),
|
||||
toggleAudioBtn: document.getElementById('toggleAudioBtn'),
|
||||
clearLogBtn: document.getElementById('clearLogBtn'),
|
||||
|
||||
// 输入框
|
||||
textInput: document.getElementById('textInput'),
|
||||
logLevel: document.getElementById('logLevel'),
|
||||
|
||||
// 显示区域
|
||||
digitalHumanBox: document.getElementById('digitalHumanBox'),
|
||||
logContainer: document.getElementById('logContainer'),
|
||||
connectionStatus: document.getElementById('connectionStatus'),
|
||||
sdkVersion: document.getElementById('sdkVersion')
|
||||
};
|
||||
|
||||
// ===== 日志工具 =====
|
||||
const logger = {
|
||||
log(level, message, data = null) {
|
||||
const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false });
|
||||
const entry = document.createElement('div');
|
||||
entry.className = 'log-entry';
|
||||
|
||||
const levelClass = level.toLowerCase();
|
||||
const displayMessage = data ? `${message} ${JSON.stringify(data, null, 2)}` : message;
|
||||
|
||||
entry.innerHTML = `
|
||||
<span class="log-time">[${timestamp}]</span>
|
||||
<span class="log-level ${levelClass}">${level}</span>
|
||||
<span class="log-message">${displayMessage}</span>
|
||||
`;
|
||||
|
||||
elements.logContainer.appendChild(entry);
|
||||
elements.logContainer.scrollTop = elements.logContainer.scrollHeight;
|
||||
|
||||
console[level === 'ERROR' ? 'error' : level === 'WARN' ? 'warn' : 'log'](message, data);
|
||||
},
|
||||
|
||||
debug(message, data) { this.log('DEBUG', message, data); },
|
||||
info(message, data) { this.log('INFO', message, data); },
|
||||
warn(message, data) { this.log('WARN', message, data); },
|
||||
error(message, data) { this.log('ERROR', message, data); }
|
||||
};
|
||||
|
||||
// ===== SDK 事件监听配置 =====
|
||||
const eventListeners = {
|
||||
[ICSEventKey.ERROR]: (error) => {
|
||||
logger.error('SDK 错误', error);
|
||||
updateConnectionStatus(false);
|
||||
},
|
||||
|
||||
[ICSEventKey.JOB_INFO_CHANGE]: (jobInfo) => {
|
||||
logger.info('任务信息变更', jobInfo);
|
||||
state.jobInfo = jobInfo;
|
||||
},
|
||||
|
||||
[ICSEventKey.SPEAKING_START]: () => {
|
||||
logger.info('数字人开始说话');
|
||||
state.isSpeaking = true;
|
||||
updateUIState();
|
||||
},
|
||||
|
||||
[ICSEventKey.SPEAKING_STOP]: () => {
|
||||
logger.info('数字人停止说话');
|
||||
state.isSpeaking = false;
|
||||
updateUIState();
|
||||
},
|
||||
|
||||
[ICSEventKey.SPEECH_RECOGNIZED]: (data) => {
|
||||
logger.info('语音识别结果', data);
|
||||
},
|
||||
|
||||
[ICSEventKey.SEMANTIC_RECOGNIZED]: (data) => {
|
||||
logger.info('语义识别结果', data);
|
||||
},
|
||||
|
||||
[ICSEventKey.ENTER_SLEEP]: () => {
|
||||
logger.info('数字人进入休眠状态');
|
||||
},
|
||||
|
||||
[ICSEventKey.ENTER_ACTIVE]: () => {
|
||||
logger.info('数字人进入激活状态');
|
||||
},
|
||||
|
||||
[ICSEventKey.LANGUAGE_INFO_CHANGE]: (langInfo) => {
|
||||
logger.info('语言信息变更', langInfo);
|
||||
},
|
||||
|
||||
[ICSEventKey.JOB_END_MENTION]: (data) => {
|
||||
logger.warn('任务即将结束', data);
|
||||
},
|
||||
|
||||
[ICSEventKey.JOB_END]: (data) => {
|
||||
logger.info('任务已结束', data);
|
||||
state.isChatting = false;
|
||||
state.jobInfo = null;
|
||||
updateConnectionStatus(false);
|
||||
updateUIState();
|
||||
}
|
||||
};
|
||||
|
||||
// ===== UI 状态更新 =====
|
||||
function updateUIState() {
|
||||
const { isInitialized, isChatting, isSpeaking } = state;
|
||||
|
||||
elements.initBtn.disabled = isInitialized;
|
||||
elements.destroyBtn.disabled = !isInitialized;
|
||||
|
||||
elements.startChatBtn.disabled = !isInitialized || isChatting;
|
||||
elements.stopChatBtn.disabled = !isInitialized || !isChatting;
|
||||
|
||||
elements.startSpeakBtn.disabled = !isInitialized || !isChatting || isSpeaking;
|
||||
elements.stopSpeakBtn.disabled = !isInitialized || !isChatting || !isSpeaking;
|
||||
|
||||
elements.textInput.disabled = !isInitialized || !isChatting;
|
||||
elements.sendTextBtn.disabled = !isInitialized || !isChatting;
|
||||
|
||||
elements.interruptBtn.disabled = !isInitialized || !isChatting;
|
||||
elements.toggleAudioBtn.disabled = !isInitialized;
|
||||
|
||||
if (isChatting) {
|
||||
elements.digitalHumanBox.classList.add('active');
|
||||
} else {
|
||||
elements.digitalHumanBox.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
function updateConnectionStatus(connected) {
|
||||
const status = elements.connectionStatus;
|
||||
if (connected) {
|
||||
status.textContent = '已连接';
|
||||
status.className = 'status connected';
|
||||
} else {
|
||||
status.textContent = '未连接';
|
||||
status.className = 'status disconnected';
|
||||
}
|
||||
}
|
||||
|
||||
// ===== SDK 操作 =====
|
||||
|
||||
async function initializeSDK() {
|
||||
try {
|
||||
const serverAddress = elements.serverAddress.value.trim();
|
||||
const robotId = elements.robotId.value.trim();
|
||||
let onceCode = elements.onceCode.value.trim();
|
||||
|
||||
if (!serverAddress) {
|
||||
logger.error('请输入服务器地址');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!onceCode) {
|
||||
logger.info('授权码为空,正在自动获取...');
|
||||
try {
|
||||
onceCode = await fetchOnceCode();
|
||||
elements.onceCode.value = onceCode;
|
||||
logger.info('授权码自动获取成功');
|
||||
} catch (err) {
|
||||
logger.error('授权码自动获取失败', err.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('正在初始化 SDK...');
|
||||
|
||||
const isSupported = await HwICSUiSdk.checkBrowserSupport(true);
|
||||
if (!isSupported) {
|
||||
logger.error('当前浏览器不支持该功能');
|
||||
return;
|
||||
}
|
||||
|
||||
const logLevel = elements.logLevel.value;
|
||||
HwICSUiSdk.setLogLevel(logLevel);
|
||||
HwICSUiSdk.addEventListeners(eventListeners);
|
||||
|
||||
const createParam = {
|
||||
serverAddress,
|
||||
containerId: 'digitalHumanBox',
|
||||
robotId: robotId || undefined,
|
||||
onceCode: onceCode || undefined,
|
||||
config: {
|
||||
[ConfigKey.ENABLE_CAPTION]: true,
|
||||
[ConfigKey.ENABLE_CHAT_BTN]: true,
|
||||
[ConfigKey.ENABLE_HOT_ISSUES]: true,
|
||||
[ConfigKey.ENABLE_WEAK_ERROR_INFO]: true,
|
||||
[ConfigKey.SHOW_LANGUAGE_UI]: true,
|
||||
[ConfigKey.ENABLE_DEVICE_SETTING]: true,
|
||||
[ConfigKey.ENABLE_NETWORK_CHECK]: true,
|
||||
[ConfigKey.AUTO_PLAY_REMOTE_VIDEO]: true,
|
||||
[ConfigKey.DEFAULT_MUTE_REMOTE_AUDIO]: false
|
||||
},
|
||||
logLevel
|
||||
};
|
||||
|
||||
await HwICSUiSdk.create(createParam);
|
||||
|
||||
state.isInitialized = true;
|
||||
updateConnectionStatus(true);
|
||||
updateUIState();
|
||||
|
||||
elements.sdkVersion.textContent = `v${HwICSUiSdk.version}`;
|
||||
|
||||
logger.info('SDK 初始化成功', { version: HwICSUiSdk.version });
|
||||
|
||||
} catch (error) {
|
||||
logger.error('SDK 初始化失败', error);
|
||||
state.isInitialized = false;
|
||||
updateUIState();
|
||||
}
|
||||
}
|
||||
|
||||
async function destroySDK() {
|
||||
try {
|
||||
logger.info('正在销毁 SDK...');
|
||||
|
||||
await HwICSUiSdk.destroy();
|
||||
|
||||
state.isInitialized = false;
|
||||
state.isChatting = false;
|
||||
state.isSpeaking = false;
|
||||
state.jobInfo = null;
|
||||
|
||||
updateConnectionStatus(false);
|
||||
updateUIState();
|
||||
|
||||
elements.digitalHumanBox.innerHTML = `
|
||||
<div class="placeholder">
|
||||
<span class="placeholder-icon">🤖</span>
|
||||
<p>请先初始化 SDK 并创建数字人</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
logger.info('SDK 已销毁');
|
||||
|
||||
} catch (error) {
|
||||
logger.error('销毁 SDK 失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function startChat() {
|
||||
try {
|
||||
logger.info('正在开始对话...');
|
||||
|
||||
const result = await HwICSUiSdk.startChat({
|
||||
interactionMode: InteractionMode.AUDIO,
|
||||
wakeupType: undefined,
|
||||
preCheckPermissions: true
|
||||
});
|
||||
|
||||
if (result.result) {
|
||||
state.isChatting = true;
|
||||
logger.info('对话已开始', { chatId: result.chatId, sessionId: result.sessionId });
|
||||
updateUIState();
|
||||
} else {
|
||||
logger.error('开始对话失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('开始对话出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function stopChat() {
|
||||
try {
|
||||
logger.info('正在停止对话...');
|
||||
|
||||
const result = await HwICSUiSdk.stopChat();
|
||||
|
||||
if (result.result) {
|
||||
state.isChatting = false;
|
||||
state.isSpeaking = false;
|
||||
logger.info('对话已停止');
|
||||
updateUIState();
|
||||
} else {
|
||||
logger.error('停止对话失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('停止对话出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function startUserSpeak() {
|
||||
try {
|
||||
logger.info('开始语音识别...');
|
||||
|
||||
const result = await HwICSUiSdk.startUserSpeak({
|
||||
preCheckPermissions: true
|
||||
});
|
||||
|
||||
if (result.result) {
|
||||
logger.info('语音识别已启动');
|
||||
} else {
|
||||
logger.error('启动语音识别失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('启动语音识别出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function stopUserSpeak() {
|
||||
try {
|
||||
logger.info('停止语音识别...');
|
||||
|
||||
const result = await HwICSUiSdk.stopUserSpeak();
|
||||
|
||||
if (result.result) {
|
||||
logger.info('语音识别已停止');
|
||||
} else {
|
||||
logger.error('停止语音识别失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('停止语音识别出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendTextMessage() {
|
||||
const text = elements.textInput.value.trim();
|
||||
|
||||
if (!text) {
|
||||
logger.warn('请输入文本内容');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('发送文本消息...', { text });
|
||||
|
||||
const result = await HwICSUiSdk.sendTextQuestion({
|
||||
text,
|
||||
questionParam: undefined
|
||||
});
|
||||
|
||||
if (result.result) {
|
||||
logger.info('消息发送成功', { chatId: result.chatId });
|
||||
elements.textInput.value = '';
|
||||
} else {
|
||||
logger.error('发送消息失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('发送消息出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function interruptSpeaking() {
|
||||
try {
|
||||
logger.info('打断说话...');
|
||||
|
||||
const result = await HwICSUiSdk.interruptSpeaking();
|
||||
|
||||
if (result.result) {
|
||||
logger.info('已打断说话');
|
||||
} else {
|
||||
logger.error('打断说话失败', result);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('打断说话出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleAudio() {
|
||||
try {
|
||||
if (state.isMuted) {
|
||||
const result = await HwICSUiSdk.unmuteRemoteAudio();
|
||||
if (result) {
|
||||
state.isMuted = false;
|
||||
elements.toggleAudioBtn.innerHTML = '<span class="icon">🔊</span> 静音';
|
||||
logger.info('已取消静音');
|
||||
}
|
||||
} else {
|
||||
const result = await HwICSUiSdk.muteRemoteAudio();
|
||||
if (result) {
|
||||
state.isMuted = true;
|
||||
elements.toggleAudioBtn.innerHTML = '<span class="icon">🔇</span> 取消静音';
|
||||
logger.info('已静音');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('切换音频状态出错', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetOnceCode() {
|
||||
try {
|
||||
elements.getOnceCodeBtn.disabled = true;
|
||||
elements.getOnceCodeBtn.textContent = '🔄 获取中...';
|
||||
logger.info('正在获取授权码...');
|
||||
|
||||
const onceCode = await fetchOnceCode();
|
||||
elements.onceCode.value = onceCode;
|
||||
logger.info('授权码获取成功');
|
||||
|
||||
} catch (error) {
|
||||
logger.error('获取授权码失败', error.message);
|
||||
} finally {
|
||||
elements.getOnceCodeBtn.disabled = false;
|
||||
elements.getOnceCodeBtn.textContent = '🔑 获取授权码';
|
||||
}
|
||||
}
|
||||
|
||||
function clearLogs() {
|
||||
elements.logContainer.innerHTML = '';
|
||||
logger.info('日志已清空');
|
||||
}
|
||||
|
||||
// ===== 事件绑定 =====
|
||||
function bindEvents() {
|
||||
elements.initBtn.addEventListener('click', initializeSDK);
|
||||
elements.destroyBtn.addEventListener('click', destroySDK);
|
||||
elements.getOnceCodeBtn.addEventListener('click', handleGetOnceCode);
|
||||
|
||||
elements.startChatBtn.addEventListener('click', startChat);
|
||||
elements.stopChatBtn.addEventListener('click', stopChat);
|
||||
|
||||
elements.startSpeakBtn.addEventListener('click', startUserSpeak);
|
||||
elements.stopSpeakBtn.addEventListener('click', stopUserSpeak);
|
||||
|
||||
elements.sendTextBtn.addEventListener('click', sendTextMessage);
|
||||
elements.textInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
sendTextMessage();
|
||||
}
|
||||
});
|
||||
|
||||
elements.interruptBtn.addEventListener('click', interruptSpeaking);
|
||||
elements.toggleAudioBtn.addEventListener('click', toggleAudio);
|
||||
|
||||
elements.clearLogBtn.addEventListener('click', clearLogs);
|
||||
elements.logLevel.addEventListener('change', () => {
|
||||
if (state.isInitialized) {
|
||||
HwICSUiSdk.setLogLevel(elements.logLevel.value);
|
||||
logger.info(`日志级别已切换为: ${elements.logLevel.value}`);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (state.isInitialized) {
|
||||
HwICSUiSdk.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 初始化 =====
|
||||
function init() {
|
||||
logger.info('应用初始化完成');
|
||||
logger.info('HwICSUiSdk 版本', { version: HwICSUiSdk.version, buildTime: HwICSUiSdk.buildTime });
|
||||
|
||||
bindEvents();
|
||||
updateUIState();
|
||||
}
|
||||
|
||||
init();
|
||||
653
frontend/styles/main.css
Normal file
653
frontend/styles/main.css
Normal file
@@ -0,0 +1,653 @@
|
||||
/* ===== CSS 变量定义 ===== */
|
||||
:root {
|
||||
/* 主色调 - 科技蓝 */
|
||||
--primary-color: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--primary-light: #3b82f6;
|
||||
--primary-gradient: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
|
||||
|
||||
/* 辅助色 */
|
||||
--success-color: #10b981;
|
||||
--warning-color: #f59e0b;
|
||||
--danger-color: #ef4444;
|
||||
--info-color: #06b6d4;
|
||||
|
||||
/* 中性色 */
|
||||
--bg-primary: #0f172a;
|
||||
--bg-secondary: #1e293b;
|
||||
--bg-tertiary: #334155;
|
||||
--text-primary: #f8fafc;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
--border-color: #475569;
|
||||
|
||||
/* 阴影 */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
|
||||
--shadow-glow: 0 0 20px rgba(37, 99, 235, 0.3);
|
||||
|
||||
/* 间距 */
|
||||
--spacing-xs: 0.25rem;
|
||||
--spacing-sm: 0.5rem;
|
||||
--spacing-md: 1rem;
|
||||
--spacing-lg: 1.5rem;
|
||||
--spacing-xl: 2rem;
|
||||
|
||||
/* 圆角 */
|
||||
--radius-sm: 0.375rem;
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--radius-xl: 1rem;
|
||||
|
||||
/* 字体 */
|
||||
--font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
/* ===== 基础样式重置 ===== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ===== 应用容器 ===== */
|
||||
.app-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ===== 头部 ===== */
|
||||
.header {
|
||||
background: var(--bg-secondary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: var(--spacing-md) var(--spacing-xl);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 2rem;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 50%, #2563eb 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: var(--spacing-xs) var(--spacing-md);
|
||||
border-radius: var(--radius-full);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.status::before {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.status.connected {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.status.disconnected {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.version {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* ===== 主内容区 ===== */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: var(--spacing-xl);
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr 380px;
|
||||
gap: var(--spacing-xl);
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ===== 面板通用样式 ===== */
|
||||
section {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
/* ===== 配置面板 ===== */
|
||||
.config-panel {
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
padding-bottom: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.config-section:last-of-type {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.config-section h3 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-light);
|
||||
margin-bottom: var(--spacing-md);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-xs);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.form-group input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.form-group input[readonly] {
|
||||
background: var(--bg-primary);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.input-with-status {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-with-status input {
|
||||
padding-right: 80px;
|
||||
}
|
||||
|
||||
.input-with-button {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.input-with-button input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.field-status {
|
||||
position: absolute;
|
||||
right: var(--spacing-sm);
|
||||
font-size: 0.75rem;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.field-status.success {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.field-status.error {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.field-status.loading {
|
||||
background: rgba(6, 182, 212, 0.2);
|
||||
color: var(--info-color);
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
display: block;
|
||||
margin-top: var(--spacing-xs);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
margin-top: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-actions.main-actions {
|
||||
margin-top: var(--spacing-xl);
|
||||
padding-top: var(--spacing-lg);
|
||||
border-top: 2px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* ===== 按钮样式 ===== */
|
||||
.btn {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-xs);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.btn:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
background: var(--border-color);
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* ===== 数字人显示区 ===== */
|
||||
.digital-human-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.digital-human-box {
|
||||
flex: 1;
|
||||
min-height: 400px;
|
||||
background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 2px dashed var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.digital-human-box.active {
|
||||
border-style: solid;
|
||||
border-color: var(--primary-color);
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
|
||||
.digital-human-box::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(37, 99, 235, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(6, 182, 212, 0.1) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.placeholder-icon {
|
||||
font-size: 4rem;
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-md);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* ===== 控制面板 ===== */
|
||||
.control-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.control-group h3 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.button-row .btn {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.text-input-row {
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.text-input-row input {
|
||||
flex: 1;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.text-input-row input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.text-input-row input:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ===== 日志面板 ===== */
|
||||
.log-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.log-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.log-toolbar select {
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.log-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-md);
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
padding: var(--spacing-xs) 0;
|
||||
border-bottom: 1px solid var(--bg-tertiary);
|
||||
display: flex;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.log-entry:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.log-level {
|
||||
font-weight: 600;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.log-level.debug {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.log-level.info {
|
||||
color: var(--info-color);
|
||||
}
|
||||
|
||||
.log-level.warn {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.log-level.error {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.log-message {
|
||||
color: var(--text-primary);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* ===== 底部 ===== */
|
||||
.footer {
|
||||
background: var(--bg-secondary);
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding: var(--spacing-md) var(--spacing-xl);
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* ===== 响应式设计 ===== */
|
||||
@media (max-width: 1200px) {
|
||||
.main-content {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.digital-human-box {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.log-panel {
|
||||
max-height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.text-input-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 滚动条样式 ===== */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ===== 动画效果 ===== */
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
animation: slideIn 0.2s ease-out;
|
||||
}
|
||||
Reference in New Issue
Block a user