Repository: maioria/chatgpt-talkieai Branch: main Commit: 1702a894bb50 Files: 413 Total size: 1.3 MB Directory structure: gitextract_o3btxkwl/ ├── .gitignore ├── LICENSE ├── README.md ├── talkieai-server/ │ ├── app/ │ │ ├── __init__.py │ │ ├── ai/ │ │ │ ├── __init__.py │ │ │ ├── impl/ │ │ │ │ ├── __init__.py │ │ │ │ ├── chat_gpt_ai.py │ │ │ │ └── zhipu_ai.py │ │ │ ├── interfaces.py │ │ │ └── models.py │ │ ├── api/ │ │ │ ├── __init__.py │ │ │ ├── account_routes.py │ │ │ ├── message_routes.py │ │ │ ├── session_routes.py │ │ │ ├── sys_routes.py │ │ │ └── topics_route.py │ │ ├── config.py │ │ ├── core/ │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── azure_voice.py │ │ │ ├── db_cache.py │ │ │ ├── exceptions.py │ │ │ ├── language.py │ │ │ ├── logging.py │ │ │ └── utils.py │ │ ├── db/ │ │ │ ├── __init__.py │ │ │ ├── account_entities.py │ │ │ ├── chat_entities.py │ │ │ ├── sys_entities.py │ │ │ └── topic_entities.py │ │ ├── main.py │ │ ├── models/ │ │ │ ├── __init__.py │ │ │ ├── account_models.py │ │ │ ├── chat_models.py │ │ │ ├── response.py │ │ │ ├── sys_models.py │ │ │ └── topic_models.py │ │ └── services/ │ │ ├── __init__.py │ │ ├── account_service.py │ │ ├── chat_service.py │ │ ├── sys_service.py │ │ └── topic_service.py │ ├── data/ │ │ ├── azure.json │ │ ├── azure_style_label.json │ │ ├── default_topic_data.json │ │ ├── language_demo_map.json │ │ └── sys_language.json │ ├── requirements.txt │ └── start.sh └── talkieai-uniapp/ ├── index.html ├── package.json ├── src/ │ ├── App.vue │ ├── api/ │ │ ├── account.ts │ │ ├── chat.ts │ │ ├── sys.ts │ │ └── topic.ts │ ├── axios/ │ │ ├── api.ts │ │ ├── axiosServer.ts │ │ └── axiosService.ts │ ├── components/ │ │ ├── AudioPlayer.vue │ │ ├── Checkbox.vue │ │ ├── Collect.vue │ │ ├── CommonHeader.vue │ │ ├── FunctionalText.vue │ │ ├── GithubLink.vue │ │ ├── Loading.vue │ │ ├── LoadingRound.vue │ │ ├── Rare2.vue │ │ ├── Rate.vue │ │ ├── Speech.vue │ │ ├── WordAnalysisPopup.vue │ │ ├── audioPlayerExecuter.ts │ │ └── speechExecuter.ts │ ├── config/ │ │ └── env.ts │ ├── env.d.ts │ ├── global/ │ │ └── globalCount.hooks.ts │ ├── less/ │ │ └── global.less │ ├── main.ts │ ├── manifest.json │ ├── models/ │ │ ├── chat.ts │ │ ├── models.ts │ │ └── sys.ts │ ├── pages/ │ │ ├── chat/ │ │ │ ├── components/ │ │ │ │ ├── CommonAudioPlayer.vue │ │ │ │ ├── MessageContent.vue │ │ │ │ ├── MessageGrammar.vue │ │ │ │ ├── MessageGrammarPopup.vue │ │ │ │ ├── MessagePronunciation.vue │ │ │ │ ├── MessageSpeech.vue │ │ │ │ ├── PhonemeBox.vue │ │ │ │ ├── Prompt.vue │ │ │ │ ├── PromptPopup.vue │ │ │ │ ├── TextPronunciation.vue │ │ │ │ ├── TranslationPopup.vue │ │ │ │ └── WordDetail.vue │ │ │ ├── index.vue │ │ │ └── settings.vue │ │ ├── contact/ │ │ │ ├── index.vue │ │ │ └── less/ │ │ │ └── index.less │ │ ├── feedback/ │ │ │ ├── index.vue │ │ │ └── less/ │ │ │ └── index.less │ │ ├── index/ │ │ │ ├── components/ │ │ │ │ └── Topics.vue │ │ │ ├── index.vue │ │ │ └── switchRole.vue │ │ ├── login/ │ │ │ ├── index.vue │ │ │ └── service.ts │ │ ├── my/ │ │ │ ├── index.vue │ │ │ ├── learnLanguage.vue │ │ │ └── less/ │ │ │ └── index.less │ │ ├── practice/ │ │ │ ├── components/ │ │ │ │ ├── Single.vue │ │ │ │ └── Statement.vue │ │ │ └── index.vue │ │ └── topic/ │ │ ├── completion.vue │ │ ├── history.vue │ │ ├── index.vue │ │ ├── phrase.vue │ │ └── topicCreate.vue │ ├── pages.json │ ├── shime-uni.d.ts │ ├── uni.scss │ ├── uni_modules/ │ │ ├── uni-badge/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-badge/ │ │ │ │ └── uni-badge.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-calendar/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-calendar/ │ │ │ │ ├── calendar.js │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ ├── uni-calendar-item.vue │ │ │ │ ├── uni-calendar.vue │ │ │ │ └── util.js │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-card/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-card/ │ │ │ │ └── uni-card.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-collapse/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-collapse/ │ │ │ │ │ └── uni-collapse.vue │ │ │ │ └── uni-collapse-item/ │ │ │ │ └── uni-collapse-item.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-combox/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-combox/ │ │ │ │ └── uni-combox.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-countdown/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-countdown/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-countdown.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-data-checkbox/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-data-checkbox/ │ │ │ │ └── uni-data-checkbox.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-data-picker/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-data-picker/ │ │ │ │ │ ├── keypress.js │ │ │ │ │ └── uni-data-picker.vue │ │ │ │ └── uni-data-pickerview/ │ │ │ │ ├── uni-data-picker.js │ │ │ │ └── uni-data-pickerview.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-data-select/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-data-select/ │ │ │ │ └── uni-data-select.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-dateformat/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-dateformat/ │ │ │ │ ├── date-format.js │ │ │ │ └── uni-dateformat.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-datetime-picker/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-datetime-picker/ │ │ │ │ ├── calendar-item.vue │ │ │ │ ├── calendar.vue │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ ├── time-picker.vue │ │ │ │ ├── uni-datetime-picker.vue │ │ │ │ └── util.js │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-drawer/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-drawer/ │ │ │ │ ├── keypress.js │ │ │ │ └── uni-drawer.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-easyinput/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-easyinput/ │ │ │ │ ├── common.js │ │ │ │ └── uni-easyinput.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-fab/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-fab/ │ │ │ │ └── uni-fab.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-fav/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-fav/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-fav.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-file-picker/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-file-picker/ │ │ │ │ ├── choose-and-upload-file.js │ │ │ │ ├── uni-file-picker.vue │ │ │ │ ├── upload-file.vue │ │ │ │ ├── upload-image.vue │ │ │ │ └── utils.js │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-forms/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-forms/ │ │ │ │ │ ├── uni-forms.vue │ │ │ │ │ ├── utils.js │ │ │ │ │ └── validate.js │ │ │ │ └── uni-forms-item/ │ │ │ │ └── uni-forms-item.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-goods-nav/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-goods-nav/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-goods-nav.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-grid/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-grid/ │ │ │ │ │ └── uni-grid.vue │ │ │ │ └── uni-grid-item/ │ │ │ │ └── uni-grid-item.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-group/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-group/ │ │ │ │ └── uni-group.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-icons/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-icons/ │ │ │ │ ├── icons.js │ │ │ │ ├── uni-icons.vue │ │ │ │ └── uniicons.css │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-indexed-list/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-indexed-list/ │ │ │ │ ├── uni-indexed-list-item.vue │ │ │ │ └── uni-indexed-list.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-link/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-link/ │ │ │ │ └── uni-link.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-list/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-list/ │ │ │ │ │ ├── uni-list.vue │ │ │ │ │ ├── uni-refresh.vue │ │ │ │ │ └── uni-refresh.wxs │ │ │ │ ├── uni-list-ad/ │ │ │ │ │ └── uni-list-ad.vue │ │ │ │ ├── uni-list-chat/ │ │ │ │ │ ├── uni-list-chat.scss │ │ │ │ │ └── uni-list-chat.vue │ │ │ │ └── uni-list-item/ │ │ │ │ └── uni-list-item.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-load-more/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-load-more/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-load-more.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-nav-bar/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-nav-bar/ │ │ │ │ ├── uni-nav-bar.vue │ │ │ │ └── uni-status-bar.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-notice-bar/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-notice-bar/ │ │ │ │ └── uni-notice-bar.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-number-box/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-number-box/ │ │ │ │ └── uni-number-box.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-pagination/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-pagination/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── es.json │ │ │ │ │ ├── fr.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-pagination.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-popup/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-popup/ │ │ │ │ │ ├── i18n/ │ │ │ │ │ │ ├── en.json │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ │ └── zh-Hant.json │ │ │ │ │ ├── keypress.js │ │ │ │ │ ├── popup.js │ │ │ │ │ └── uni-popup.vue │ │ │ │ ├── uni-popup-dialog/ │ │ │ │ │ ├── keypress.js │ │ │ │ │ └── uni-popup-dialog.vue │ │ │ │ ├── uni-popup-message/ │ │ │ │ │ └── uni-popup-message.vue │ │ │ │ └── uni-popup-share/ │ │ │ │ └── uni-popup-share.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-rate/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-rate/ │ │ │ │ └── uni-rate.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-row/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-col/ │ │ │ │ │ └── uni-col.vue │ │ │ │ └── uni-row/ │ │ │ │ └── uni-row.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-scss/ │ │ │ ├── changelog.md │ │ │ ├── index.scss │ │ │ ├── package.json │ │ │ ├── readme.md │ │ │ ├── styles/ │ │ │ │ ├── index.scss │ │ │ │ ├── setting/ │ │ │ │ │ ├── _border.scss │ │ │ │ │ ├── _color.scss │ │ │ │ │ ├── _radius.scss │ │ │ │ │ ├── _space.scss │ │ │ │ │ ├── _styles.scss │ │ │ │ │ ├── _text.scss │ │ │ │ │ └── _variables.scss │ │ │ │ └── tools/ │ │ │ │ └── functions.scss │ │ │ ├── theme.scss │ │ │ └── variables.scss │ │ ├── uni-search-bar/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-search-bar/ │ │ │ │ ├── i18n/ │ │ │ │ │ ├── en.json │ │ │ │ │ ├── index.js │ │ │ │ │ ├── zh-Hans.json │ │ │ │ │ └── zh-Hant.json │ │ │ │ └── uni-search-bar.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-section/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-section/ │ │ │ │ └── uni-section.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-segmented-control/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-segmented-control/ │ │ │ │ └── uni-segmented-control.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-steps/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-steps/ │ │ │ │ └── uni-steps.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-swipe-action/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-swipe-action/ │ │ │ │ │ └── uni-swipe-action.vue │ │ │ │ └── uni-swipe-action-item/ │ │ │ │ ├── bindingx.js │ │ │ │ ├── isPC.js │ │ │ │ ├── mpalipay.js │ │ │ │ ├── mpother.js │ │ │ │ ├── mpwxs.js │ │ │ │ ├── render.js │ │ │ │ ├── uni-swipe-action-item.vue │ │ │ │ └── wx.wxs │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-swiper-dot/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-swiper-dot/ │ │ │ │ └── uni-swiper-dot.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-table/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ ├── uni-table/ │ │ │ │ │ └── uni-table.vue │ │ │ │ ├── uni-tbody/ │ │ │ │ │ └── uni-tbody.vue │ │ │ │ ├── uni-td/ │ │ │ │ │ └── uni-td.vue │ │ │ │ ├── uni-th/ │ │ │ │ │ ├── filter-dropdown.vue │ │ │ │ │ └── uni-th.vue │ │ │ │ ├── uni-thead/ │ │ │ │ │ └── uni-thead.vue │ │ │ │ └── uni-tr/ │ │ │ │ ├── table-checkbox.vue │ │ │ │ └── uni-tr.vue │ │ │ ├── i18n/ │ │ │ │ ├── en.json │ │ │ │ ├── es.json │ │ │ │ ├── fr.json │ │ │ │ ├── index.js │ │ │ │ ├── zh-Hans.json │ │ │ │ └── zh-Hant.json │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-tag/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-tag/ │ │ │ │ └── uni-tag.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-title/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-title/ │ │ │ │ └── uni-title.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-tooltip/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-tooltip/ │ │ │ │ └── uni-tooltip.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── uni-transition/ │ │ │ ├── changelog.md │ │ │ ├── components/ │ │ │ │ └── uni-transition/ │ │ │ │ ├── createAnimation.js │ │ │ │ └── uni-transition.vue │ │ │ ├── package.json │ │ │ └── readme.md │ │ └── uni-ui/ │ │ ├── changelog.md │ │ ├── components/ │ │ │ └── uni-ui/ │ │ │ └── uni-ui.vue │ │ ├── package.json │ │ └── readme.md │ └── utils/ │ ├── bus.ts │ └── utils.ts ├── tsconfig.json └── vite.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /talkieai-server/.env /talkieai-uniapp/package-lock.json /talkieai-uniapp/node_modules/ ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # TalkieAI ## 简介 [TalkieAI](https://github.com/maioria/chatgpt-talkieai) 是一个基于AI的外语学习应用,可通过语音进行聊天,语法分析,翻译。 AI可以基于CHAT-GPT, 国内可以配置chat-gpt代理或者使用[智谱开放平台](https://open.bigmodel.cn/) ## 微信小程序 ![](https://aitake.oss-cn-wulanchabu.aliyuncs.com/9dcce2ab0be473a09e3c4ce28e5d7d05ca848ead92981c14fd1d7601eb7be0f8.jpg) ## 后端 - 使用python语言开发,开发使用的版本为3.11,web框架为fastAPI,数据层框架为SQLAlchemy,语音使用azure。 ## 前端 - 前端使用uniapp开发,基于vue3,可发布到网页与小程序与APP ## 项目示例图 ![](https://aitake.oss-cn-wulanchabu.aliyuncs.com/c79b6648bea061d2813773075ba3349807dcaea90c9699c5cef9cfa6b894e9ad.png) ![](https://aitake.oss-cn-wulanchabu.aliyuncs.com/e7a3ad173b55dad7682d843ce1e7a424ef321bf03ac100c81ff07519b05352d0.png) ![](https://aitake.oss-cn-wulanchabu.aliyuncs.com/6a7d7ff41c5f366b43084a41e7268dcdbc32b65fd0dd976b5ec368ac28dca3cf.png) ![](https://aitake.oss-cn-wulanchabu.aliyuncs.com/70d6feeb534e9aa9748d51c8c10acc06bb00224f8a40d96a002dc434977d1524.png) ## 本地启动 ```bash # 数据库,创建一个空的数据库,.env文件配置好数据库后启动服务,服务会自动生成相应的表,并且加载默认数据 # 1.克隆本仓库; git clone git@github.com:maioria/chatgpt-talkieai.git cd talkieai-server # 2.安装依赖; pip3 install -r requirements.txt # 3. 启动服务(需要新建.env文件并设置变量,参考.env.default) nohup uvicorn app.main:app --host 0.0.0.0 --port 8097 & #前端使用HBuilder直接web或者小程序运行 # 1. 安装依赖(前端只用了俩个依赖fingerprintjs2 与 recorder) npm install ``` ## nginx配置(Web) ```bash # uniapp可以直接跨域请求服务端地址,也可通过nginx来配置反向代理 server { listen 80; listen [::]:80; server_name {server_name}; rewrite ^(.*) https://$server_name$1 permanent; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name {server_name}; root {前端编译完后的路径}; ssl_certificate "{crt}"; ssl_certificate_key "{key}"; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # Load configuration files for the default server block. location ^~ /api/ { proxy_pass http://localhost:8000/api/; proxy_set_header Host $http_host; proxy_connect_timeout 15s; proxy_send_timeout 300s; proxy_read_timeout 300s; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } } ``` ## 贡献 如果您有任何建议或意见,欢迎提出 [Issues](https://github.com/maioria/chatgpt-talkieai/issues) 或 [ Pull Request](https://github.com/maioria/chatgpt-talkieai/pulls)。 ================================================ FILE: talkieai-server/app/__init__.py ================================================ ================================================ FILE: talkieai-server/app/ai/__init__.py ================================================ from app.config import Config from app.ai.impl.zhipu_ai import ZhipuAIComponent from app.ai.impl.chat_gpt_ai import ChatGPTAI if Config.AI_SERVER=='CHAT_GPT': chat_ai = ChatGPTAI(api_key=Config.CHAT_GPT_KEY, base_url=Config.CHAT_GPT_PROXY, model=Config.CHAT_GPT_MODEL) elif Config.AI_SERVER=='ZHIPU': chat_ai = ZhipuAIComponent(api_key=Config.ZHIPU_AI_API_KEY, model=Config.ZHIPU_AI_MODEL) else: raise Exception('AI_SERVER配置错误,只能配置为CHAT_GPT或ZHIPU') ================================================ FILE: talkieai-server/app/ai/impl/__init__.py ================================================ ================================================ FILE: talkieai-server/app/ai/impl/chat_gpt_ai.py ================================================ from typing import List, Dict import json from dataclasses import dataclass from app.ai.interfaces import * from app.ai.models import * from app.core.logging import logging class ChatGPTAI(ChatAI): """本地直接调用openai的接口""" def __init__(self, api_key: str, base_url: str = None, model: str = None): from openai import OpenAI self.api_key = api_key self.client = OpenAI(api_key=api_key) self.model = model if base_url: self.client.base_url = base_url def invoke_greet(self, params: GreetParams) -> str: messages = [ {"role": "system", "content": f"你需要使用标识为 {params.language} 的语言来打个招呼,10字左右."} ] invoke_dto = MessageInvokeDTO(messages=messages) return self._original_invoke_chat(invoke_dto) def topic_invoke_greet(self, params: TopicGreetParams) -> str: messages = [ { "role": "system", "content": f"场景:{params.prompt}. 现在你需要使用标识为 {params.language} 的语言来打个招呼,20字左右.", } ] invoke_dto = MessageInvokeDTO(messages=messages) return self._original_invoke_chat(invoke_dto) def invoke_message(self, dto: MessageParams) -> AIMessageResult: """与AI自由聊天""" language = dto.language system_message = ( 'The reply must be json, and format of json is {"message":"result of message","message_style":"must be one of the options ' + f"{json.dumps(dto.styles, ensure_ascii=False)}" + '"}, ' + f"The 'message_style' within the square brackets . " + f"I want you to act as an {language} speaking partner and improver, your name is {dto.name}. " + f"No matter what language I speak to you, you need to reply me in {language}. " + f"I hope you will ask me a question from time to time in your reply " ) messages = [{"role": "system", "content": system_message}] for message in dto.messages: messages.append(message) resp = self._original_invoke_chat_json(MessageInvokeDTO(messages=messages)) result = AIMessageResult( message=resp["message"], message_style=resp["message_style"] ) return result def topic_invoke_message(self, dto: AITopicMessageParams) -> AITopicMessageResult: """与AI自由聊天""" language = dto.language system_message = ( f"Topic:{dto.prompt}.Please chat with me in this topic. If this conversation can be concluded or if the user wishes to end it, please return topic_completed=true." + 'The reply must be json, and format of json is {"message":"result of message","topic_completed":"Whether this topic has been completd.","message_style":"must be one of the options ' + f"{json.dumps(dto.styles, ensure_ascii=False)}" + '"}, ' + f"The 'message_style' within the square brackets . " + f"I want you to act as an {language} speaking partner and improver, your name is {dto.name}. " + f"No matter what language I speak to you, you need to reply me in {language}. " + f"I hope you will ask me a question from time to time in your reply " ) messages = [{"role": "system", "content": system_message}] for message in dto.messages: messages.append(message) resp = self._original_invoke_chat_json(MessageInvokeDTO(messages=messages)) message_style = None # resp是否有message_style if "message_style" in resp: message_style = resp["message_style"] completed = False # resp是否有topic_completed if "topic_completed" in resp: completed = resp["topic_completed"] == "true" result = AITopicMessageResult( message=resp["message"], message_style=message_style, completed=completed ) return result def topic_invoke_complete( self, dto: AITopicCompleteParams ) -> AITopicCompleteResult: """场景 结束""" system_content = "下面是一场对话\n" for message in dto.messages: if message.role.lower() == "system": system_content = system_content + f"AI: {message.content}\n" elif message.role.lower() == "account": system_content = system_content + f"用户: {message.content}\n" system_content = system_content + "下面是用户对话中需要实现的目标\n" for target in dto.targets: system_content = system_content + f"{target}\n" system_content = ( system_content + "现在你需要计算出 <用户:> 所说的所有话中使用了多少单词数量(仅需要数字结果,重复单词不需要计算),对应后面的目标实现了多少个(仅需要数字结果),对用户的表达给出评分(满分100分,仅需要数字结果),还要给出300字以内的建议(包含中文讲解与英文示例),返回结果只需要有json格式,使用单词量放在words字段,目标实现数量放在targets字段,评分放在score字段,建议放在suggestion字段,不需要再额外的任何信息,记住,只需要统计<用户:>下的内容\n" ) json_result = self._original_invoke_chat_json( MessageInvokeDTO(messages=[{"role": "system", "content": system_content}]) ) # 组装成AITopicCompleteResult返回 return AITopicCompleteResult( targets=json_result["targets"], score=json_result["score"], words=json_result["words"], suggestion=json_result["suggestion"], ) def invoke_translate(self, dto: TranslateParams) -> str: """翻译""" system_message = f"下面是段文本:'{dto.content}' 仅输出翻译成 {dto.target_language} 后的内容" invoke_dto = MessageInvokeDTO( messages=[{"role": "system", "content": system_message}] ) resp = self._original_invoke_chat(invoke_dto) return resp def invoke_grammar_analysis( self, params: GrammarAnalysisParams ) -> AIGrammarAnalysisResult: messages = [ { "role": "user", "content": f"检查内容是否存在语法错误(不需要检查符号的使用),如果存在就用中文返回这段内容中的语法错误,再提供一句推荐示例,要求数据格式为json,无任何转义字符,可直接被程序正常序列化,语法是否错误放在属性isCorrect中,错误原因放在errorReason中,修正后的正确示例放在correctContent中,推荐示例放在better中,正确示例与推荐示例的语言要使用{params.language},错误原因使用中文. 提供内容是:{params.content}", } ] invoke_dto = MessageInvokeDTO(messages=messages) result_data = self._original_invoke_chat(invoke_dto) result_json = json.loads(result_data) return AIGrammarAnalysisResult( is_correct=result_json["isCorrect"], error_reason=result_json["errorReason"], correct_content=result_json["correctContent"], better=result_json["better"], ) def invoke_prompt_sentence(self, params: PromptSentenceParams) -> str: """ """ logging.info(f"request_params:{params}") system_content = "下面是一场对话\n" for message in reversed(params.messages): if message["role"].lower() == "user": system_content = system_content + f"用户: {message['content']}\n" else: system_content = system_content + f"AI: {message['content']}\n" system_content = ( system_content + "现在你需要做为一个用户来回答下一句话,不可以有提供帮助与提问问题的意思,语言使用" + params.language + ", 直接输出内容前面不可以加 User:" ) invoke_dto = MessageInvokeDTO( messages=[{"role": "user", "content": system_content}] ) resp = self._original_invoke_chat(invoke_dto) return resp def invoke_word_detail(self, params: WordDetailParams) -> AIWordDetailResult: logging.info(f"request_dto:{params}") messages = [ { "role": "user", "content": f'提供一个单词,只需要简洁快速的用中文返回这个单词的音标与翻译,要求数据格式为json,音标放在属性phonetic中,音标的前后要加上"/",翻译放在translation中, 这个单词是"{params.word}"', } ] invoke_dto = MessageInvokeDTO(messages=messages) result_data = self._original_invoke_chat(invoke_dto) result_json = json.loads(result_data) return AIWordDetailResult( phonetic=result_json["phonetic"], translation=result_json["translation"] ) def _original_invoke_chat_json(self, dto: MessageInvokeDTO): logging.info(f"request_dto:{dto}") resp = self.client.chat.completions.create( model=self.model, temperature=dto.temperature, messages=dto.messages, max_tokens=dto.max_tokens, response_format={"type": "json_object"}, ) logging.info(f"response:{resp}") result = resp.choices[0].message.content return json.loads(result) def _original_invoke_chat(self, dto: MessageInvokeDTO): logging.info(f"dto:{dto}") resp = self.client.chat.completions.create( model=self.model, temperature=dto.temperature, messages=dto.messages, max_tokens=dto.max_tokens, ) logging.info(f"response:{resp}") result = resp.choices[0].message.content # 去掉俩边的 “” '' result = result.strip('"') result = result.strip("'") # 去掉json的转义字符 result = result.replace('\\"', '"').replace("\\n", "\n").replace("\\", "") return result ================================================ FILE: talkieai-server/app/ai/impl/zhipu_ai.py ================================================ from typing import List, Dict import json from pydantic import BaseModel from app.ai.interfaces import * from app.ai.models import * from app.core.language import * from app.core.logging import logging class ZhipuInvokeDTO(BaseModel): messages: List[Dict] model: str temperature: int = 0.1 class ZhipuAIComponent(ChatAI): def __init__(self, api_key: str, model: str): from zhipuai import ZhipuAI self.client = ZhipuAI(api_key=api_key) self.model = model def invoke_greet(self, params: GreetParams) -> str: messages = [ {"role": "user", "content": f"你需要使用标识为 {params.language} 的语言来打个招呼,10字左右."} ] invoke_dto = MessageInvokeDTO(messages=messages) return self._original_invoke_chat(invoke_dto) def topic_invoke_greet(self, params: TopicGreetParams) -> str: messages = [ { "role": "user", "content": f"场景:{params.prompt}. 现在你需要打个招呼,20字左右.记住语言必须使用使用 {get_language_label_by_value(params.language)},不可以使用其他语言 ", } ] invoke_dto = MessageInvokeDTO(messages=messages) return self._original_invoke_chat(invoke_dto) def invoke_message(self, dto: MessageParams) -> AIMessageResult: """与AI自由聊天""" language = dto.language system_message = ( 'The reply must be json, and format of json is {"message":"result of message","message_style":"must be one of the options ' + f"{json.dumps(dto.styles, ensure_ascii=False)}" + '"}, ' + f"The 'message_style' within the square brackets . " + f"I want you to act as an {language} speaking partner and improver, your name is {dto.name}. " + f"No matter what language I speak to you, you need to reply me in {language}. " + f"I hope you will ask me a question from time to time in your reply " ) messages = [{"role": "system", "content": system_message}] for message in dto.messages: messages.append(message) resp = self._original_invoke_chat(MessageInvokeDTO(messages=messages)) # 检查resp是否是json格式,如果不json格式,就返回错误 try: resp = json.loads(resp) result = AIMessageResult( message=resp["message"], message_style=resp["message_style"] ) except Exception as e: logging.warn(f"resp不是json格式:{resp},request_params:{system_message}") result = AIMessageResult(message=resp, message_style=None) return result def topic_invoke_message(self, dto: AITopicMessageParams) -> AITopicMessageResult: """与AI自由聊天""" language = dto.language system_message = ( f"Topic:{dto.prompt}.Please chat with me in this topic. If this conversation can be concluded or if the user wishes to end it, please return topic_completed=true." + 'The reply must be json, and format of json is {"message":"result of message","topic_completed":"Whether this topic has been completd.","message_style":"must be one of the options ' + f"{json.dumps(dto.styles, ensure_ascii=False)}" + '"}, ' + f"The 'message_style' within the square brackets . " + f"I want you to act as an {language} speaking partner and improver, your name is {dto.name}. " + f"No matter what language I speak to you, you need to reply me in {language}. " + f"I hope you will ask me a question from time to time in your reply " ) messages = [{"role": "system", "content": system_message}] for message in dto.messages: messages.append(message) resp = self._original_invoke_chat(MessageInvokeDTO(messages=messages)) # 检查resp是否是json格式,如果不json格式,就返回错误 try: resp = json.loads(resp) message_style = None # resp是否有message_style if "message_style" in resp: message_style = resp["message_style"] completed = False # resp是否有topic_completed if "topic_completed" in resp: completed = resp["topic_completed"] == "true" result = AITopicMessageResult( message=resp["message"], message_style=message_style, completed=completed, ) except Exception as e: logging.warn(f"resp不是json格式:{resp},request_params:{system_message}") result = AITopicMessageResult( message=resp, completed=False, message_style=None ) return result def topic_invoke_complete( self, dto: AITopicCompleteParams ) -> AITopicCompleteResult: """场景 结束""" system_content = "下面是一场对话\n" for message in dto.messages: if message.role.lower() == "system": system_content = system_content + f"AI: {message.content}\n" elif message.role.lower() == "account": system_content = system_content + f"用户: {message.content}\n" system_content = system_content + "下面是用户对话中需要实现的目标\n" for target in dto.targets: system_content = system_content + f"{target}\n" system_content = ( system_content + "现在你需要计算出 <用户:> 所说的所有话中使用了多少单词数量(仅需要数字结果,重复单词不需要计算),对应后面的目标实现了多少个(仅需要数字结果),对用户的表达给出评分(满分100分,仅需要数字结果),还要给出300字以内的建议(包含中文讲解与英文示例),返回结果只需要有json格式,使用单词量放在words字段,目标实现数量放在targets字段,评分放在score字段,建议放在suggestion字段,不需要再额外的任何信息,记住,只需要统计<用户:>下的内容\n" ) json_result = self._original_invoke_chat_json( MessageInvokeDTO(messages=[{"role": "user", "content": system_content}]) ) # 组装成AITopicCompleteResult返回 return AITopicCompleteResult( targets=json_result["targets"], score=json_result["score"], words=json_result["words"], suggestion=json_result["suggestion"], ) def invoke_translate(self, dto: TranslateParams) -> str: """翻译""" system_message = f"下面是段文本:'{dto.content}' 仅输出翻译成 {dto.target_language} 后的内容,不可以有其他介绍内容" invoke_dto = MessageInvokeDTO( messages=[{"role": "user", "content": system_message}] ) resp = self._original_invoke_chat(invoke_dto) return resp def invoke_grammar_analysis( self, params: GrammarAnalysisParams ) -> AIGrammarAnalysisResult: messages = [ { "role": "user", "content": f"检查内容是否存在语法错误(不需要检查符号的使用),如果存在就用中文返回这段内容中的语法错误,再提供一句推荐示例,要求数据格式为json,无任何转义字符,可直接被程序正常序列化,语法是否错误放在属性isCorrect中,错误原因放在errorReason中,修正后的正确示例放在correctContent中,推荐示例放在better中,正确示例与推荐示例的语言要使用{params.language},错误原因使用中文. 提供内容是:{params.content}", } ] invoke_dto = MessageInvokeDTO(messages=messages) result_json = self._original_invoke_chat_json(invoke_dto) return AIGrammarAnalysisResult( is_correct=result_json["isCorrect"], error_reason=result_json["errorReason"], correct_content=result_json["correctContent"], better=result_json["better"], ) def invoke_prompt_sentence(self, params: PromptSentenceParams) -> str: """ """ logging.info(f"request_params:{params}") system_content = "下面是一场对话\n" for message in reversed(params.messages): if message["role"].lower() == "user": system_content = system_content + f"用户: {message['content']}\n" else: system_content = system_content + f"AI: {message['content']}\n" system_content = ( system_content + "现在你需要做为一个用户来回答下一句话,不可以有提供帮助与提问问题的意思,返回内容不得包含 用户: 等其他介绍字眼,语言使用" + params.language ) invoke_dto = MessageInvokeDTO( messages=[{"role": "user", "content": system_content}] ) resp = self._original_invoke_chat(invoke_dto) return resp def invoke_word_detail(self, params: WordDetailParams) -> AIWordDetailResult: logging.info(f"request_dto:{params}") messages = [ { "role": "user", "content": f'提供一个单词,只需要简洁快速的用中文返回这个单词的音标与翻译,要求数据格式为json,音标放在属性phonetic中,音标的前后要加上"/",翻译放在translation中, 这个单词是"{params.word}"', } ] invoke_dto = MessageInvokeDTO(messages=messages) result_json = self._original_invoke_chat_json(invoke_dto) return AIWordDetailResult( phonetic=result_json["phonetic"], translation=result_json["translation"] ) def _original_invoke_chat(self, dto: MessageInvokeDTO): logging.info(f"request_params:{dto.__dict__}") # invoke_dto = ZhipuInvokeDTO(messages=dto.messages, model=self.model) resp = self.client.chat.completions.create( model=self.model, messages=dto.messages, stream=False ) logging.info(f"response:{resp}") result = resp.choices[0].message.content # 去掉俩边的 “” result = result.strip('"') # 去掉json的转义字符 result = result.replace('\\"', '"').replace("\\n", "\n").replace("\\", "") return result def _original_invoke_chat_json(self, dto: MessageInvokeDTO): logging.info(f"request_params:{dto.__dict__}") invoke_dto = ZhipuInvokeDTO(messages=dto.messages, model=self.model) resp = self.client.chat.completions.create(**invoke_dto.__dict__) logging.info(f"response:{resp}") result = resp.choices[0].message.content # 如果格式为类似markdown的 ```json\n{}\n```,就去掉前后的 ```json\n 与 \n``` if result.startswith("```json\n") and result.endswith("\n```"): result = result.replace("```json\n", "").replace("\n```", "") # 去掉俩边的 “” result = result.strip('"') # 去掉json的转义字符 result = result.replace('\\"', '"').replace("\\n", "\n").replace("\\", "") return json.loads(result) ================================================ FILE: talkieai-server/app/ai/interfaces.py ================================================ from app.ai.models import * from abc import ABC, abstractmethod from typing import List, Dict from abc import ABC, abstractmethod from dataclasses import dataclass from app.ai.models import * @dataclass class MessageInvokeDTO: messages: List[Dict] temperature: float = 0.5 max_tokens: int = 300 @dataclass class FunctionInvokeDTO: function: Dict messages: List[Dict] temperature: float = 0.5 max_tokens: int = 300 class ChatAI(ABC): @abstractmethod def invoke_message(self, dto: MessageParams) -> AIMessageResult: """聊天""" pass @abstractmethod def invoke_translate(self, dto: TranslateParams) -> str: """翻译""" pass @abstractmethod def invoke_greet(self, dto: GreetParams) -> str: """打招呼""" pass @abstractmethod def invoke_grammar_analysis( self, dto: GrammarAnalysisParams ) -> AIGrammarAnalysisResult: """语法分析""" pass @abstractmethod def invoke_prompt_sentence(self, dto: PromptSentenceParams) -> str: """为用户提示句子""" pass @abstractmethod def invoke_word_detail(self, dto: WordDetailParams) -> AIWordDetailResult: """单词详情""" pass @abstractmethod def topic_invoke_greet(self, dto: TopicGreetParams) -> str: """场景 打招呼""" pass @abstractmethod def topic_invoke_message(self, dto: AITopicMessageParams) -> AITopicMessageResult: """场景 聊天""" pass @abstractmethod def topic_invoke_complete(self, dto: AITopicCompleteParams) -> AITopicCompleteResult: """场景 结束""" pass ================================================ FILE: talkieai-server/app/ai/models.py ================================================ from typing import List, Dict from dataclasses import dataclass, field @dataclass class MessageItemParams: role: str content: str @dataclass class MessageParams: language: str name: str messages: List[Dict] styles: List[str] temperature: float = 0.5 max_tokens: int = 300 @dataclass class AITopicMessageParams: language: str speech_role_name: str prompt: str name: str messages: List[Dict] = field(default_factory=list) styles: List[str] = field(default_factory=list) temperature: float = 0.5 max_tokens: int = 300 @dataclass class AITopicCompleteParams: language: str targets: List[str] = field(default_factory=list) messages: List[MessageItemParams] = field(default_factory=list) @dataclass class AITopicCompleteResult: targets: str score: str words: int suggestion: str @dataclass class AIMessageResult: message: str message_style: str | None @dataclass class AITopicMessageResult: message: str message_style: str | None completed: bool @dataclass class TranslateParams: target_language: str content: str @dataclass class GreetParams: language: str @dataclass class GrammarAnalysisParams: language: str content: str @dataclass class AIGrammarAnalysisResult: is_correct: bool error_reason: str correct_content: str better: str @dataclass class PromptSentenceParams: language: str messages: List[Dict] @dataclass class WordDetailParams: word: str @dataclass class AIWordDetailResult: phonetic: str translation: str @dataclass class TopicGreetParams: language: str prompt: str ================================================ FILE: talkieai-server/app/api/__init__.py ================================================ ================================================ FILE: talkieai-server/app/api/account_routes.py ================================================ from fastapi import APIRouter, Depends, Request from sqlalchemy.orm import Session from app.core import get_current_account from app.db import get_db from app.models.account_models import * from app.models.response import ApiResponse from app.services.account_service import AccountService from app.services.chat_service import ChatService router = APIRouter() @router.post("/account/visitor-login", name="Visitor login") def visitor_login( request: Request, dto: VisitorLoginDTO, db: Session = Depends(get_db) ): """用户访客登录,一个IP只能有一个访客,如果ip已经生成了访客""" client_host = request.client.host # client_host 不能为空 if not client_host: return ApiResponse(code="400", status="FAILED", message="client_host 不能为空") # dto.fingerprint 不能为空 if not dto.fingerprint: return ApiResponse(code="400", status="FAILED", message="dto.fingerprint 不能为空") user_agent = request.headers["User-Agent"] account_service = AccountService(db) return ApiResponse( data=account_service.visitor_login(dto.fingerprint, client_host, user_agent) ) @router.get("/account/info", name="Get User info") def get_account_info( db: Session = Depends(get_db), account_id: str = Depends(get_current_account) ): """获取用户信息""" account_service = AccountService(db) return ApiResponse(data=account_service.get_account_info(account_id)) @router.post("/account/settings") def account_settings_api( dto: AccountSettingsDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """用户保存设置""" account_service = AccountService(db) return ApiResponse(data=account_service.save_settings(dto, account_id)) @router.get('/account/settings') def get_account_settings_api( db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取用户设置""" account_service = AccountService(db) return ApiResponse(data=account_service.get_settings(account_id)) @router.post("/account/role", name="Update User role") def update_role( dto: UpdateRoleDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """选择角色""" account_service = AccountService(db) return ApiResponse(data=account_service.update_role_setting(dto, account_id)) @router.get("/account/role", name="Get User role") def get_account_role( db: Session = Depends(get_db), account_id: str = Depends(get_current_account) ): """获取选择的角色""" account_service = AccountService(db) return ApiResponse(data=account_service.get_role_setting(account_id)) @router.get("/account/collect") def get_account_collect_api( type: str, message_id: str = None, content: str = None, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取用户收藏状态""" account_service = AccountService(db) return ApiResponse( data=account_service.get_collect( CollectDTO(type=type, message_id=message_id, content=content), account_id ) ) @router.post("/account/collect") def account_collect_api( dto: CollectDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """用户保存单词与句子的接口""" account_service = AccountService(db) return ApiResponse(data=account_service.collect(dto, account_id)) @router.delete("/account/collect") def account_collect_api( dto: CollectDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """取消用户保存的单词或者句子""" account_service = AccountService(db) return ApiResponse(data=account_service.cancel_collect(dto, account_id)) @router.get("/account/collects") def get_account_collects_api( type: str, page: int = 1, page_size: int = 10, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取用户收藏的列表信息,包含分页效果""" account_service = AccountService(db) return ApiResponse( data=account_service.get_collects(type, page, page_size, account_id) ) ================================================ FILE: talkieai-server/app/api/message_routes.py ================================================ from fastapi import APIRouter, Depends, Response from sqlalchemy.orm import Session from app.core import get_current_account from app.core.utils import * from app.db import get_db from app.models.account_models import * from app.models.chat_models import * from app.models.response import ApiResponse from app.services.account_service import AccountService from app.services.chat_service import ChatService router = APIRouter() @router.post("/messages/{message_id}/practice") def message_practice_api( message_id: str, dto: MessagePracticeDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """发送消息""" chat_service = ChatService(db) return ApiResponse( data=chat_service.message_practice(message_id, dto, account_id) ) @router.post("/messages/{message_id}/translate") def translate_api( message_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """翻译成用户的源语言""" chat_service = ChatService(db) return ApiResponse(data=chat_service.translate_message(message_id, account_id)) @router.get("/message/speech") def speech_api( message_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """消息转语音""" chat_service = ChatService(db) speech_result = chat_service.message_speech(message_id, account_id) """获取文件""" file_path = voice_file_get_path(speech_result["file"]) # 判断文件是否存在 with open(file_path, "rb") as file: contents = file.read() headers = { "Content-Type": "audio/wav", "Content-Disposition": "attachment", "filename": speech_result["file"], } return Response( content=contents, media_type="application/octet-stream", headers=headers ) @router.post("/message/translate-source-language") def translate_source_language( dto: TranslateTextDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """翻译成用户本身的语言""" chat_service = ChatService(db) return ApiResponse(data=chat_service.translate_source_language(dto, account_id)) @router.post("/message/translate-setting-language") def translate_setting_language( dto: TranslateTextDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """翻译成用户学习的语言""" chat_service = ChatService(db) return ApiResponse(data=chat_service.translate_setting_language(dto, account_id)) @router.get("/message/speech-content") def speech_content_api( content: str, session_id: str = None, speech_role_name: str = None, speech_role_style: str = None, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """消息转语音""" chat_service = ChatService(db) speech_result = chat_service.message_speech_content( TransformContentSpeechDTO( content=content, speech_role_name=speech_role_name, speech_role_style=speech_role_style, session_id=session_id ), account_id, ) """获取文件""" file_path = voice_file_get_path(speech_result["file"]) # 判断文件是否存在 with open(file_path, "rb") as file: contents = file.read() headers = { "Content-Type": "audio/wav", "Content-Disposition": "attachment", "filename": speech_result["file"], } return Response( content=contents, media_type="application/octet-stream", headers=headers ) @router.post("/message/grammar") def grammar_api( dto: GrammarDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """分析语法错误""" chat_service = ChatService(db) return ApiResponse(data=chat_service.grammar_analysis(dto, account_id)) # 进行发音评估 @router.post("/message/pronunciation") def pronunciation_api( dto: PronunciationDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """进行发单评估""" chat_service = ChatService(db) return ApiResponse(data=chat_service.pronunciation(dto, account_id)) # 进行音素级别的发音评估 # 获取单词的音标与翻译 @router.post("/message/word/detail") def get_word_api( dto: WordDetailDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取单词的音标与翻译""" chat_service = ChatService(db) return ApiResponse(data=chat_service.get_word(dto, account_id)) # 单词练习 @router.post("/message/word/practice") def word_practice_api( dto: WordPracticeDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """单词练习""" chat_service = ChatService(db) return ApiResponse(data=chat_service.word_practice(dto, account_id)) # 帮助用户生成提示句 @router.post("/message/prompt") def prompt_api( dto: PromptDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """帮助用户生成提示句""" chat_service = ChatService(db) return ApiResponse(data=chat_service.prompt_sentence(dto, account_id)) ================================================ FILE: talkieai-server/app/api/session_routes.py ================================================ from fastapi import APIRouter, Depends, Response from sqlalchemy.orm import Session from app.core import get_current_account from app.core.utils import * from app.db import get_db from app.models.account_models import * from app.models.response import ApiResponse from app.services.account_service import AccountService from app.services.chat_service import ChatService router = APIRouter() @router.get("/sessions/default") def get_default_session( db: Session = Depends(get_db), account_id: str = Depends(get_current_account) ): """获取默认会话""" chat_service = ChatService(db) return ApiResponse(data=chat_service.get_default_session(account_id)) @router.get("/sessions/{session_id}") def get_session( session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取会话详情""" chat_service = ChatService(db) return ApiResponse(data=chat_service.get_session(session_id, account_id)) @router.post("/sessions/{session_id}/voice-translate") def voice_upload_api( session_id: str, dto: VoiceTranslateDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """语音解析成文字""" chat_service = ChatService(db) return ApiResponse(data=chat_service.transform_text(session_id, dto, account_id)) # 获取ai的第一句问候语 @router.get("/sessions/{session_id}/greeting") def get_session_greeting( session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取会话消息""" chat_service = ChatService(db) return ApiResponse(data=chat_service.get_session_greeting(session_id, account_id)) @router.post("/sessions/{session_id}/chat") def chat_api( session_id: str, dto: ChatDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """发送消息""" chat_service = ChatService(db) return ApiResponse( data=chat_service.send_session_message(session_id, dto, account_id) ) # 删除最近俩次的对话 @router.delete("/sessions/{session_id}/messages/latest") def delete_latest_session_messages( session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """删除最近一次的对话""" chat_service = ChatService(db) return ApiResponse( data=chat_service.delete_latest_session_messages(session_id, account_id) ) # 删除session下所有的对话 @router.delete("/sessions/{session_id}/messages") def delete_all_session_messages( session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """删除最近一次的对话""" chat_service = ChatService(db) return ApiResponse( data=chat_service.delete_all_session_messages(session_id, account_id) ) ================================================ FILE: talkieai-server/app/api/sys_routes.py ================================================ import os from fastapi import APIRouter, Depends, Request, UploadFile, File, Response from sqlalchemy.orm import Session from app.core import get_current_account from app.db import get_db from app.models.sys_models import * from app.models.response import ApiResponse from app.services.sys_service import SysService from app.config import Config from app.core.utils import * router = APIRouter() @router.get("/languages/example") def get_settings_languages_example( language: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取支持的语言""" sys_service = SysService(db) return ApiResponse( data=sys_service.get_settings_languages_example(language, account_id) ) # 获取语言下支持的角色 @router.get("/sys/roles") def get_settings_roles( locale: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取支持的角色""" sys_service = SysService(db) return ApiResponse(data=sys_service.get_settings_roles(locale, account_id)) @router.get("/sys/languages") def get_settings_languages( db: Session = Depends(get_db), account_id: str = Depends(get_current_account) ): """获取支持的语言""" sys_service = SysService(db) return ApiResponse(data=sys_service.get_settings_languages(account_id)) @router.post("/voices/upload") def voice_upload_api( db: Session = Depends(get_db), file: UploadFile = File(...), account_id: str = Depends(get_current_account), ): """上传语音文件""" sys_service = SysService(db) return ApiResponse(data=sys_service.voice_upload(file, account_id)) @router.get("/voices/{file_name}") def get_file(file_name: str, response: Response): """获取文件""" file_path = voice_file_get_path(file_name) # 判断文件是否存在 if os.path.isfile(file_path): with open(file_path, "rb") as file: contents = file.read() response.headers["Content-Type"] = "application/octet-stream" response.headers[ "Content-Disposition" ] = f"attachment; filename={os.path.basename(file_path)}" return Response(content=contents, media_type="application/octet-stream") else: return {"error": f"File {file_name} not found."} @router.post("/sys/feedback") def add_feedback( dto: FeedbackDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """用户反馈""" sys_service = SysService(db) sys_service.add_feedback(dto, account_id) return ApiResponse(data="SUCCESS") ================================================ FILE: talkieai-server/app/api/topics_route.py ================================================ from fastapi import APIRouter, Depends, Response from sqlalchemy.orm import Session from app.core import get_current_account from app.db import get_db from app.models.topic_models import * from app.models.response import ApiResponse from app.core.logging import logging from app.services.topic_service import TopicService router = APIRouter() # 用户创建自定义话题 @router.post("/topics/custom", name="Create custom topic") def create_custom_topic( topic: TopicCreateDTO, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """用户创建自定义话题""" topic_service = TopicService(db) return ApiResponse(data=topic_service.create_custom_topic(topic, account_id)) # 获取用户创建的自定义话题 @router.get("/topics/custom", name="Get custom topic") def get_custom_topic( db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取用户创建的自定义话题""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_custom_topic(account_id)) # 获取所有话题组与话题 @router.get("/topics", name="Get all chat topic group") def get_all_chat_topics( type: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取所有话题组与话题""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_all_topics(type, account_id)) # 获取自定义话题的示例 @router.get("/topics/random", name="Get custom topic example") def get_custom_topic_example( db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取自定义话题的示例""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_custom_topic_example(account_id)) # 获取话题详情 @router.get("/topics/{topic_id}", name="Get topic detail") def get_topic_detail( topic_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取话题详情""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_topic_detail(topic_id, account_id)) # 获取话题历史记录,topic_id做为可选参数,为空时查询所有历史记录 @router.get("/topics/{topic_id}/history", name="Get chat topic history") def get_chat_topic_history( topic_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取话题历史记录,topic_id做为可选参数,为空时查询所有历史记录""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_topic_history(topic_id, account_id)) # 获取话题短语记录 @router.get("/topics/{topic_id}/phrases", name="Get chat topic phrases") def get_chat_topic_phrases( topic_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取话题短语记录""" topic_service = TopicService(db) return ApiResponse(data=topic_service.get_topic_phrases(topic_id, account_id)) # 基于主题创建一个session @router.post("/topics/{topic_id}/session", name="Create chat topic session") def create_chat_topic_session( topic_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """基于主题创建一个session""" topic_service = TopicService(db) return ApiResponse(data=topic_service.create_topic_session(topic_id, account_id)) # 结束当前会话并进行评分 @router.post("/topics/sessions/{session_id}/complete", name="Compete Session") def complete_session( session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """结束话题下的session""" topic_service = TopicService(db) return ApiResponse( data=topic_service.complete_topic_session(session_id, account_id) ) # 删除当前会话 @router.delete("/topics/{topic_id}/session/{session_id}", name="Delete Session") def delete_session( topic_id: str, session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """删除话题下的session""" topic_service = TopicService(db) return ApiResponse( data=topic_service.delete_topic_session(topic_id, session_id, account_id) ) # 获取话题下的session结果 @router.get( "/topics/{topic_id}/session/{session_id}/completion", name="Get Session Result" ) def get_session_result( topic_id: str, session_id: str, db: Session = Depends(get_db), account_id: str = Depends(get_current_account), ): """获取话题下的session结果""" topic_service = TopicService(db) return ApiResponse( data=topic_service.get_session_result(topic_id, session_id, account_id) ) ================================================ FILE: talkieai-server/app/config.py ================================================ import os from dotenv import load_dotenv load_dotenv() class Config: DEFAULT_SOURCE_LANGUAGE = 'zh-CN' DEFAULT_TARGET_LANGUAGE = 'en-US' AI_NAME = os.getenv('AI_NAME') # 数据库连接信息,需要判断不能为空 SQLALCHEMY_DATABASE_URL: str = os.getenv('DATABASE_URL') # 文件上传路径 TEMP_SAVE_FILE_PATH = os.getenv('TEMP_SAVE_FILE_PATH') # 微软语音 AZURE_KEY = os.getenv('AZURE_KEY') # AI AI_SERVER = os.getenv('AI_SERVER') # ChatGPT Key CHAT_GPT_PROXY = os.getenv('CHAT_GPT_PROXY') CHAT_GPT_KEY = os.getenv('CHAT_GPT_KEY') CHAT_GPT_MODEL = os.getenv('CHAT_GPT_MODEL') # 智谱AI配置 ZHIPU_AI_API_KEY = os.getenv('ZHIPU_AI_API_KEY') ZHIPU_AI_MODEL = os.getenv('ZHIPU_AI_MODEL') # WeChat WECHAT_APP_ID = os.getenv('WECHAT_APP_ID') WECHAT_APP_SECRET = os.getenv('WECHAT_APP_SECRET') WE_CHAT_SERVER_URL = os.getenv('WE_CHAT_SERVER_URL') # 是否开启SQL语句打印 SQL_ECHO: bool = os.getenv('SQL_ECHO').lower() == 'true' # JWT配置 TOKEN_SECRET = os.getenv('TOKEN_SECRET') ALGORITHM = 'HS256' DECODED_TOKEN_USER_KEY = "sub" DECODED_TOKEN_IAT_KEY = "iat" TOKEN_EXPIRE_TIME = int(os.getenv("TOKEN_EXPIRE_TIME")) # API前缀 API_PREFIX = os.getenv('API_PREFIX', '/api') ================================================ FILE: talkieai-server/app/core/__init__.py ================================================ from fastapi import Header from app.config import Config from app.core.auth import Auth auth = Auth(Config.TOKEN_SECRET, Config.ALGORITHM, Config.DECODED_TOKEN_IAT_KEY, Config.TOKEN_EXPIRE_TIME, Config.DECODED_TOKEN_USER_KEY) def get_current_account(x_token: str = Header(None), x_token_query: str = None): if x_token_query: return auth.get_current_account(x_token_query) return auth.get_current_account(x_token) ================================================ FILE: talkieai-server/app/core/auth.py ================================================ import time import jwt from fastapi import HTTPException class Auth: def __init__(self, token_secret: str, algorithm: str, decoded_token_iat_key: str, expire_time: int, decoded_token_user_key: str): self.token_secret = token_secret self.algorithm = algorithm self.expire_time = expire_time self.decoded_token_iat_key = decoded_token_iat_key self.decoded_token_user_key = decoded_token_user_key def init_token(self, name: str, id: str) -> str: return jwt.encode({ 'sub': id, 'iat': int(time.time()), 'name': name }, self.token_secret, algorithm=self.algorithm) def get_current_account(self, x_token: str) -> str: """Get user info from x_token""" if not x_token: raise HTTPException(status_code=401, detail="X-Token header is missing") try: decoded_token = jwt.decode(x_token, self.token_secret, algorithms=[self.algorithm]) except jwt.PyJWTError: print(jwt.PyJWTError) raise HTTPException(status_code=401, detail="Invalid token") # Check Whether the token expired iat = decoded_token.get(self.decoded_token_iat_key) """Check whether the token is expired""" delta = int((time.time() - iat) / 60) if delta > self.expire_time: raise HTTPException(status_code=401, detail="Token has expired") account_id = decoded_token.get(self.decoded_token_user_key) if not account_id: raise HTTPException(status_code=401, detail="User not found in token") return account_id ================================================ FILE: talkieai-server/app/core/azure_voice.py ================================================ import json import azure.cognitiveservices.speech as speechsdk from app.config import Config from app.core.logging import logging from app.core.language import * key = Config.AZURE_KEY region = "eastasia" speech_config = speechsdk.SpeechConfig(subscription=key, region=region) speech_synthesizer = speechsdk.SpeechSynthesizer( speech_config=speech_config, audio_config=None ) def speech_default(content: str, output_path_str: str, language: str, voice_name: str|None = None): """默认语音合成 还是用不了,因为每次还要实例化 speech_synthesizer""" speech_config.speech_recognition_language = language speech_config.speech_synthesis_language = language # 如果voice_name是空,则设置对应语言的默认角色 if not voice_name: voice_name = get_azure_language_default_role(language) speech_config.speech_synthesis_voice_name = voice_name speech_synthesis_result = speech_synthesizer.speak_text_async(content).get() audio_data_stream = speechsdk.AudioDataStream(speech_synthesis_result) if ( speech_synthesis_result.reason == speechsdk.ResultReason.SynthesizingAudioCompleted ): audio_data_stream.save_to_wav_file(output_path_str) elif ( speech_synthesis_result.reason == speechsdk.ResultReason.Canceled ): cancellation_details = speech_synthesis_result.cancellation_details logging.error( "Speech synthesis canceled: {}".format(cancellation_details.reason) ) if cancellation_details.reason == speechsdk.CancellationReason.Error: if cancellation_details.error_details: logging.error( "Error details: {}".format(cancellation_details.error_details) ) logging.error( "Did you set the speech resource key and region values?" ) raise Exception("语音合成失败") else: logging.error( "Speech synthesis failed: {}".format(speech_synthesis_result.reason) ) raise Exception("语音合成失败") def speech_by_ssml( content: str, output_path_str: str, voice_name: str, speech_rate: str, feel: str, targetLang: str, ): """可定制的文本转语音""" # 如果voice_name是空,则设置对应语言的默认角色 if not voice_name: voice_name = get_azure_language_default_role(targetLang) ssml = f""" {content} """ logging.info(ssml) speech_synthesis_result = speech_synthesizer.start_speaking_ssml_async( ssml ).get() # Get the audio data stream audio_data_stream = speechsdk.AudioDataStream(speech_synthesis_result) if ( speech_synthesis_result.reason == speechsdk.ResultReason.SynthesizingAudioStarted ): logging.info("init 1") audio_data_stream.save_to_wav_file(output_path_str) logging.info("init 2") else: logging.error( "Speech synthesis failed: {}".format(speech_synthesis_result.reason) ) if speech_synthesis_result.reason == speechsdk.ResultReason.Canceled: cancellation_details = speech_synthesis_result.cancellation_details logging.error( "Speech synthesis canceled: {}".format(cancellation_details.reason) ) if cancellation_details.reason == speechsdk.CancellationReason.Error: if cancellation_details.error_details: logging.error( "Error details: {}".format(cancellation_details.error_details) ) logging.error( "Did you set the speech resource key and region values?" ) raise Exception("语音合成失败") def speech_pronunciation(content: str, speech_path: str, language: str = "en-US"): """发音评估""" audio_config = speechsdk.audio.AudioConfig(filename=speech_path) speech_config.speech_recognition_language = language speech_recognizer = speechsdk.SpeechRecognizer( speech_config=speech_config, audio_config=audio_config ) # "{\"referenceText\":\"good morning\",\"gradingSystem\":\"HundredMark\",\"granularity\":\"Phoneme\",\"EnableMiscue\":true}" 通过dict生成json json_param = { "referenceText": content, "gradingSystem": "HundredMark", "granularity": "Word", "EnableMiscue": True, } pronunciation_assessment_config = speechsdk.PronunciationAssessmentConfig( json_string=json.dumps(json_param) ) pronunciation_assessment_config.apply_to(speech_recognizer) speech_recognition_result = speech_recognizer.recognize_once() pronunciation_assessment_result = speechsdk.PronunciationAssessmentResult( speech_recognition_result ) result = { "accuracy_score": pronunciation_assessment_result.accuracy_score, "fluency_score": pronunciation_assessment_result.fluency_score, "completeness_score": pronunciation_assessment_result.completeness_score, "pronunciation_score": pronunciation_assessment_result.pronunciation_score, } original_words = pronunciation_assessment_result.words result_words = [] # 循环words,获取每个单词的发音评估结果 for word in original_words: result_words.append( { "word": word.word, "accuracy_score": word.accuracy_score, "error_type": word.error_type, } ) result["words"] = result_words return result # 单词发单评估,可以精确到每一个音素 def word_speech_pronunciation(word: str, speech_path: str, language: str = "en-US"): audio_config = speechsdk.audio.AudioConfig(filename=speech_path) speech_config.speech_recognition_language = language speech_recognizer = speechsdk.SpeechRecognizer( speech_config=speech_config, audio_config=audio_config ) # "{\"referenceText\":\"good morning\",\"gradingSystem\":\"HundredMark\",\"granularity\":\"Phoneme\",\"EnableMiscue\":true}" 通过dict生成json json_param = { "referenceText": word, "gradingSystem": "HundredMark", "granularity": "Phoneme", "EnableMiscue": True, "phonemeAlphabet": "IPA", } pronunciation_assessment_config = speechsdk.PronunciationAssessmentConfig( json_string=json.dumps(json_param) ) pronunciation_assessment_config.apply_to(speech_recognizer) speech_recognition_result = speech_recognizer.recognize_once() pronunciation_assessment_result = speechsdk.PronunciationAssessmentResult( speech_recognition_result ) result = { "accuracy_score": pronunciation_assessment_result.accuracy_score, "fluency_score": pronunciation_assessment_result.fluency_score, "completeness_score": pronunciation_assessment_result.completeness_score, "pronunciation_score": pronunciation_assessment_result.pronunciation_score, } original_words = pronunciation_assessment_result.words result_words = [] # 循环words,获取每个单词的发音评估结果 for word in original_words: # 获取音素评估结果 phonemes = word.phonemes phonemes_list = [] for phoneme in phonemes: phonemes_list.append( { "phoneme": phoneme.phoneme, "accuracy_score": phoneme.accuracy_score } ) result_words.append( { "word": word.word, "accuracy_score": word.accuracy_score, "error_type": word.error_type, "phonemes": phonemes_list, } ) result["words"] = result_words return result # 语音转文字 def speech_translate_text(speech_path: str, language: str) -> str: # languages = ["zh-CN", "en-US"] languages = [] # 如果languages已经包含了language,就不需要再添加了,不包含需要添加,并且放在第一位 if language not in languages: languages.insert(0, language) auto_detect_source_language_config = ( speechsdk.languageconfig.AutoDetectSourceLanguageConfig(languages=languages) ) audio_config = speechsdk.audio.AudioConfig(filename=speech_path) speech_recognizer = speechsdk.SpeechRecognizer( speech_config=speech_config, auto_detect_source_language_config=auto_detect_source_language_config, audio_config=audio_config, ) speech_recognition_result = speech_recognizer.recognize_once_async().get() if speech_recognition_result.reason == speechsdk.ResultReason.RecognizedSpeech: print("Recognized: {}".format(speech_recognition_result.text)) return speech_recognition_result.text elif speech_recognition_result.reason == speechsdk.ResultReason.NoMatch: print( "No speech could be recognized: {}".format( speech_recognition_result.no_match_details ) ) elif speech_recognition_result.reason == speechsdk.ResultReason.Canceled: cancellation_details = speech_recognition_result.cancellation_details print("Speech Recognition canceled: {}".format(cancellation_details.reason)) if cancellation_details.reason == speechsdk.CancellationReason.Error: print("Error details: {}".format(cancellation_details.error_details)) print("Did you set the speech resource key and region values?") # 获取支持的语音列表,组装成对象数组进行返回 def get_voice_list(): """通过synthesizer.getVoicesAsync()方法来获取所有支持的语音列表""" speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config) voice_list = speech_synthesizer.get_voices_async().get() # 迭代list,组装成对象数组进行返回 voice_vo_list = [] for voice in voice_list.voices: voice_vo_list.append( { "gender": voice.gender.value, "locale": voice.locale, "local_name": voice.local_name, "name": voice.name, "short_name": voice.short_name, "voice_type": { "name": voice.voice_type.name, "value": voice.voice_type.value, }, "style_list": voice.style_list, } ) return voice_vo_list # 获取支持的语音列表,组装成对象数组进行返回 def get_voice_list(): """通过synthesizer.getVoicesAsync()方法来获取所有支持的语音列表""" speech_synthesizer = speechsdk.SpeechSynthesizer(speech_config=speech_config) voice_list = speech_synthesizer.get_voices_async().get() # 迭代list,组装成对象数组进行返回 voice_vo_list = [] for voice in voice_list.voices: voice_vo_list.append( { "gender": voice.gender.value, "locale": voice.locale, "local_name": voice.local_name, "name": voice.name, "short_name": voice.short_name, "voice_type": { "name": voice.voice_type.name, "value": voice.voice_type.value, }, "style_list": voice.style_list, } ) return voice_vo_list voice_vo_list = get_voice_list() # 获取azure语音配置,并且按 locale 分组 azure_voice_configs = voice_vo_list azure_voice_configs_group = {} for azure_voice_config in azure_voice_configs: if azure_voice_config["locale"] not in azure_voice_configs_group: azure_voice_configs_group[azure_voice_config["locale"]] = [] azure_voice_configs_group[azure_voice_config["locale"]].append(azure_voice_config) def get_azure_voice_role_by_short_name(short_name: str): """根据short_name获取语音配置""" local = short_name.rsplit('-', 1)[0] azure_voice_configs = azure_voice_configs_group[local] # 迭代azure_voice_configs,找到item中short_name与settings.speech_role_name相同的item,取local_name result = None for item in azure_voice_configs: if item["short_name"] == short_name: return item # 抛出异常 raise Exception("未找到对应的语音配置") ================================================ FILE: talkieai-server/app/core/db_cache.py ================================================ ================================================ FILE: talkieai-server/app/core/exceptions.py ================================================ # 用户资源访问受限exception class UserAccessDeniedException(Exception): pass # 用户密码不正确 class UserPasswordIncorrectException(Exception): pass # 参数不正确 class ParameterIncorrectException(Exception): pass ================================================ FILE: talkieai-server/app/core/language.py ================================================ import json from app.core.logging import logging language_data = [] azure_data = {} with open("data/azure.json", "r") as f: azure_data = json.load(f) azure_style_label_data = [] with open("data/azure_style_label.json", "r") as f: azure_style_label_data = json.load(f) azure_style_label_map = {} for item in azure_style_label_data: azure_style_label_map[item["value"]] = item["label"] sys_language_data = {} with open("data/sys_language.json", "r") as f: sys_language_data = json.load(f) def get_label_by_language(language: str) -> str: """根据语言获取对应的label""" for item in sys_language_data: if item["value"] == language: return item["label"] raise Exception("没有找到对应的语言:{language}") def get_azure_style_label(style: str): """根据style获取对应的label""" # 检查azure_style_label_map是否包含style if style in azure_style_label_map: return azure_style_label_map[style] logging.warning(f"没有找到对应的style:{style}") return "" def get_azure_language_default_role(language: str): """根据语言获取默认的角色""" for item in sys_language_data: if item["value"] == language: return item["default_voice_role_name"] raise Exception(f"没有找到对应的语言:{language}") def get_role_info_by_short_name(short_name: str): """根据short_name获取角色信息""" for item in sys_language_data: if item["short_name"] == short_name: return item raise Exception(f"没有找到对应的角色:{short_name}") ================================================ FILE: talkieai-server/app/core/logging.py ================================================ import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') ================================================ FILE: talkieai-server/app/core/utils.py ================================================ import hashlib import os import shutil import string import time from datetime import datetime, timedelta from uuid import UUID import random import jwt from fastapi import UploadFile from app.config import Config def short_uuid() -> str: """64-bit characters reduced to 8-bit characters. link https://blog.csdn.net/dqchouyang/article/details/70230863 """ uuidChars = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") uuid = str(uuid4()).replace('-', '') result = '' for i in range(0, 8): sub = uuid[i * 4: i * 4 + 4] x = int(sub, 16) result += uuidChars[x % 0x3E] return result def uuid4(): """Generate a random UUID.""" return UUID(bytes=os.urandom(16), version=4) def digest_password(password: str): """Sha256 digest password""" return hashlib.sha256(password.encode('utf-8')).hexdigest() def generate_code(): """Generate a 4 random letter verification code""" code = ''.join(random.sample(string.digits, 6)) return code # 日期转换成 年-月-日 时:分:秒 def date_to_str(date): return date.strftime("%Y-%m-%d %H:%M:%S") def day_to_str(date): return date.strftime("%Y-%m-%d 00:00:00") def friendly_time(dt): """ 将日期时间字符串转换为友好的时间差表达方式。 参数: dt -- 日期时间字符串,格式为YYYY-MM-DD HH:MM:SS 返回值: 友好的时间差表达方式,例如:1分钟前、1小时前、1天前等等。 """ now = datetime.now() then = datetime.strptime(dt, '%Y-%m-%d %H:%M:%S') diff = now - then if diff < timedelta(seconds=60): return '刚刚' elif diff < timedelta(minutes=1): return f'{diff.seconds}秒前' elif diff < timedelta(hours=1): return f'{diff.seconds // 60}分钟前' elif diff < timedelta(days=1): return f'{diff.seconds // 3600}小时前' elif diff < timedelta(days=30): return f'{diff.days}天前' elif diff < timedelta(days=365): return f'{diff.days // 30}个月前' else: return f'{diff.days // 365}年前' def save_file(upload_file: UploadFile) -> str: # 获取upload_file文件后缀名 file_ext = get_file_ext(upload_file.filename) filename = f'{short_uuid()}{file_ext}' file_path = f'{Config.TEMP_SAVE_FILE_PATH}/{filename}' if not os.path.exists(Config.TEMP_SAVE_FILE_PATH): os.makedirs(Config.TEMP_SAVE_FILE_PATH) with open(file_path, 'wb') as buffer: shutil.copyfileobj(upload_file.file, buffer) return filename # def save_voice_file(upload_file: UploadFile) -> str: # # 获取upload_file文件后缀名 # file_ext = get_file_ext(upload_file.filename) # filename = f'{short_uuid()}{file_ext}' # file_path = voice_file_get_path(filename) # if not os.path.exists(Config.TEMP_SAVE_FILE_PATH): # os.makedirs(Config.TEMP_SAVE_FILE_PATH) # with open(file_path, 'wb') as buffer: # shutil.copyfileobj(upload_file.file, buffer) # return filename def file_get_path(filename: str) -> str: return f'{Config.TEMP_SAVE_FILE_PATH}/{filename}' # 获取文件的后缀名 def get_file_ext(filename: str) -> str: return os.path.splitext(filename)[1] # 获取年月日 yyyymmdd格式 def get_date_str(): return time.strftime("%Y%m%d", time.localtime()) def save_image_file(upload_file: UploadFile) -> str: # 获取upload_file文件后缀名 file_ext = get_file_ext(upload_file.filename) filename = f'{short_uuid()}{file_ext}' file_full_path = image_file_get_path(filename) # 检查文件的目录是否存在,如果不存在就创建 if not os.path.exists(os.path.dirname(file_full_path)): os.makedirs(os.path.dirname(file_full_path)) with open(file_full_path, "wb") as buffer: shutil.copyfileobj(upload_file.file, buffer) return filename def save_voice_file(upload_file: UploadFile, prefix='') -> str: # 获取upload_file文件后缀名 file_ext = get_file_ext(upload_file.filename) filename = f'{prefix}_{short_uuid()}{file_ext}' file_full_path = voice_file_get_path(filename) # 检查文件的目录是否存在,如果不存在就创建 if not os.path.exists(os.path.dirname(file_full_path)): os.makedirs(os.path.dirname(file_full_path)) with open(file_full_path, "wb") as buffer: shutil.copyfileobj(upload_file.file, buffer) return filename def voice_file_get_path(filename: str) -> str: result = f"{Config.TEMP_SAVE_FILE_PATH}/voices/{filename}" # 检查full_file_name文件的所属目录是否存在,不存在则创建新的目录 if not os.path.exists(os.path.dirname(result)): os.makedirs(os.path.dirname(result)) return result def image_file_get_path(filename: str) -> str: return f"{Config.TEMP_SAVE_FILE_PATH}/images/{filename}" # 获取文件的后缀名 def get_file_ext(filename: str) -> str: return os.path.splitext(filename)[1] ================================================ FILE: talkieai-server/app/db/__init__.py ================================================ from sqlalchemy import create_engine, event from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from app.config import Config from sqlalchemy.exc import DisconnectionError def checkout_listener(dbapi_con, con_record, con_proxy): try: try: dbapi_con.ping(False) except TypeError: dbapi_con.ping() except dbapi_con.OperationalError as exc: if exc.args[0] in (2006, 2013, 2014, 2045, 2055): raise DisconnectionError() else: raise # 创建数据库连接, SQLALCHEMY_DATABASE_URL不能为空 if not Config.SQLALCHEMY_DATABASE_URL: raise Exception('SQLALCHEMY_DATABASE_URL不能为空') engine = create_engine(Config.SQLALCHEMY_DATABASE_URL, echo=Config.SQL_ECHO, pool_pre_ping=True, pool_size=100, pool_recycle=360) event.listen(engine, 'checkout', checkout_listener) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) # 创建基类 Base = declarative_base() # 数据库会话 def get_db(): db = SessionLocal() try: yield db finally: db.close() ================================================ FILE: talkieai-server/app/db/account_entities.py ================================================ import datetime from sqlalchemy import Column, String, DateTime, Integer, Index, Text from app.db import Base, engine class AccountEntity(Base): """访客表""" __tablename__ = "account" id = Column("id", String(80), primary_key=True) client_host = Column("client_host", String(50), nullable=False) user_agent = Column("user_agent", String(512), nullable=True) fingerprint = Column("fingerprint", String(64), nullable=True) status = Column("status", String(50), default="ACTIVE") create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class AccountSettingsEntity(Base): """用户设置表""" __tablename__ = "account_settings" id = Column("id", Integer, primary_key=True, autoincrement=True) account_id = Column("account_id", String(80), nullable=False) source_language = Column("source_language", String(80), nullable=False) target_language = Column("target_language", String(80), nullable=False) speech_role_name = Column("speech_role_name", String(80), nullable=True) auto_playing_voice = Column("auto_playing_voice", Integer, default=1) playing_voice_speed = Column("playing_voice_speed", String(50), default='1.0') auto_text_shadow = Column("auto_text_shadow", Integer, default=1) auto_pronunciation = Column("auto_pronunciation", Integer, default=1) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class AccountCollectEntity(Base): """用户收藏表""" __tablename__ = "account_collect" id = Column("id", Integer, primary_key=True, autoincrement=True) message_id = Column("message_id", String(80), nullable=True) account_id = Column("account_id", String(80), nullable=False) type = Column("type", String(80), nullable=False) content = Column("content", String(2500), nullable=True) translation = Column("translation", String(2500), nullable=True) deleted = Column("deleted", Integer, default="0") create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # 数据库未创建表的话自动创建表 Base.metadata.create_all(engine) ================================================ FILE: talkieai-server/app/db/chat_entities.py ================================================ import datetime from sqlalchemy import Column, String, DateTime, Integer, Index, Text from app.db import Base, engine class MessageSessionEntity(Base): """消息会话表""" __tablename__ = "message_session" id = Column("id", String(80), primary_key=True) account_id = Column("account_id", String(80), nullable=False) # CHAT TOPIC type = Column("type", String(50), nullable=False, default="CHAT") message_count = Column("message_count", Integer, default="0") is_default = Column("is_default", Integer, default="0") completed = Column("completed", Integer, default="0") deleted = Column("deleted", Integer, default="0") create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class MessageEntity(Base): """消息表""" __tablename__ = "message" id = Column("id", String(80), primary_key=True) session_id = Column("session_id", String(80), nullable=False) account_id = Column("account_id", String(80), nullable=False) sender = Column("sender", String(80), nullable=False) receiver = Column("receiver", String(80), nullable=False) type = Column("type", String(50), nullable=False) content = Column("content", String(2500), nullable=False) style = Column("style", String(80), nullable=True) length = Column("length", Integer, nullable=False) file_name = Column("file_name", String(80), nullable=True) create_time = Column("create_time", DateTime, default=datetime.datetime.now) deleted = Column("deleted", Integer, default="0") # 设置一个自增的sequence 排序使用 sequence = Column("sequence", Integer, nullable=False) # session_id需要加索引 Index("idx_session_id", "session_id") # account_id需要加索引 Index("idx_account_id", "account_id") # sequence 需要加索引 Index("idx_sequence", "sequence") class MessageTranslateEntity(Base): """用户翻译记录表, 使用自增id""" __tablename__ = "message_translate" id = Column("id", Integer, primary_key=True, autoincrement=True) session_id = Column("session_id", String(80), nullable=False) message_id = Column("message_id", String(80), nullable=False) account_id = Column("account_id", String(80), nullable=False) source_language = Column("source_language", String(80), nullable=False) target_language = Column("target_language", String(80), nullable=False) source_text = Column("source_text", String(512), nullable=False) target_text = Column("target_text", String(512), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) class MessageGrammarEntity(Base): """用户语法与语音分析表,结果使用json保存""" __tablename__ = "message_grammar" id = Column("id", Integer, primary_key=True, autoincrement=True) session_id = Column("session_id", String(80), nullable=False) message_id = Column("message_id", String(80), nullable=False) file_name = Column("file_name", String(80), nullable=True) account_id = Column("account_id", String(80), nullable=False) # GRAMMAR PRONUNCIATION type = Column("type", String(80), nullable=False) result = Column("result", Text, nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) # 数据库未创建表的话自动创建表 Base.metadata.create_all(engine) ================================================ FILE: talkieai-server/app/db/sys_entities.py ================================================ import datetime from sqlalchemy import Column, String, DateTime, Integer, Index, Text from app.db import Base, engine class SettingsLanguageEntity(Base): """会话语言配置表""" __tablename__ = "settings_language" id = Column("id", String(80), primary_key=True) language = Column("language", String(80), nullable=False) full_language = Column("full_language", String(80), nullable=False) label = Column("label", String(80), nullable=False) full_label = Column("full_label", String(80), nullable=False) description = Column("description", String(250), nullable=True) sequence = Column("sequence", Integer, default="1") create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class SettingsLanguageExampleEntity(Base): """会话示例配置表""" __tablename__ = "settings_language_example" id = Column("id", String(80), primary_key=True) language = Column("language", String(80), nullable=False) example = Column("example", String(250), nullable=True) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # 会话角色配置表 class SettingsRoleEntity(Base): """会话角色配置表""" __tablename__ = "settings_role" id = Column("id", Integer, primary_key=True, autoincrement=True) locale = Column("locale", String(80), nullable=False) local_name = Column("local_name", String(80), nullable=False) name = Column("name", String(255), nullable=False) short_name = Column("short_name", String(80), nullable=False) gender = Column("gender", Integer, nullable=False, default=1) # 头像地址 avatar = Column("avatar", String(350), nullable=True) # 试听音频地址 audio = Column("audio", String(350), nullable=True) styles = Column("styles", String(350), nullable=True) status = Column("status", String(80), nullable=False, default="ACTIVE") # 排序 sequence = Column("sequence", Integer, nullable=False, default=0) # 创建时间 create_time = Column( "create_time", DateTime, nullable=False, default=datetime.datetime.now ) # 更新时间 update_time = Column( "update_time", DateTime, nullable=False, default=datetime.datetime.now ) deleted = Column("deleted", Integer, nullable=False, default=0) class FileDetail(Base): """文件表""" __tablename__ = "file_detail" id = Column("id", String(80), primary_key=True) file_path = Column("file_path", String(150), nullable=False) module = Column("module", String(80), nullable=True) module_id = Column("module_id", String(80), nullable=True) file_name = Column("file_name", String(150), nullable=True) file_ext = Column("file_ext", String(20), nullable=True) deleted = Column("deleted", Integer, default="0") created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) class SysCacheEntity(Base): """系统缓存表""" __tablename__ = "sys_cache" id = Column("id", Integer, primary_key=True, autoincrement=True) key = Column("key", String(80), nullable=False) value = Column("value", String(512), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class FeedbackEntity(Base): """用户反馈表""" __tablename__ = "sys_feedback" id = Column("id", Integer, autoincrement=True, primary_key=True) account_id = Column("account_id", String(80), nullable=False) content = Column("content", String(2500), nullable=False) contact = Column("contact", String(250), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) class SysDictTypeEntity(Base): """系统字典类型表""" __tablename__ = "sys_dict_type" id = Column("id", Integer, autoincrement=True, primary_key=True) dict_type = Column("dict_type", String(80), nullable=False) dict_name = Column("dict_name", String(80), nullable=False) status = Column("status", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class SysDictDataEntity(Base): """系统字典数据表""" __tablename__ = "sys_dict_data" id = Column("id", Integer, autoincrement=True, primary_key=True) dict_type = Column("dict_type", String(80), nullable=False) dict_label = Column("dict_label", String(80), nullable=False) dict_value = Column("dict_value", String(80), nullable=False) status = Column("status", String(80), nullable=False, default="1") create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # 数据库未创建表的话自动创建表 Base.metadata.create_all(engine) ================================================ FILE: talkieai-server/app/db/topic_entities.py ================================================ import datetime from sqlalchemy import Column, String, DateTime, Integer, Index, Text from app.db import Base, engine # 聊天话题组表 class TopicGroupEntity(Base): """聊天话题组表""" __tablename__ = "topic_group" id = Column("id", String(80), primary_key=True) # ROLE_PLAY CHAT_TOPIC type = Column("type", String(80), nullable=False) name = Column("name", String(80), nullable=False) description = Column("description", String(80), nullable=False) status = Column("status", String(80), nullable=False, default="ACTIVE") sequence = Column("sequence", Integer, nullable=False, default=1) created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) class TopicEntity(Base): """聊天话题表""" __tablename__ = "topic" id = Column("id", String(80), primary_key=True) # 所属组 group_id = Column("group_id", String(80), nullable=False) language = Column("language", String(80), nullable=False) name = Column("name", String(80), nullable=False) level = Column("level", Integer, nullable=False) # 所属角色 role_short_name = Column("role_short_name", String(80), nullable=False) # 角色语音默认速度 role_speech_rate = Column("speech_rate", String(80), nullable=False, default="1") topic_bot_name = Column("topic_bot_name", String(580), nullable=False) topic_user_name = Column("topic_user_name", String(580), nullable=False) prompt = Column("prompt", String(2500), nullable=False) description = Column("description", String(580), nullable=False) status = Column("status", String(80), nullable=False, default="ACTIVE") sequence = Column("sequence", Integer, nullable=False, default=1) image_url = Column("image_url", String(500), nullable=True) created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # created_by 增加搜索索引 created_by_index = Index("created_by_index", created_by) # group_id 增加搜索索引 group_id_index = Index("group_id_index", group_id) class TopicSessionRelation(Base): """话题与session关系表""" __tablename__ = "topic_session_relation" id = Column("id", Integer, primary_key=True, autoincrement=True) # 所属话题 topic_id = Column("topic_id", String(80), nullable=False) # 所属session_id session_id = Column("session_id", String(80), nullable=False) account_id = Column("account_id", String(80), nullable=False) # 创建时间 create_time = Column("create_time", DateTime, default=datetime.datetime.now) # 更新时间 update_time = Column("update_time", DateTime, default=datetime.datetime.now) # topic_id 增加搜索索引 topic_id_index = Index("topic_id_index", topic_id) # session_id 增加搜索索引 session_id_index = Index("session_id_index", session_id) class AccountTopicEntity(Base): """ 用户创建的话题表 """ __tablename__ = "account_topic" id = Column("id", String(80), primary_key=True) language = Column("language", String(80), nullable=False) ai_role = Column("ai_role", String(280), nullable=False) my_role = Column("my_role", String(280), nullable=False) topic = Column("topic", String(2500), nullable=False) account_id = Column("account_id", String(80), nullable=False) created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) sequence = Column("sequence", Integer, nullable=False, default=1) # 话题目标表 class TopicTargetEntity(Base): """话题目标表""" __tablename__ = "topic_target" id = Column("id", Integer, primary_key=True, autoincrement=True) # 所属话题 topic_id = Column("topic_id", String(80), nullable=False) # 目标类型 MAIN TRIAL type = Column("type", String(80), nullable=False) # 目标描述 description = Column("description", String(500), nullable=False) # 目标描述的翻译 description_translation = Column("description_translation", String(500), nullable=True) # 目标状态 status = Column("status", String(80), nullable=False, default="ACTIVE") sequence = Column("sequence", Integer, nullable=False, default=1) created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # topic_id 增加搜索索引 topic_id_index = Index("topic_id_index", topic_id) # 话题短语 class TopicPhraseEntity(Base): """话题短语""" __tablename__ = "topic_phrase" id = Column("id", Integer, primary_key=True, autoincrement=True) # 所属话题 topic_id = Column("topic_id", String(80), nullable=False) # 短语 phrase = Column("phrase", String(500), nullable=False) # 短语翻译 phrase_translation = Column("phrase_translation", String(500), nullable=True) # 短语类型 type = Column("type", String(80), nullable=False) # 短语状态 status = Column("status", String(80), nullable=False, default="ACTIVE") sequence = Column("sequence", Integer, nullable=False, default=1) created_by = Column("created_by", String(80), nullable=False) create_time = Column("create_time", DateTime, default=datetime.datetime.now) update_time = Column("update_time", DateTime, default=datetime.datetime.now) # topic_id 增加搜索索引 topic_id_index = Index("topic_id_index", topic_id) class TopicHistoryEntity(Base): """话题历史记录表""" __tablename__ = "topic_history" id = Column("id", Integer, primary_key=True, autoincrement=True) account_id = Column("account_id", String(80), nullable=False) # 所属话题 topic_id = Column("topic_id", String(80), nullable=False) # 话题类型 topic_type = Column("topic_type", String(80), nullable=False) # 话题名 topic_name = Column("topic_name", String(80), nullable=False) # 主目标数量 main_target_count = Column("main_target_count", Integer, nullable=False, default=0) # 试验目标数量 trial_target_count = Column( "trial_target_count", Integer, nullable=False, default=0 ) main_target_completed_count = Column( "main_target_completed_count", Integer, nullable=False, default=0 ) trial_target_completed_count = Column( "trial_target_completed_count", Integer, nullable=False, default=0 ) # 完成度 completion = Column("completion", Integer, nullable=False, default=0) # 语音评分 audio_score = Column("audio_score", Integer, nullable=True, default=0) # 内容评分 content_score = Column("content_score", Integer, nullable=True, default=0) # 建议 suggestion = Column("suggestion", String(2080), nullable=True) word_count = Column("word_count", Integer, nullable=True, default=0) # 所属session_id session_id = Column("session_id", String(80), nullable=False) completed = Column("completed", String(80), nullable=False, default="0") status = Column("status", String(80), nullable=False, default="ACTIVE") # 创建时间 create_time = Column("create_time", DateTime, default=datetime.datetime.now) # 数据库未创建表的话自动创建表 Base.metadata.create_all(engine) ================================================ FILE: talkieai-server/app/main.py ================================================ from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse from app.config import Config from app.core.exceptions import UserAccessDeniedException from app.core.logging import logging from app.models.response import ApiResponse from app.api.sys_routes import router as sys_routes from app.api.account_routes import router as account_routes from app.api.message_routes import router as message_routes from app.api.session_routes import router as session_routes from app.api.topics_route import router as topic_routes app = FastAPI() # Enables CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(account_routes, prefix=f"{Config.API_PREFIX}/v1") app.include_router(topic_routes, prefix=f"{Config.API_PREFIX}/v1") app.include_router(sys_routes, prefix=f"{Config.API_PREFIX}/v1") app.include_router(session_routes, prefix=f"{Config.API_PREFIX}/v1") app.include_router(message_routes, prefix=f"{Config.API_PREFIX}/v1") @app.exception_handler(Exception) async def conflict_error_handler(_, exc: Exception): """全局异常处理""" logging.error(exc) # 返回状态码仍为200,exc的错误信息放到ApiResponse中以json格式方式返回并且可以跨域访问 return JSONResponse( headers={ "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "*", "Access-Control-Allow-Headers": "*", }, content=ApiResponse(code="500", status="FAILED", message=str(exc)).__dict__, ) # UserAccessDeniedException异常处理状态码为403 @app.exception_handler(UserAccessDeniedException) async def user_access_denied_error_handler(_, exc: UserAccessDeniedException): """全局异常处理""" logging.error(exc) # 返回状态码仍为200,exc的错误信息放到ApiResponse中以json格式方式返回并且可以跨域访问 return JSONResponse( headers={ "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "*", "Access-Control-Allow-Headers": "*", }, content=ApiResponse(code="403", status="FAILED", message=str(exc)).__dict__, ) ================================================ FILE: talkieai-server/app/models/__init__.py ================================================ ================================================ FILE: talkieai-server/app/models/account_models.py ================================================ from enum import Enum from typing import List, Dict from pydantic import BaseModel, constr class MessageType(Enum): """消息类型""" ACCOUNT = "ACCOUNT" SYSTEM = "SYSTEM" class WechatLoginDTO(BaseModel): code: str = None state: str = None class VisitorLoginDTO(BaseModel): fingerprint: constr(min_length=15) class ChatDTO(BaseModel): """聊天""" message: str | None = None file_name: str | None = None class MessagePracticeDTO(BaseModel): """句子练习""" file_name: str = None class TransformSpeechDTO(BaseModel): """消息转语音""" message_id: constr(min_length=5) class VoiceTranslateDTO(BaseModel): """语音转文字""" file_name: constr(min_length=1) class TranslateDTO(BaseModel): """翻译""" message_id: constr(min_length=1) class TranslateTextDTO(BaseModel): """翻译""" text: constr(min_length=1) session_id: str = None class GrammarDTO(BaseModel): """分析英文的语法错误""" message_id: constr(min_length=1) class WordDetailDTO(BaseModel): """单词详情""" word: constr(min_length=1) class WordPracticeDTO(BaseModel): """单词练习""" session_id: str = None word: constr(min_length=1) file_name: constr(min_length=1) class CollectDTO(BaseModel): """收藏单词或者句子""" type: constr(min_length=1) message_id: str = None content: str = None class PromptDTO(BaseModel): """帮助用户生成提示句""" session_id: constr(min_length=1) class AccountSettingsDTO(BaseModel): auto_playing_voice: bool = True playing_voice_speed: str = "1.0" auto_text_shadow: bool = True auto_pronunciation: bool = True class CreateSessionDTO(BaseModel): role_name: str class UpdateRoleDTO(BaseModel): language: str role_name: str style: str = None avatar: str local_name: str class UpdateLanguageDTO(BaseModel): language: constr(min_length=1) class AccountSettingsDTO(BaseModel): target_language: str | None = None speech_role_name: str | None = None auto_playing_voice: int = 1 playing_voice_speed: str = "1.0" auto_text_shadow: int = 1 auto_pronunciation: int = 1 ================================================ FILE: talkieai-server/app/models/chat_models.py ================================================ from enum import Enum from typing import List, Dict from pydantic import BaseModel, constr class MessageType(Enum): """消息类型""" ACCOUNT = "ACCOUNT" SYSTEM = "SYSTEM" # 智谱AI的第一句提示词 PROMPT = "PROMPT" class CreateTalkSessionDTO(BaseModel): language: str short_name: str style: str = "" class CreateSessionDTO(BaseModel): topic_id: str class ChatDTO(BaseModel): """聊天""" message: str = None file_name: str = None class MessagePracticeDTO(BaseModel): """句子练习""" file_name: str = None class TransformSpeechDTO(BaseModel): """消息转语音""" message_id: constr(min_length=5) class VoiceTranslateDTO(BaseModel): """语音转文字""" file_name: constr(min_length=1) class TranslateDTO(BaseModel): """翻译""" message_id: constr(min_length=1) class TranslateTextDTO(BaseModel): """翻译""" text: constr(min_length=1) class TransformContentSpeechDTO(BaseModel): """内容转语音""" session_id: str | None = None content: constr(max_length=500) speech_role_name: str | None = None speech_role_style: str | None = None speech_style: str = "" speech_rate: str = "1.0" class GrammarDTO(BaseModel): """分析英文的语法错误""" message_id: constr(min_length=1) class PronunciationDTO(BaseModel): """语音评估""" message_id: constr(min_length=1) class WordDetailDTO(BaseModel): """单词详情""" language: str = "en-US" session_id: str = None word: constr(min_length=1) class WordPracticeDTO(BaseModel): """单词练习""" session_id: str = None word: constr(min_length=1) file_name: constr(min_length=1) class PromptDTO(BaseModel): """帮助用户生成提示句""" session_id: constr(min_length=1) ================================================ FILE: talkieai-server/app/models/response.py ================================================ class ApiResponse: def __init__(self, code: str = '200', status: str = 'SUCCESS', data=None, message: str = 'success'): self.code = code self.status = status self.data = data self.message = message ================================================ FILE: talkieai-server/app/models/sys_models.py ================================================ from enum import Enum from typing import List, Dict from pydantic import BaseModel, constr class UpdateLanguageDTO(BaseModel): language: constr(min_length=1) class FeedbackDTO(BaseModel): content: constr(min_length=1) contact: str = None ================================================ FILE: talkieai-server/app/models/topic_models.py ================================================ from enum import Enum from typing import List, Dict from pydantic import BaseModel, constr class CreateSessionDTO(BaseModel): topic_id: str class TopicCreateDTO(BaseModel): ai_role: str my_role: str topic: str ================================================ FILE: talkieai-server/app/services/__init__.py ================================================ from app.services.sys_service import SysService from app.services.account_service import AccountService from app.services.chat_service import ChatService from app.services.topic_service import TopicService from app.db import SessionLocal # 检查初始化数据 db = SessionLocal() sys_service = SysService(db) account_service = AccountService(db) topic_service = TopicService(db) chat_service = ChatService(db) db.close() ================================================ FILE: talkieai-server/app/services/account_service.py ================================================ import json import os import re import datetime from sqlalchemy.orm import Session from app.config import Config from app.core import auth, azure_voice from app.core.azure_voice import * from app.core.exceptions import UserAccessDeniedException from app.core.utils import * from app.db.sys_entities import * from app.db.account_entities import * from app.db.chat_entities import * from app.models.account_models import * from app.core.logging import logging from app.ai import chat_ai from app.ai.models import * from app.core.logging import logging from app.core.language import * from app.core.language import * MESSAGE_SYSTEM = "SYSTEM" class AccountService: def __init__(self, db: Session): self.db = db def visitor_login(self, fingerprint: str, client_host: str, user_agent: str = None): """先检查此ip下是否有用户,如果有,直接返回ip下的用户,如果没有,就生成新的访客""" visitor = ( self.db.query(AccountEntity).filter_by(fingerprint=fingerprint).first() ) if not visitor: visitor = AccountEntity( id=f"visitor_{short_uuid()}", fingerprint=fingerprint, client_host=client_host, user_agent=user_agent, ) self.db.add(visitor) self.db.commit() self.__check_and_init_default_settings(visitor.id) return auth.init_token(visitor.id, visitor.id) def collect(self, dto: CollectDTO, account_id: str): """用户收藏,根据类型保存到AccountCollectEntity,保存前先做检查,如果已经存在,则不需要再进行保存""" # 先检查是否已经存在,如果已经存在,就不需要再进行保存 if dto.message_id: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, message_id=dto.message_id) .first() ) else: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, type=dto.type, content=dto.content) .first() ) if collect: if collect.deleted == 1: collect.deleted = 0 collect.update_time = datetime.datetime.now() self.db.commit() return # 查询出session if dto.message_id: message = ( self.db.query(MessageEntity) .filter_by(id=dto.message_id, account_id=account_id) .first() ) content = message.content else: content = dto.content # 获得翻译 source_language = self.get_account_source_language(account_id) translation = chat_ai.invoke_translate( TranslateParams(target_language=source_language, content=content) ) # 如果没有任何符号且只有单独一个单词,则type为WORD,否则为 SENTENCE if re.match(r"^[a-zA-Z]+$", content) and len(content.split(" ")) == 1: type = "WORD" else: type = "SENTENCE" account_collect = AccountCollectEntity( account_id=account_id, type=type, message_id=dto.message_id, content=content, translation=translation, ) self.db.add(account_collect) self.db.commit() return def get_account_info(self, account_id: str): """获取用户的今日聊天次数与总次数返回""" # 如果是访客,就返回访客的信息 if account_id.startswith("visitor_"): account = self.db.query(AccountEntity).filter_by(id=account_id).first() else: # 不再支持account raise Exception("不再支持account") if not account: raise Exception("User not found") result = { "account_id": account_id, "today_chat_count": self.get_user_current_day_system_message_count( account_id ), "total_chat_count": self.get_user_system_message_count(account_id), } account_settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) target_language = account_settings.target_language result["target_language"] = target_language result["target_language_label"] = get_label_by_language(target_language) return result def get_collect(self, dto: CollectDTO, account_id: str): """获取用户是否已经收藏的数据""" if dto.message_id: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, message_id=dto.message_id) .first() ) else: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, type=dto.type, content=dto.content) .first() ) if collect and collect.deleted == 0: return {"is_collect": True} else: return {"is_collect": False} def cancel_collect(self, dto: CollectDTO, account_id: str): """取消收藏""" if dto.message_id: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, message_id=dto.message_id) .first() ) else: collect = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, type=dto.type, content=dto.content) .first() ) if collect: collect.deleted = 1 collect.update_time = datetime.datetime.now() self.db.commit() return def get_collects(self, type: str, page: int, page_size: int, account_id: str): """获取用户收藏的列表信息""" query = ( self.db.query(AccountCollectEntity) .filter_by(account_id=account_id, type=type, deleted=0) .order_by(AccountCollectEntity.create_time.desc()) ) collects = query.offset((page - 1) * page_size).limit(page_size).all() # 获取总数 total = query.count() result = [] for collect in collects: result.append( { "id": collect.id, "type": collect.type, "content": collect.content, "translation": collect.translation, "message_id": collect.message_id, "create_time": date_to_str(collect.create_time), } ) return {"total": total, "list": result} def get_settings(self, account_id: str): """获取AccountSettingsEntity中key 为 auto_playing_voice, playing_voice_speed, auto_text_shadow, auto_pronunciation的配置""" settings = ( self.db.query(AccountSettingsEntity) .filter(AccountSettingsEntity.account_id == account_id) .first() ) # 设置 vo dict,里面的值与settings中的值一致 vo = { "auto_playing_voice": settings.auto_playing_voice, "playing_voice_speed": settings.playing_voice_speed, "auto_text_shadow": settings.auto_text_shadow, "auto_pronunciation": settings.auto_pronunciation, "speech_role_name": settings.speech_role_name, "target_language": settings.target_language, } # 如果存在 speech_role_name,则从azure_voice_configs_group获取对应值,取local_name if settings.speech_role_name: voice_role_config = get_azure_voice_role_by_short_name( settings.speech_role_name ) vo["speech_role_name_label"] = voice_role_config["local_name"] return vo def save_settings(self, dto: AccountSettingsDTO, account_id: str): """保存用户设置""" account_settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) if dto.auto_playing_voice is not None: account_settings.auto_playing_voice = dto.auto_playing_voice if dto.playing_voice_speed is not None: account_settings.playing_voice_speed = dto.playing_voice_speed if dto.auto_text_shadow is not None: account_settings.auto_text_shadow = dto.auto_text_shadow if dto.auto_pronunciation is not None: account_settings.auto_pronunciation = dto.auto_pronunciation if dto.speech_role_name is not None: account_settings.speech_role_name = dto.speech_role_name if dto.target_language is not None: if dto.target_language != account_settings.target_language: # 获取语言对应的语音角色 speech_role_name = get_azure_language_default_role( dto.target_language ) account_settings.speech_role_name = speech_role_name account_settings.target_language = dto.target_language self.db.commit() def update_role_setting(self, dto: UpdateRoleDTO, account_id: str): """选择角色""" # 先删除 account_settings 中的数据 account_settings_entity = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) # dto 转json格式保存 account_settings_entity.role_setting = json.dumps(dto.model_dump()) self.db.commit() return { "account_id": account_id, "role_name": dto.role_name, "role_style": dto.style, } def get_role_setting(self, account_id: str): """获取用户当前设置的角色""" # 先删除 account_settings 中的数据 account_settings_entity = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) role_setting = get_azure_voice_role_by_short_name(account_settings_entity.speech_role_name) # 补充头像,根据性别补充头像 if role_setting['gender'] == 1: role_setting['role_image'] = 'http://qiniu.prejade.com/1597936949107363840/talkie/images/en-US_JennyNeural.png' else: role_setting['role_image'] = 'http://qiniu.prejade.com/1597936949107363840/talkie/images/en-US_Guy.png' return { "role_setting": role_setting } def get_account_source_language(self, account_id: str): """获取用户的学习语言""" settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) if settings: return settings.source_language else: return Config.DEFAULT_SOURCE_LANGUAGE def get_account_target_language(self, account_id: str): """获取用户的目标语言""" settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) if settings: return settings.target_language else: return Config.DEFAULT_TARGET_LANGUAGE def get_user_current_day_system_message_count(self, account_id: str): """获取用户当天系统消息次数""" # 获取当天0点的时间进行筛选 today = day_to_str(datetime.datetime.now()) return ( self.db.query(MessageEntity) .filter_by(account_id=account_id, type=MessageType.SYSTEM.value) .filter(MessageEntity.create_time >= today) .count() ) def get_user_system_message_count(self, account_id: str): """获取用户当天系统消息次数""" return ( self.db.query(MessageEntity) .filter_by(account_id=account_id, type=MessageType.SYSTEM.value) .count() ) def __check_and_init_default_settings(self, account_id: str): """检查并初始化用户的默认设置""" # 先检查是否已经存在,如果已经存在,就不需要再进行保存 settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) if not settings: speech_role_name = get_azure_language_default_role( Config.DEFAULT_TARGET_LANGUAGE ) settings = AccountSettingsEntity( account_id=account_id, target_language=Config.DEFAULT_TARGET_LANGUAGE, source_language=Config.DEFAULT_SOURCE_LANGUAGE, speech_role_name=speech_role_name, ) self.db.add(settings) self.db.commit() ================================================ FILE: talkieai-server/app/services/chat_service.py ================================================ from sqlalchemy.orm import Session from app.core.utils import * from app.db.account_entities import * from app.db.chat_entities import * from app.db.sys_entities import * from app.db.topic_entities import * from app.models.account_models import * from app.models.chat_models import * from app.services.account_service import AccountService from app.services.topic_service import TopicService from app.ai.models import * from app.ai import chat_ai from app.core.azure_voice import * from app.core.exceptions import * MESSAGE_SYSTEM = "SYSTEM" # 读取data下 language_demo_map.json 生成对应字典 language_demo_map = {} with open("data/language_demo_map.json", "r") as f: language_demo_map = json.load(f) class ChatService: """聊天核心类,会调用account_service与topic_service, 反向不可以引用""" def __init__(self, db: Session): self.db = db self.account_service = AccountService(db) self.topic_service = TopicService(db) def get_settings_languages_example(self, language: str, account_id: str): """获取语言下的示例""" # 获取语言下的示例 # 语言没有国家 所以去掉后面的国家后缀 language = language.split("-")[0] return language_demo_map[language] def get_default_session(self, account_id: str): """获取用户的默认会话, 如果没有默认会话,就创建一个""" session = ( self.db.query(MessageSessionEntity) .filter_by( account_id=account_id, is_default=1, ) .order_by(MessageSessionEntity.create_time.desc()) .first() ) if not session: # 为用户创建一个默认的session return self.create_session( account_id, ) return self.__convert_session_model(session) def get_session(self, session_id: str, account_id: str): """获取会话详情""" session = self.__get_and_check_session(session_id, account_id) result = self.__convert_session_model(session) # 获取会话下的消息 result["messages"] = self.get_session_messages(session_id, account_id, 1, 100) return result def get_session_greeting(self, session_id: str, account_id: str): """需要会话没有任何消息时,需要返回的问候语""" # 检查session是否存在 session = self.__get_and_check_session(session_id, account_id) # 检查会话下是否已经有了消息 self.__check_has_messages(session_id, account_id) # 区分自由聊天与话题聊天 if session.type == "CHAT": language = self.account_service.get_account_target_language(account_id) result = chat_ai.invoke_greet(GreetParams(language=language)) elif session.type == "TOPIC": topic_greet_params = self.topic_service.get_topic_greet_params(session.id) result = chat_ai.topic_invoke_greet(topic_greet_params) sequence = self.__get_message_sequence() add_message = self.__add_system_message(session_id, account_id, result, "", sequence + 1) self.db.add(add_message) self.db.commit() self.db.flush() self.__refresh_session_message_count(session_id) return self.initMessageResult(add_message) def send_session_message(self, session_id: str, dto: ChatDTO, account_id: str): """发送消息""" # 如果有file_name却没有message,需要解析出message if not dto.file_name and not dto.message: raise Exception("Message or file_name is required") session = self.__get_and_check_session(session_id, account_id) account_settins = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) target_language = account_settins.target_language if dto.message: send_message_content = dto.message else: send_message_content = speech_translate_text( voice_file_get_path(dto.file_name), target_language ) # 获取前面的sequence sequence = self.__get_message_sequence() add_account_message = self.__add_account_message( account_id, session_id, send_message_content, sequence + 1, dto.file_name ) send_message_id = add_account_message.id message_history = ( self.db.query(MessageEntity) .filter(MessageEntity.session_id == session_id) .order_by(MessageEntity.create_time.desc()) .slice(0, 5) .all() ) messages = [] for message in reversed(message_history): if message.type == MessageType.SYSTEM.value: messages.append({"role": "assistant", "content": message.content}) else: messages.append({"role": "user", "content": message.content}) # 补充上用户新加的这个 messages.append({"role": "user", "content": send_message_content}) completed = False if session.type == 'CHAT': speech_role_name = account_settins.speech_role_name styles = [] if speech_role_name: voice_role_config = get_azure_voice_role_by_short_name(speech_role_name) styles = voice_role_config["style_list"] message_params = MessageParams( language=target_language, name=Config.AI_NAME, messages=messages, styles=styles ) invoke_result = chat_ai.invoke_message(message_params) elif session.type == 'TOPIC': topic_message_params = self.topic_service.get_topic_message_params(session.id) topic_message_params.messages = messages invoke_result = chat_ai.topic_invoke_message(topic_message_params) completed = invoke_result.completed add_system_message = self.__add_system_message( session_id, account_id, invoke_result.message, invoke_result.message_style, sequence + 2, ) self.db.add(add_account_message) self.db.add(add_system_message) self.db.commit() self.db.flush() self.__refresh_session_message_count(session_id) return { "data": invoke_result.message, "id": add_system_message.id, "session_id": session_id, "send_message_id": send_message_id, "send_message_content": send_message_content, "create_time": date_to_str(add_system_message.create_time), "completed": completed, } def message_practice( self, message_id: str, dto: MessagePracticeDTO, account_id: str ): """用户发送过的消息进行练习""" message = self.db.query(MessageEntity).filter_by(id=message_id).first() if not message: raise Exception("Message not found") target_language = self.account_service.get_account_target_language(account_id) return word_speech_pronunciation( message.content, voice_file_get_path(dto.file_name), target_language ) def get_word(self, dto: WordDetailDTO, account_id: str): """通过AI获取单词的音标与翻译""" # 先查询数据库中是否有数据,如果有数据就直接返回 word = self.db.query(SysCacheEntity).filter_by(key=f"word_{dto.word}").first() if word: return json.loads(word.value) invoke_result = chat_ai.invoke_word_detail(WordDetailParams(word=dto.word)) result = invoke_result.__dict__ result["original"] = dto.word # result 转换成字符串进行保存 sys_cache = SysCacheEntity(key=f"word_{dto.word}", value=json.dumps(result)) self.db.add(sys_cache) self.db.commit() return result def grammar_analysis(self, dto: GrammarDTO, account_id: str): message = self.db.query(MessageEntity).filter_by(id=dto.message_id).first() # 检查AccountGrammarEntity是否已经存在数据,如果存在就直接返回已经保存的数据 message_grammar = ( self.db.query(MessageGrammarEntity) .filter_by( message_id=dto.message_id, file_name=message.file_name, type="GRAMMAR" ) .first() ) if message_grammar: return json.loads(message_grammar.result) content = message.content target_language = self.account_service.get_account_target_language(account_id) result = chat_ai.invoke_grammar_analysis( GrammarAnalysisParams(language=target_language, content=content) ).__dict__ result["original"] = content # result是json格式的字符串,把result 解析成json返回 # 结果以字符串方式保存到数据库中 message_grammar = MessageGrammarEntity( account_id=account_id, session_id=message.session_id, message_id=dto.message_id, file_name=message.file_name, type="GRAMMAR", result=json.dumps(result), ) self.db.add(message_grammar) self.db.commit() return result def word_practice(self, dto: WordPracticeDTO, account_id: str): """单词发音练习""" target_language = self.account_service.get_account_target_language(account_id) return word_speech_pronunciation( dto.word, voice_file_get_path(dto.file_name), language=target_language ) def pronunciation(self, dto: PronunciationDTO, account_id: str): """发单评估""" # 先根据message_id查询出message message = self.db.query(MessageEntity).filter_by(id=dto.message_id).first() if not message: raise UserAccessDeniedException("message不存在") file_name = message.file_name if not file_name: raise UserAccessDeniedException("message中没有语音文件") # 检查AccountGrammarEntity是否已经存在数据,如果存在就直接返回已经保存的数据 grammar = ( self.db.query(MessageGrammarEntity) .filter_by( message_id=dto.message_id, file_name=message.file_name, type="PRONUNCIATION", ) .first() ) if grammar: return json.loads(grammar.result) file_full_path = voice_file_get_path(file_name) # 检查文件是否存在 if not os.path.exists(file_full_path): raise UserAccessDeniedException("语音文件不存在") target_language = self.account_service.get_account_target_language(account_id) # 进行评分 try: session = ( self.db.query(MessageSessionEntity) .filter_by(id=message.session_id) .first() ) pronunciation_result = word_speech_pronunciation( message.content, file_full_path, language=target_language ) logging.info("end") except Exception as e: # 输出错误信息 logging.exception( f"file_full_path:{file_full_path}\n content:{message.content}", e ) raise UserAccessDeniedException("语音评估失败") # 结果以字符串方式保存到数据库中 message_grammar = MessageGrammarEntity( account_id=account_id, session_id=message.session_id, message_id=dto.message_id, file_name=message.file_name, type="PRONUNCIATION", result=json.dumps(pronunciation_result), ) self.db.add(message_grammar) self.db.commit() return pronunciation_result def message_speech_content(self, dto: TransformContentSpeechDTO, account_id: str): """如果file表中已经存在文件的保存,则直接返回,如果不存在,生成一份并保存""" # 根据convert_language与speech_role_name,speech_rate,speech_style来生成唯一标识,用于生成缓存的key # 获取用户语言 account_settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) target_language = account_settings.target_language set_speech_role_name = None set_speech_role_style = "" if dto.speech_role_name: set_speech_role_name = dto.speech_role_name if dto.speech_role_style: set_speech_role_style = dto.speech_role_style elif account_settings.speech_role_name: set_speech_role_name = account_settings.speech_role_name set_speech_rate = "1.0" if dto.speech_rate: set_speech_rate = dto.speech_rate elif account_settings.speech_role_speed: set_speech_rate = account_settings.speech_role_speed content_md5 = hashlib.md5(dto.content.encode("utf-8")).hexdigest() key = f"content_{set_speech_role_name}_{set_speech_role_style}_{set_speech_rate}_{content_md5}" file_module = "SPEECH_CONTENT_VOICE" # 对key进行md5加密 file_detail = ( self.db.query(FileDetail) .filter_by(module=file_module, module_id=key, deleted=0) .first() ) if file_detail: # 检查文件是否存在,只有文件存在情况下才进行返回 if os.path.exists(voice_file_get_path(file_detail.file_name)): return {"file": file_detail.file_name} else: # 如果文件不存在,就删除数据库中的记录,重新生成 file_detail.deleted = 1 self.db.commit() # 调用speech组件,将speech_content转换成语音文件 filename = f"{key}.wav" full_file_name = voice_file_get_path(filename) if set_speech_rate != "1.0" or set_speech_role_style: speech_by_ssml( dto.content, full_file_name, voice_name=set_speech_role_name, speech_rate=set_speech_rate, feel=set_speech_role_style, targetLang=target_language, ) else: speech_default( dto.content, full_file_name, target_language, set_speech_role_name ) file_detail = FileDetail( id=short_uuid(), file_path=filename, module=file_module, file_name=filename, module_id=key, file_ext="wav", created_by=account_id, ) self.db.add(file_detail) self.db.commit() self.db.flush() return {"file": file_detail.file_name} def message_speech(self, message_id: str, account_id: str): """文字转语音""" # 如果没有,就生成一个 message = self.db.query(MessageEntity).filter_by(id=message_id).first() # 获取用户的语音配置 account_settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) target_language = account_settings.target_language voice_name = account_settings.speech_role_name speech_speed = account_settings.playing_voice_speed filename = f"message_{message.id}_{voice_name}_{speech_speed}.wav" full_file_name = voice_file_get_path(filename) voice_role_style = "" if message.style: voice_role_style = message.style speech_by_ssml( message.content, full_file_name, voice_name=voice_name, speech_rate=speech_speed, feel=voice_role_style, targetLang=target_language, ) file_detail = FileDetail( id=short_uuid(), file_path=filename, module="SPEECH_VOICE", file_name=filename, module_id=message_id, file_ext="wav", created_by=account_id, ) self.db.add(file_detail) message.file_name = filename self.db.commit() return {"file": file_detail.file_name} def create_session(self, account_id: str): """为用户创建新的session,并且设置成默认的session""" session = MessageSessionEntity( id=f"session_{short_uuid()}", account_id=account_id, is_default=1 ) self.db.add(session) self.db.commit() return self.__convert_session_model(session) def get_session_messages( self, session_id: str, account_id: str, page: int, page_size: int ): query = ( self.db.query(MessageEntity) .filter_by(session_id=session_id, account_id=account_id, deleted=0) .filter( MessageEntity.type.in_( [MessageType.ACCOUNT.value, MessageType.SYSTEM.value] ) ) ) messages = ( query.order_by(MessageEntity.sequence.desc()) .offset((page - 1) * page_size) .limit(page_size) .all() ) # 获取总数 total = query.count() result = [] for message in reversed(messages): result.append(self.initMessageResult(message)) # 如果是我的消息,则检查是否进行过语音分析,如果进行过就加载分析结果 self.initOwnerMessagePronunciation(result) return {"total": total, "list": result} def prompt_sentence(self, dto: PromptDTO, account_id: str): """提示用户下一句话""" # 查询出session中最后5条消息 messageEntities = ( self.db.query(MessageEntity) .filter_by(session_id=dto.session_id) .order_by(MessageEntity.create_time.desc()) .limit(5) .all() ) messages = [] for message in messageEntities: messages.append(self.initMessageResult(message)) target_language = self.account_service.get_account_target_language(account_id) return chat_ai.invoke_prompt_sentence( PromptSentenceParams(language=target_language, messages=messages) ) def delete_all_session_messages(self, session_id: str, account_id: str): """把所有的消息都调整为deleted=1""" messages = ( self.db.query(MessageEntity) .filter_by(session_id=session_id, account_id=account_id, deleted=0) .all() ) for message in messages: message.deleted = 1 self.db.commit() return True def transform_text(self, session_id: str, dto: VoiceTranslateDTO, account_id: str): """语音解析成文字""" result = speech_translate_text( voice_file_get_path(dto.file_name), self.account_service.get_account_target_language(account_id), ) return result def delete_latest_session_messages(self, session_id: str, account_id: str): """查出最近的一条type为ACCOUNT的数据,并且把create_time之后的数据全部调整为deleted=1,删除成功后需要返回所有删除成功的message的id""" message = ( self.db.query(MessageEntity) .filter_by( session_id=session_id, account_id=account_id, type=MessageType.ACCOUNT.value, deleted=0, ) .order_by(MessageEntity.create_time.desc()) .first() ) if message: # 获取所有需要删除的数据 messages = ( self.db.query(MessageEntity) .filter_by(session_id=session_id, deleted=0) .filter(MessageEntity.create_time >= message.create_time) .all() ) for message in messages: message.deleted = 1 self.db.commit() return [message.id for message in messages] return [] def initOwnerMessagePronunciation(self, result): # 过滤出所有role为USER的id列表,然后根据id列表获取所有的message_pronunciation,再组装到item中,不存在则组装None user_message_ids = [item["id"] for item in result if item["role"] == "USER"] message_pronunciations = ( self.db.query(MessageGrammarEntity) .filter( MessageGrammarEntity.message_id.in_(user_message_ids), MessageGrammarEntity.type == "PRONUNCIATION", ) .all() ) for item in result: if item["role"] == "USER": item["pronunciation"] = None for message_pronunciation in message_pronunciations: if message_pronunciation.message_id == item["id"]: item["pronunciation"] = json.loads(message_pronunciation.result) break def translate_source_language(self, dto: TranslateTextDTO, account_id: str): """翻译成源语言""" source_language = self.account_service.get_account_source_language(account_id) result = self.translate_language(dto.text, source_language) return result def translate_setting_language(self, dto: TranslateTextDTO, account_id: str): """翻译成目标语言,也就是用户学习的语言""" # 获取用户配置language account_settings = ( self.db.query(AccountSettingsEntity) .filter_by(account_id=account_id) .first() ) result = self.translate_language(dto.text, account_settings.target_language) return result def translate_language(self, content: str, language: str): """翻译成参数中配置的语言""" result = chat_ai.invoke_translate( TranslateParams(target_language=language, content=content) ) return result def translate_message(self, message_id: str, account_id: str): # 检查是否已经生成了对应翻译,生成的话直接返回 message_translate = ( self.db.query(MessageTranslateEntity) .filter_by(message_id=message_id) .first() ) if message_translate: return message_translate.target_text message = self.db.query(MessageEntity).filter_by(id=message_id).first() content = message.content source_language = self.account_service.get_account_source_language(account_id) target_language = self.account_service.get_account_target_language(account_id) result = self.translate_source_language( TranslateTextDTO(text=content), account_id ) account_translate = MessageTranslateEntity( account_id=account_id, session_id=message.session_id, message_id=message_id, target_language=target_language, source_language=source_language, source_text=content, target_text=result, ) self.db.add(account_translate) self.db.commit() return result def initMessageResult(self, message: MessageEntity): return { "role": "ASSISTANT" if message.type == MessageType.SYSTEM.value else "USER", "content": message.content, "file_name": message.file_name, "id": message.id, "create_time": date_to_str(message.create_time), "session_id": message.session_id, } def __convert_session_model(self, session: MessageSessionEntity): return { "id": session.id, "type": session.type, "message_count": session.message_count, "create_time": date_to_str(session.create_time), "friendly_time": friendly_time(date_to_str(session.create_time)), } def __add_account_message( self, account_id: str, session_id: str, content: str, sequence: int, file_name: str = None ): """添加用户消息""" message = MessageEntity( id=short_uuid(), account_id=account_id, sender=account_id, session_id=session_id, receiver=MESSAGE_SYSTEM, type=MessageType.ACCOUNT.value, content=content, file_name=file_name, length=len(content), sequence=sequence, ) return message def __add_system_message( self, session_id, account_id: str, content: str, style: str, sequence: int ) -> MessageEntity: """添加系统消息""" add_message = MessageEntity( id=short_uuid(), account_id=account_id, sender=MESSAGE_SYSTEM, session_id=session_id, receiver=account_id, type=MessageType.SYSTEM.value, content=content, style=style, length=len(content), sequence=sequence, ) return add_message def __refresh_session_message_count(self, session_id: str): """刷新session的消息数量, 需要排除deleted为1的数据""" count = ( self.db.query(MessageEntity) .filter(MessageEntity.session_id == session_id, MessageEntity.deleted == 0) .count() ) self.db.query(MessageSessionEntity).filter( MessageSessionEntity.id == session_id ).update({"message_count": count}) self.db.commit() self.db.flush() def __get_and_check_session( self, session_id: str, account_id: str ) -> MessageSessionEntity: """检查会话是否存在""" session = ( self.db.query(MessageSessionEntity) .filter_by(id=session_id, account_id=account_id) .first() ) if not session: raise Exception("Session not found") return session def __check_has_messages(self, session_id: str, account_id: str): """检查会话下是否已经有了消息""" messages = ( self.db.query(MessageEntity) .filter_by(session_id=session_id, account_id=account_id, deleted=0) .order_by(MessageEntity.create_time.desc()) .slice(0, 1) .all() ) if len(messages) == 1: raise Exception("Session has messages") def __get_message_sequence(self): """获取当前最大的sequence""" sequence = ( self.db.query(MessageEntity) .filter_by(deleted=0) .order_by(MessageEntity.sequence.desc()) .first() ) if sequence: return sequence.sequence return 0 ================================================ FILE: talkieai-server/app/services/sys_service.py ================================================ import json import os from pydub import AudioSegment from sqlalchemy.orm import Session from app.core.utils import * from app.core.exceptions import * from app.core.utils import * from app.db.account_entities import * from app.db.sys_entities import * from app.core.logging import logging from app.models.sys_models import * from app.core.language import * # 读取data下 language_demo_map.json 生成对应字典 LANGUAGE_DICT_KEY = "learn_language" language_demo_map = {} with open("data/language_demo_map.json", "r") as f: language_demo_map = json.load(f) class SysService: def __init__(self, db: Session): self.db = db # 查出所有系统配置的语言,并且生成默认角色与语气的信息 self._check_and_init_default_learn_languages() def get_settings_roles(self, locale: str, account_id: str): """根据语言获取语言下所有支持的角色""" roles = ( self.db.query(SettingsRoleEntity) .filter_by(locale=locale, deleted=0) .order_by(SettingsRoleEntity.name.asc()) .all() ) result = [] for role in roles: # 通过roles批量获取所有的style,并且在迭代中进行组装 styles = json.loads(role.styles) # 转换成value、label格式 styles_data = [] for style in styles: if style: styles_data.append( {"value": style, "label": get_azure_style_label(style)} ) else: styles_data.append({"value": "", "label": ""}) result.append( { "id": role.id, "name": role.name, "locale": role.locale, "local_name": role.local_name, "short_name": role.short_name, "avatar": role.avatar, "audio": role.audio, "speech_content": "", "gender": role.gender, "styles": styles_data, } ) return result def get_settings_languages_example(self, language: str, account_id: str): """获取语言下的示例""" # 获取语言下的示例 # 语言没有国家 所以去掉后面的国家后缀 language = language.split("-")[0] languages = ( self.db.query(SettingsLanguageExampleEntity) .filter_by(language=language) .first() ) return languages.example def get_settings_languages(self, account_id: str): """获取用户支持的所有语种""" # sys_dict_data 获取 dict_type 为 learn_language 的数据 sys_dict_data_entities = ( self.db.query(SysDictDataEntity) .filter(SysDictDataEntity.dict_type == LANGUAGE_DICT_KEY) .all() ) # 取出dict_label 与 dict_value 返回 result = [] for sys_dict_data_entity in sys_dict_data_entities: result.append( { "label": sys_dict_data_entity.dict_label, "value": sys_dict_data_entity.dict_value, } ) return result def get_settings_languages_example(self, language: str, account_id: str): """获取语言下的示例""" # 获取语言下的示例 # 语言没有国家 所以去掉后面的国家后缀 language = language.split("-")[0] return language_demo_map[language] def voice_upload(self, file: UploadFile, account_id: str): """用户上传语音文件""" file_name = save_voice_file(file, account_id) # 如果上传的是mp3格式(暂时只有android手机只能用mp3格式), 就转换成wav返回, 为了后续azure服务解析音频(mp3会解析失败), 因为chat接口本身比较慢,所以在这里进行转换 if file.filename.endswith(".mp3"): mp3_file_path = file_get_path(file_name) wav_file_name = file_name.replace(".mp3", ".wav") wav_file_path = file_get_path(wav_file_name) sound = AudioSegment.from_mp3(mp3_file_path) sound.export(wav_file_path, format="wav") # mp3文件需要删除 os.remove(mp3_file_path) file_name = wav_file_name return {"file": file_name} def add_feedback(self, dto: FeedbackDTO, account_id: str): add_feedback = FeedbackEntity( account_id=account_id, content=dto.content, contact=dto.contact ) self.db.add(add_feedback) self.db.commit() self.db.refresh(add_feedback) def _check_and_init_default_learn_languages(self): # 查出所有系统配置的语言 sys_dict_data_entities = ( self.db.query(SysDictDataEntity) .filter(SysDictDataEntity.dict_type == LANGUAGE_DICT_KEY) .all() ) # sys_dict_data_entities 为空的话加载默认的语言 if sys_dict_data_entities: return # 加载 data下 sys_language.json文件,生成初始值 with open("data/sys_language.json", "r") as f: sys_language = json.load(f) # 保存到数据库中 for language in sys_language: add_language = SysDictDataEntity( dict_type=LANGUAGE_DICT_KEY, dict_label=language["label"], dict_value=language["value"], ) self.db.add(add_language) # 继续检查角色的配置 self._check_and_init_default_roles(add_language.dict_value) self.db.commit() def _check_and_init_default_roles(self, locale: str): """检查是否初始化了数据,如果未初始化,从azure获取所有的角色,并且保存到数据库中""" # 查出所有系统配置的角色 roles = ( self.db.query(SettingsRoleEntity).filter_by(locale=locale, deleted=0).all() ) if roles: return roles = [role for role in azure_data if role["locale"] == locale] # 保存到数据库中 for role in roles: add_role = SettingsRoleEntity( name=role["name"], locale=role["locale"], local_name=role["local_name"], short_name=role["short_name"], gender=role["gender"], styles=json.dumps(role["style_list"]), ) self.db.add(add_role) self.db.commit() ================================================ FILE: talkieai-server/app/services/topic_service.py ================================================ import json from sqlalchemy.orm import Session from app.core.utils import * from app.models.topic_models import * from app.db.topic_entities import * from app.db.chat_entities import * from app.db.account_entities import * from app.ai.models import * from app.core.logging import logging from app.ai import chat_ai from app.core.azure_voice import * from app.models.chat_models import * class TopicService: def __init__(self, db: Session): self.db = db self.__check_and_init_topics() def get_topic_greet_params(self, session_id: str) -> TopicGreetParams: """获取话题的prompt""" # 根据关联关系取到topic_id topic_session_relation = ( self.db.query(TopicSessionRelation).filter_by(session_id=session_id).first() ) topic_entity = ( self.db.query(TopicEntity) .filter(TopicEntity.id == topic_session_relation.topic_id) .first() ) topic_greet_params = TopicGreetParams( language=topic_entity.language, prompt=topic_entity.prompt, ) return topic_greet_params def get_topic_message_params(self, session_id: str) -> AITopicMessageParams: """获取话题的prompt""" # 根据关联关系取到topic_id topic_session_relation = ( self.db.query(TopicSessionRelation).filter_by(session_id=session_id).first() ) topic_entity = ( self.db.query(TopicEntity) .filter(TopicEntity.id == topic_session_relation.topic_id) .first() ) styles = [] if topic_entity.role_short_name: voice_role_config = get_azure_voice_role_by_short_name( topic_entity.role_short_name ) styles = voice_role_config["style_list"] topic_message_params = AITopicMessageParams( name=topic_entity.topic_bot_name, language=topic_entity.language, prompt=topic_entity.prompt, speech_role_name=topic_entity.role_short_name, styles=styles, ) return topic_message_params def get_all_topics(self, type: str, account_id: str): """获取所有话题组与话题""" # 获取用户配置,通过用户配置获取language account_settins = ( self.db.query(AccountSettingsEntity) .filter(AccountSettingsEntity.account_id == account_id) .first() ) # 如果用户没有配置,则使用默认的en-US if account_settins is None: language = "en-US" else: language = account_settins.target_language if language == "en-GB": language = "en-US" result = [] topic_group_entities = ( self.db.query(TopicGroupEntity) .filter( TopicGroupEntity.type == type, TopicEntity.language == language, ) .order_by(TopicGroupEntity.sequence.desc()) .all() ) # 迭代话题组,获取话题组下的话题 for topic_group_entity in topic_group_entities: topic_entities = ( self.db.query(TopicEntity) .filter(TopicEntity.group_id == topic_group_entity.id) .order_by(TopicEntity.sequence.desc()) .all() ) # 批量查询话题是否已经完成的状态,在后续迭代中补充此属性 topic_ids = [] for topic_entity in topic_entities: topic_ids.append(topic_entity.id) topic_history_entities = ( self.db.query(TopicHistoryEntity) .filter(TopicHistoryEntity.topic_id.in_(topic_ids)) .filter(TopicHistoryEntity.account_id == account_id) .filter(TopicHistoryEntity.completed == "1") .all() ) topic_history_map = {} for topic_history_entity in topic_history_entities: topic_history_map[topic_history_entity.topic_id] = topic_history_entity # 迭代话题数据进行补充 topics = [] for topic_entitity in topic_entities: topic = { "id": topic_entitity.id, "name": topic_entitity.name, "description": topic_entitity.description, "prompt": topic_entitity.prompt, "level": topic_entitity.level, "image_url": topic_entitity.image_url, } # 补充是否已经完成的状态 if topic_entitity.id in topic_history_map: topic["completed"] = topic_history_map[topic_entitity.id].completed else: topic["completed"] = "0" topics.append(topic) group = { "id": topic_group_entity.id, "name": topic_group_entity.name, "topics": topics, } result.append(group) return result def get_topic_detail(self, topic_id: str, account_id: str): """获取话题详情""" topic_entity = ( self.db.query(TopicEntity).filter(TopicEntity.id == topic_id).first() ) # 获取话题目标 topic_target_entities = ( self.db.query(TopicTargetEntity) .filter(TopicTargetEntity.topic_id == topic_id) .order_by(TopicTargetEntity.type) .all() ) # 迭代话题目标数据进行补充 # TopicTargetEntity按 type为 MAIN TRIAL 的不同值进行组装 main_targets = [] trial_targets = [] for topic_target_entity in topic_target_entities: target = { "id": topic_target_entity.id, "type": topic_target_entity.type, "description": topic_target_entity.description, } if topic_target_entity.type == "MAIN": main_targets.append(target) elif topic_target_entity.type == "TRIAL": trial_targets.append(target) result = { "id": topic_entity.id, "name": topic_entity.name, "description": topic_entity.description, "prompt": topic_entity.prompt, "image_url": topic_entity.image_url, "main_targets": main_targets, "trial_targets": trial_targets, } return result def create_topic_session(self, topic_id: str, account_id: str): """基于主题创建一个会话""" # 获取Topic详情 topic_entity = ( self.db.query(TopicEntity).filter(TopicEntity.id == topic_id).first() ) # 创建session session = MessageSessionEntity( id=f"session_{short_uuid()}", account_id=account_id, type="TOPIC", ) self.db.add(session) # 创建session与topic的关系 session_topic_relation = TopicSessionRelation( session_id=session.id, topic_id=topic_id, account_id=account_id, ) self.db.add(session_topic_relation) # 保存话题历史记录 topic_entity = ( self.db.query(TopicEntity).filter(TopicEntity.id == topic_id).first() ) topic_history_entity = TopicHistoryEntity( account_id=account_id, topic_id=topic_id, topic_type="TOPIC", topic_name=topic_entity.name, completion=0, session_id=session.id, ) self.db.add(topic_history_entity) self.db.commit() return {"id": session.id} def complete_topic_session(self, session_id: str, account_id: str): """结束话题下的session""" # 获取话题与session数据 topic_session_relation = ( self.db.query(TopicSessionRelation).filter_by(session_id=session_id).first() ) topic_id = topic_session_relation.topic_id topic_entity = ( self.db.query(TopicEntity).filter(TopicEntity.id == topic_id).first() ) session_entity = ( self.db.query(MessageSessionEntity) .filter(MessageSessionEntity.id == session_id) .first() ) # 取出所有的聊天内容,通过AI进行完成度与评分计算,并计算出所使用的单词数量 message_entities = ( self.db.query(MessageEntity) .filter(MessageEntity.session_id == session_id) .order_by(MessageEntity.create_time.asc()) .all() ) messages = [] for message in message_entities: if message.type == MessageType.SYSTEM.value: messages.append(MessageItemParams(role='assistant', content=message.content)) else: messages.append(MessageItemParams(role='user', content=message.content)) # 计算完成度,取出topic下所有的target topic_target_entities = ( self.db.query(TopicTargetEntity) .filter(TopicTargetEntity.topic_id == topic_id) .all() ) targets = [] for target in topic_target_entities: targets.append(target.description) invoke_result = chat_ai.topic_invoke_complete( AITopicCompleteParams( language=topic_entity.language, messages=messages, targets=targets ) ) # 获取history 并进行记录 topic_history_entity = ( self.db.query(TopicHistoryEntity) .filter(TopicHistoryEntity.session_id == session_id) .first() ) topic_history_entity.completion = invoke_result.targets topic_history_entity.content_score = invoke_result.score topic_history_entity.word_count = invoke_result.words topic_history_entity.suggestion = invoke_result.suggestion topic_history_entity.completed = "1" session_entity.completed = "1" self.db.commit() def delete_topic_session(self, topic_id: str, session_id: str, account_id: str): """把话题下的history的status设置为DELETED""" topic_history_entity = ( self.db.query(TopicHistoryEntity) .filter(TopicHistoryEntity.session_id == session_id) .first() ) topic_history_entity.status = "DELETED" self.db.commit() def get_custom_topic_example(self, account_id: str): """获取随机话题""" return {"my_role": "Jack", "ai_role": "小厨师", "topic": "关于厨房的那些事儿"} def create_custom_topic(self, dto: TopicCreateDTO, account_id: str): """用户创建自己的话题""" # 获取用户的语言配置信息 account_settins = ( self.db.query(AccountSettingsEntity) .filter(AccountSettingsEntity.account_id == account_id) .first() ) language = account_settins.target_language if language == "en-GB": language = "en-US" account_topic = AccountTopicEntity( id=f"account_topic_{short_uuid()}", account_id=account_id, language=language, ai_role=dto.ai_role, my_role=dto.my_role, topic=dto.topic, ) return account_topic.id def get_custom_topic(self, account_id: str): """获取用户创建的自定义话题""" account_topics = ( self.db.query(AccountTopicEntity) .filter(AccountTopicEntity.account_id == account_id) .all() ) result = [] for account_topic in account_topics: result.append( { "id": account_topic.id, "ai_role": account_topic.ai_role, "my_role": account_topic.my_role, "topic": account_topic.topic, } ) return result def get_session_result(self, topic_id: str, session_id: str, account_id: str): """获取主题聊天下的结果""" topic_history_entity = ( self.db.query(TopicHistoryEntity) .filter(TopicHistoryEntity.session_id == session_id) .first() ) return { "topic_name": topic_history_entity.topic_name, "main_target_count": topic_history_entity.main_target_count, "trial_target_count": topic_history_entity.trial_target_count, "main_target_completed_count": topic_history_entity.main_target_completed_count, "trial_target_completed_count": topic_history_entity.trial_target_completed_count, "completion": topic_history_entity.completion, "audio_score": topic_history_entity.audio_score, "content_score": topic_history_entity.content_score, "suggestion": topic_history_entity.suggestion, "word_count": topic_history_entity.word_count, } def get_topic_history(self, topic_id: str, account_id: str): """获取话题历史记录,topic_id做为可选参数,为空时查询所有历史记录""" result = [] if topic_id is None: topic_history_entities = ( self.db.query(TopicHistoryEntity) .filter( TopicHistoryEntity.account_id == account_id, TopicHistoryEntity.status == "ACTIVE", ) .order_by(TopicHistoryEntity.create_time.desc()) .all() ) else: topic_history_entities = ( self.db.query(TopicHistoryEntity) .filter(TopicHistoryEntity.account_id == account_id) .filter( TopicHistoryEntity.topic_id == topic_id, TopicHistoryEntity.status == "ACTIVE", ) .order_by(TopicHistoryEntity.create_time.desc()) .all() ) # 查出来所有历史记录涉及的Topic, 后面迭代进行补充 topic_ids = [] for topic_history_entity in topic_history_entities: topic_ids.append(topic_history_entity.topic_id) topic_entities = ( self.db.query(TopicEntity).filter(TopicEntity.id.in_(topic_ids)).all() ) # 迭代话题历史记录数据进行补充 for topic_history_entity in topic_history_entities: topic_id = topic_history_entity.topic_id # 通过topic_entities取出对应的topic topic_entity = next(filter(lambda x: x.id == topic_id, topic_entities)) create_time_str = date_to_str(topic_history_entity.create_time) history = { "id": topic_history_entity.id, "topic_id": topic_history_entity.topic_id, "topic_type": topic_history_entity.topic_type, "topic_name": topic_history_entity.topic_name, "completion": topic_history_entity.completion, "session_id": topic_history_entity.session_id, "create_time": create_time_str, "create_time_friendly": friendly_time(create_time_str), "completed": topic_history_entity.completed, "topic": { "id": topic_entity.id, "topic": topic_entity.name, "description": topic_entity.description, "prompt": topic_entity.prompt, "image_url": topic_entity.image_url, }, } result.append(history) return result def get_topic_phrases(self, topic_id: str, account_id: str): """获取话题短语记录""" result = [] topic_phrase_entities = ( self.db.query(TopicPhraseEntity) .filter(TopicPhraseEntity.topic_id == topic_id) .order_by(TopicPhraseEntity.sequence) .all() ) for topic_phrase_entity in topic_phrase_entities: phrase = { "id": topic_phrase_entity.id, "phrase": topic_phrase_entity.phrase, "phrase_translation": topic_phrase_entity.phrase_translation, "type": topic_phrase_entity.type, "sequence": topic_phrase_entity.sequence, } result.append(phrase) return result def __check_and_init_topics(self): """检查与生成默认的 topics""" # 检查topic_group是否有数据 topic_group_entities = self.db.query(TopicGroupEntity).all() if len(topic_group_entities) != 0: return # 根据配置文件生成默认数据 with open("data/default_topic_data.json", "r") as f: topic_data = json.load(f) default_account = "system_init" for topic_group in topic_data["groups"]: topic_group_entity = TopicGroupEntity( id=topic_group["id"], type=topic_group["type"], name=topic_group["name"], sequence=topic_group["sequence"], description=topic_group["description"], created_by=default_account, ) self.db.add(topic_group_entity) for topic in topic_group["topics"]: topic_entity = TopicEntity( id=topic["id"], group_id=topic_group_entity.id, name=topic["name"], description=topic["description"], level=topic["level"], image_url=topic["image_url"], language=topic["language"], role_short_name=topic["role_short_name"], role_speech_rate=topic["role_speech_rate"], sequence=topic["sequence"], topic_user_name=topic["topic_user_name"], topic_bot_name=topic["topic_bot_name"], prompt=topic["prompt"], created_by=default_account, ) self.db.add(topic_entity) for target in topic["targets"]: topic_target_entity = TopicTargetEntity( topic_id=topic_entity.id, type=target["type"], description=target["description"], sequence=target["sequence"], description_translation=target["description_translation"], created_by=default_account, ) self.db.add(topic_target_entity) for phrase in topic["phrases"]: topic_phrase_entity = TopicPhraseEntity( topic_id=topic_entity.id, phrase=phrase["phrase"], phrase_translation=phrase["phrase_translation"], type=phrase["type"], sequence=phrase["sequence"], created_by=default_account, ) self.db.add(topic_phrase_entity) self.db.commit() ================================================ FILE: talkieai-server/data/azure.json ================================================ [{ "gender": 1, "locale": "af-ZA", "local_name": "Adri", "name": "Microsoft Server Speech Text to Speech Voice (af-ZA, AdriNeural)", "short_name": "af-ZA-AdriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "af-ZA", "local_name": "Willem", "name": "Microsoft Server Speech Text to Speech Voice (af-ZA, WillemNeural)", "short_name": "af-ZA-WillemNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "am-ET", "local_name": "መቅደስ", "name": "Microsoft Server Speech Text to Speech Voice (am-ET, MekdesNeural)", "short_name": "am-ET-MekdesNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "am-ET", "local_name": "አምሀ", "name": "Microsoft Server Speech Text to Speech Voice (am-ET, AmehaNeural)", "short_name": "am-ET-AmehaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-AE", "local_name": "فاطمة", "name": "Microsoft Server Speech Text to Speech Voice (ar-AE, FatimaNeural)", "short_name": "ar-AE-FatimaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-AE", "local_name": "حمدان", "name": "Microsoft Server Speech Text to Speech Voice (ar-AE, HamdanNeural)", "short_name": "ar-AE-HamdanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-BH", "local_name": "ليلى", "name": "Microsoft Server Speech Text to Speech Voice (ar-BH, LailaNeural)", "short_name": "ar-BH-LailaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-BH", "local_name": "علي", "name": "Microsoft Server Speech Text to Speech Voice (ar-BH, AliNeural)", "short_name": "ar-BH-AliNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-DZ", "local_name": "أمينة", "name": "Microsoft Server Speech Text to Speech Voice (ar-DZ, AminaNeural)", "short_name": "ar-DZ-AminaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-DZ", "local_name": "إسماعيل", "name": "Microsoft Server Speech Text to Speech Voice (ar-DZ, IsmaelNeural)", "short_name": "ar-DZ-IsmaelNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-EG", "local_name": "سلمى", "name": "Microsoft Server Speech Text to Speech Voice (ar-EG, SalmaNeural)", "short_name": "ar-EG-SalmaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-EG", "local_name": "شاكر", "name": "Microsoft Server Speech Text to Speech Voice (ar-EG, ShakirNeural)", "short_name": "ar-EG-ShakirNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-IQ", "local_name": "رنا", "name": "Microsoft Server Speech Text to Speech Voice (ar-IQ, RanaNeural)", "short_name": "ar-IQ-RanaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-IQ", "local_name": "باسل", "name": "Microsoft Server Speech Text to Speech Voice (ar-IQ, BasselNeural)", "short_name": "ar-IQ-BasselNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-JO", "local_name": "سناء", "name": "Microsoft Server Speech Text to Speech Voice (ar-JO, SanaNeural)", "short_name": "ar-JO-SanaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-JO", "local_name": "تيم", "name": "Microsoft Server Speech Text to Speech Voice (ar-JO, TaimNeural)", "short_name": "ar-JO-TaimNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-KW", "local_name": "نورا", "name": "Microsoft Server Speech Text to Speech Voice (ar-KW, NouraNeural)", "short_name": "ar-KW-NouraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-KW", "local_name": "فهد", "name": "Microsoft Server Speech Text to Speech Voice (ar-KW, FahedNeural)", "short_name": "ar-KW-FahedNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-LB", "local_name": "ليلى", "name": "Microsoft Server Speech Text to Speech Voice (ar-LB, LaylaNeural)", "short_name": "ar-LB-LaylaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-LB", "local_name": "رامي", "name": "Microsoft Server Speech Text to Speech Voice (ar-LB, RamiNeural)", "short_name": "ar-LB-RamiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-LY", "local_name": "إيمان", "name": "Microsoft Server Speech Text to Speech Voice (ar-LY, ImanNeural)", "short_name": "ar-LY-ImanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-LY", "local_name": "أحمد", "name": "Microsoft Server Speech Text to Speech Voice (ar-LY, OmarNeural)", "short_name": "ar-LY-OmarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-MA", "local_name": "منى", "name": "Microsoft Server Speech Text to Speech Voice (ar-MA, MounaNeural)", "short_name": "ar-MA-MounaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-MA", "local_name": "جمال", "name": "Microsoft Server Speech Text to Speech Voice (ar-MA, JamalNeural)", "short_name": "ar-MA-JamalNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-OM", "local_name": "عائشة", "name": "Microsoft Server Speech Text to Speech Voice (ar-OM, AyshaNeural)", "short_name": "ar-OM-AyshaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-OM", "local_name": "عبدالله", "name": "Microsoft Server Speech Text to Speech Voice (ar-OM, AbdullahNeural)", "short_name": "ar-OM-AbdullahNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-QA", "local_name": "أمل", "name": "Microsoft Server Speech Text to Speech Voice (ar-QA, AmalNeural)", "short_name": "ar-QA-AmalNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-QA", "local_name": "معاذ", "name": "Microsoft Server Speech Text to Speech Voice (ar-QA, MoazNeural)", "short_name": "ar-QA-MoazNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-SA", "local_name": "زارية", "name": "Microsoft Server Speech Text to Speech Voice (ar-SA, ZariyahNeural)", "short_name": "ar-SA-ZariyahNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-SA", "local_name": "حامد", "name": "Microsoft Server Speech Text to Speech Voice (ar-SA, HamedNeural)", "short_name": "ar-SA-HamedNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-SY", "local_name": "أماني", "name": "Microsoft Server Speech Text to Speech Voice (ar-SY, AmanyNeural)", "short_name": "ar-SY-AmanyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-SY", "local_name": "ليث", "name": "Microsoft Server Speech Text to Speech Voice (ar-SY, LaithNeural)", "short_name": "ar-SY-LaithNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-TN", "local_name": "ريم", "name": "Microsoft Server Speech Text to Speech Voice (ar-TN, ReemNeural)", "short_name": "ar-TN-ReemNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-TN", "local_name": "هادي", "name": "Microsoft Server Speech Text to Speech Voice (ar-TN, HediNeural)", "short_name": "ar-TN-HediNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ar-YE", "local_name": "مريم", "name": "Microsoft Server Speech Text to Speech Voice (ar-YE, MaryamNeural)", "short_name": "ar-YE-MaryamNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ar-YE", "local_name": "صالح", "name": "Microsoft Server Speech Text to Speech Voice (ar-YE, SalehNeural)", "short_name": "ar-YE-SalehNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "az-AZ", "local_name": "Banu", "name": "Microsoft Server Speech Text to Speech Voice (az-AZ, BanuNeural)", "short_name": "az-AZ-BanuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "az-AZ", "local_name": "Babək", "name": "Microsoft Server Speech Text to Speech Voice (az-AZ, BabekNeural)", "short_name": "az-AZ-BabekNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "bg-BG", "local_name": "Калина", "name": "Microsoft Server Speech Text to Speech Voice (bg-BG, KalinaNeural)", "short_name": "bg-BG-KalinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "bg-BG", "local_name": "Борислав", "name": "Microsoft Server Speech Text to Speech Voice (bg-BG, BorislavNeural)", "short_name": "bg-BG-BorislavNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "bn-BD", "local_name": "নবনীতা", "name": "Microsoft Server Speech Text to Speech Voice (bn-BD, NabanitaNeural)", "short_name": "bn-BD-NabanitaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "bn-BD", "local_name": "প্রদ্বীপ", "name": "Microsoft Server Speech Text to Speech Voice (bn-BD, PradeepNeural)", "short_name": "bn-BD-PradeepNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "bn-IN", "local_name": "তানিশা", "name": "Microsoft Server Speech Text to Speech Voice (bn-IN, TanishaaNeural)", "short_name": "bn-IN-TanishaaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "bn-IN", "local_name": "ভাস্কর", "name": "Microsoft Server Speech Text to Speech Voice (bn-IN, BashkarNeural)", "short_name": "bn-IN-BashkarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "bs-BA", "local_name": "Vesna", "name": "Microsoft Server Speech Text to Speech Voice (bs-BA, VesnaNeural)", "short_name": "bs-BA-VesnaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "bs-BA", "local_name": "Goran", "name": "Microsoft Server Speech Text to Speech Voice (bs-BA, GoranNeural)", "short_name": "bs-BA-GoranNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ca-ES", "local_name": "Joana", "name": "Microsoft Server Speech Text to Speech Voice (ca-ES, JoanaNeural)", "short_name": "ca-ES-JoanaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ca-ES", "local_name": "Enric", "name": "Microsoft Server Speech Text to Speech Voice (ca-ES, EnricNeural)", "short_name": "ca-ES-EnricNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ca-ES", "local_name": "Alba", "name": "Microsoft Server Speech Text to Speech Voice (ca-ES, AlbaNeural)", "short_name": "ca-ES-AlbaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "cs-CZ", "local_name": "Vlasta", "name": "Microsoft Server Speech Text to Speech Voice (cs-CZ, VlastaNeural)", "short_name": "cs-CZ-VlastaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "cs-CZ", "local_name": "Antonín", "name": "Microsoft Server Speech Text to Speech Voice (cs-CZ, AntoninNeural)", "short_name": "cs-CZ-AntoninNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "cy-GB", "local_name": "Nia", "name": "Microsoft Server Speech Text to Speech Voice (cy-GB, NiaNeural)", "short_name": "cy-GB-NiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "cy-GB", "local_name": "Aled", "name": "Microsoft Server Speech Text to Speech Voice (cy-GB, AledNeural)", "short_name": "cy-GB-AledNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "da-DK", "local_name": "Christel", "name": "Microsoft Server Speech Text to Speech Voice (da-DK, ChristelNeural)", "short_name": "da-DK-ChristelNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "da-DK", "local_name": "Jeppe", "name": "Microsoft Server Speech Text to Speech Voice (da-DK, JeppeNeural)", "short_name": "da-DK-JeppeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-AT", "local_name": "Ingrid", "name": "Microsoft Server Speech Text to Speech Voice (de-AT, IngridNeural)", "short_name": "de-AT-IngridNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-AT", "local_name": "Jonas", "name": "Microsoft Server Speech Text to Speech Voice (de-AT, JonasNeural)", "short_name": "de-AT-JonasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-CH", "local_name": "Leni", "name": "Microsoft Server Speech Text to Speech Voice (de-CH, LeniNeural)", "short_name": "de-CH-LeniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-CH", "local_name": "Jan", "name": "Microsoft Server Speech Text to Speech Voice (de-CH, JanNeural)", "short_name": "de-CH-JanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Katja", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, KatjaNeural)", "short_name": "de-DE-KatjaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Conrad", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, ConradNeural)", "short_name": "de-DE-ConradNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful"] }, { "gender": 1, "locale": "de-DE", "local_name": "Amala", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, AmalaNeural)", "short_name": "de-DE-AmalaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Bernd", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, BerndNeural)", "short_name": "de-DE-BerndNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Christoph", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, ChristophNeural)", "short_name": "de-DE-ChristophNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Elke", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, ElkeNeural)", "short_name": "de-DE-ElkeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Gisela", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, GiselaNeural)", "short_name": "de-DE-GiselaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Kasper", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, KasperNeural)", "short_name": "de-DE-KasperNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Killian", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, KillianNeural)", "short_name": "de-DE-KillianNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Klarissa", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, KlarissaNeural)", "short_name": "de-DE-KlarissaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Klaus", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, KlausNeural)", "short_name": "de-DE-KlausNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Louisa", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, LouisaNeural)", "short_name": "de-DE-LouisaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Maja", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, MajaNeural)", "short_name": "de-DE-MajaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "de-DE", "local_name": "Ralf", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, RalfNeural)", "short_name": "de-DE-RalfNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "de-DE", "local_name": "Tanja", "name": "Microsoft Server Speech Text to Speech Voice (de-DE, TanjaNeural)", "short_name": "de-DE-TanjaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "el-GR", "local_name": "Αθηνά", "name": "Microsoft Server Speech Text to Speech Voice (el-GR, AthinaNeural)", "short_name": "el-GR-AthinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "el-GR", "local_name": "Νέστορας", "name": "Microsoft Server Speech Text to Speech Voice (el-GR, NestorasNeural)", "short_name": "el-GR-NestorasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Natasha", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, NatashaNeural)", "short_name": "en-AU-NatashaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "William", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, WilliamNeural)", "short_name": "en-AU-WilliamNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Annette", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, AnnetteNeural)", "short_name": "en-AU-AnnetteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Carly", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, CarlyNeural)", "short_name": "en-AU-CarlyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "Darren", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, DarrenNeural)", "short_name": "en-AU-DarrenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "Duncan", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, DuncanNeural)", "short_name": "en-AU-DuncanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Elsie", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, ElsieNeural)", "short_name": "en-AU-ElsieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Freya", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, FreyaNeural)", "short_name": "en-AU-FreyaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Joanne", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, JoanneNeural)", "short_name": "en-AU-JoanneNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "Ken", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, KenNeural)", "short_name": "en-AU-KenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Kim", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, KimNeural)", "short_name": "en-AU-KimNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "Neil", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, NeilNeural)", "short_name": "en-AU-NeilNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-AU", "local_name": "Tim", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, TimNeural)", "short_name": "en-AU-TimNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-AU", "local_name": "Tina", "name": "Microsoft Server Speech Text to Speech Voice (en-AU, TinaNeural)", "short_name": "en-AU-TinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-CA", "local_name": "Clara", "name": "Microsoft Server Speech Text to Speech Voice (en-CA, ClaraNeural)", "short_name": "en-CA-ClaraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-CA", "local_name": "Liam", "name": "Microsoft Server Speech Text to Speech Voice (en-CA, LiamNeural)", "short_name": "en-CA-LiamNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Sonia", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, SoniaNeural)", "short_name": "en-GB-SoniaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "sad"] }, { "gender": 2, "locale": "en-GB", "local_name": "Ryan", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, RyanNeural)", "short_name": "en-GB-RyanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "chat"] }, { "gender": 1, "locale": "en-GB", "local_name": "Libby", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, LibbyNeural)", "short_name": "en-GB-LibbyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Abbi", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, AbbiNeural)", "short_name": "en-GB-AbbiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Alfie", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, AlfieNeural)", "short_name": "en-GB-AlfieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Bella", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, BellaNeural)", "short_name": "en-GB-BellaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Elliot", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, ElliotNeural)", "short_name": "en-GB-ElliotNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Ethan", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, EthanNeural)", "short_name": "en-GB-EthanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Hollie", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, HollieNeural)", "short_name": "en-GB-HollieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Maisie", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, MaisieNeural)", "short_name": "en-GB-MaisieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Noah", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, NoahNeural)", "short_name": "en-GB-NoahNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Oliver", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, OliverNeural)", "short_name": "en-GB-OliverNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Olivia", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, OliviaNeural)", "short_name": "en-GB-OliviaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-GB", "local_name": "Thomas", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, ThomasNeural)", "short_name": "en-GB-ThomasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-GB", "local_name": "Mia", "name": "Microsoft Server Speech Text to Speech Voice (en-GB, MiaNeural)", "short_name": "en-GB-MiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-HK", "local_name": "Yan", "name": "Microsoft Server Speech Text to Speech Voice (en-HK, YanNeural)", "short_name": "en-HK-YanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-HK", "local_name": "Sam", "name": "Microsoft Server Speech Text to Speech Voice (en-HK, SamNeural)", "short_name": "en-HK-SamNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-IE", "local_name": "Emily", "name": "Microsoft Server Speech Text to Speech Voice (en-IE, EmilyNeural)", "short_name": "en-IE-EmilyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-IE", "local_name": "Connor", "name": "Microsoft Server Speech Text to Speech Voice (en-IE, ConnorNeural)", "short_name": "en-IE-ConnorNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-IN", "local_name": "Neerja", "name": "Microsoft Server Speech Text to Speech Voice (en-IN, NeerjaNeural)", "short_name": "en-IN-NeerjaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-IN", "local_name": "Prabhat", "name": "Microsoft Server Speech Text to Speech Voice (en-IN, PrabhatNeural)", "short_name": "en-IN-PrabhatNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-KE", "local_name": "Asilia", "name": "Microsoft Server Speech Text to Speech Voice (en-KE, AsiliaNeural)", "short_name": "en-KE-AsiliaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-KE", "local_name": "Chilemba", "name": "Microsoft Server Speech Text to Speech Voice (en-KE, ChilembaNeural)", "short_name": "en-KE-ChilembaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-NG", "local_name": "Ezinne", "name": "Microsoft Server Speech Text to Speech Voice (en-NG, EzinneNeural)", "short_name": "en-NG-EzinneNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-NG", "local_name": "Abeo", "name": "Microsoft Server Speech Text to Speech Voice (en-NG, AbeoNeural)", "short_name": "en-NG-AbeoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-NZ", "local_name": "Molly", "name": "Microsoft Server Speech Text to Speech Voice (en-NZ, MollyNeural)", "short_name": "en-NZ-MollyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-NZ", "local_name": "Mitchell", "name": "Microsoft Server Speech Text to Speech Voice (en-NZ, MitchellNeural)", "short_name": "en-NZ-MitchellNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-PH", "local_name": "Rosa", "name": "Microsoft Server Speech Text to Speech Voice (en-PH, RosaNeural)", "short_name": "en-PH-RosaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-PH", "local_name": "James", "name": "Microsoft Server Speech Text to Speech Voice (en-PH, JamesNeural)", "short_name": "en-PH-JamesNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-SG", "local_name": "Luna", "name": "Microsoft Server Speech Text to Speech Voice (en-SG, LunaNeural)", "short_name": "en-SG-LunaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-SG", "local_name": "Wayne", "name": "Microsoft Server Speech Text to Speech Voice (en-SG, WayneNeural)", "short_name": "en-SG-WayneNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-TZ", "local_name": "Imani", "name": "Microsoft Server Speech Text to Speech Voice (en-TZ, ImaniNeural)", "short_name": "en-TZ-ImaniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-TZ", "local_name": "Elimu", "name": "Microsoft Server Speech Text to Speech Voice (en-TZ, ElimuNeural)", "short_name": "en-TZ-ElimuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Jenny Multilingual", "name": "Microsoft Server Speech Text to Speech Voice (en-US, JennyMultilingualNeural)", "short_name": "en-US-JennyMultilingualNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Jenny", "name": "Microsoft Server Speech Text to Speech Voice (en-US, JennyNeural)", "short_name": "en-US-JennyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["assistant", "chat", "customerservice", "newscast", "angry", "cheerful", "sad", "excited", "friendly", "terrified", "shouting", "unfriendly", "whispering", "hopeful" ] }, { "gender": 2, "locale": "en-US", "local_name": "Guy", "name": "Microsoft Server Speech Text to Speech Voice (en-US, GuyNeural)", "short_name": "en-US-GuyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["newscast", "angry", "cheerful", "sad", "excited", "friendly", "terrified", "shouting", "unfriendly", "whispering", "hopeful" ] }, { "gender": 1, "locale": "en-US", "local_name": "Aria", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)", "short_name": "en-US-AriaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["chat", "customerservice", "narration-professional", "newscast-casual", "newscast-formal", "cheerful", "empathetic", "angry", "sad", "excited", "friendly", "terrified", "shouting", "unfriendly", "whispering", "hopeful" ] }, { "gender": 2, "locale": "en-US", "local_name": "Davis", "name": "Microsoft Server Speech Text to Speech Voice (en-US, DavisNeural)", "short_name": "en-US-DavisNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["chat", "angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 1, "locale": "en-US", "local_name": "Amber", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AmberNeural)", "short_name": "en-US-AmberNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Ana", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AnaNeural)", "short_name": "en-US-AnaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Ashley", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AshleyNeural)", "short_name": "en-US-AshleyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-US", "local_name": "Brandon", "name": "Microsoft Server Speech Text to Speech Voice (en-US, BrandonNeural)", "short_name": "en-US-BrandonNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-US", "local_name": "Christopher", "name": "Microsoft Server Speech Text to Speech Voice (en-US, ChristopherNeural)", "short_name": "en-US-ChristopherNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Cora", "name": "Microsoft Server Speech Text to Speech Voice (en-US, CoraNeural)", "short_name": "en-US-CoraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Elizabeth", "name": "Microsoft Server Speech Text to Speech Voice (en-US, ElizabethNeural)", "short_name": "en-US-ElizabethNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-US", "local_name": "Eric", "name": "Microsoft Server Speech Text to Speech Voice (en-US, EricNeural)", "short_name": "en-US-EricNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-US", "local_name": "Jacob", "name": "Microsoft Server Speech Text to Speech Voice (en-US, JacobNeural)", "short_name": "en-US-JacobNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Jane", "name": "Microsoft Server Speech Text to Speech Voice (en-US, JaneNeural)", "short_name": "en-US-JaneNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 2, "locale": "en-US", "local_name": "Jason", "name": "Microsoft Server Speech Text to Speech Voice (en-US, JasonNeural)", "short_name": "en-US-JasonNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 1, "locale": "en-US", "local_name": "Michelle", "name": "Microsoft Server Speech Text to Speech Voice (en-US, MichelleNeural)", "short_name": "en-US-MichelleNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Monica", "name": "Microsoft Server Speech Text to Speech Voice (en-US, MonicaNeural)", "short_name": "en-US-MonicaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Nancy", "name": "Microsoft Server Speech Text to Speech Voice (en-US, NancyNeural)", "short_name": "en-US-NancyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 2, "locale": "en-US", "local_name": "Roger", "name": "Microsoft Server Speech Text to Speech Voice (en-US, RogerNeural)", "short_name": "en-US-RogerNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Sara", "name": "Microsoft Server Speech Text to Speech Voice (en-US, SaraNeural)", "short_name": "en-US-SaraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 2, "locale": "en-US", "local_name": "Steffan", "name": "Microsoft Server Speech Text to Speech Voice (en-US, SteffanNeural)", "short_name": "en-US-SteffanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-US", "local_name": "Tony", "name": "Microsoft Server Speech Text to Speech Voice (en-US, TonyNeural)", "short_name": "en-US-TonyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "cheerful", "excited", "friendly", "hopeful", "sad", "shouting", "terrified", "unfriendly", "whispering" ] }, { "gender": 2, "locale": "en-US", "local_name": "AIGenerate1", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AIGenerate1Neural)", "short_name": "en-US-AIGenerate1Neural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "AIGenerate2", "name": "Microsoft Server Speech Text to Speech Voice (en-US, AIGenerate2Neural)", "short_name": "en-US-AIGenerate2Neural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-US", "local_name": "Blue", "name": "Microsoft Server Speech Text to Speech Voice (en-US, BlueNeural)", "short_name": "en-US-BlueNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "en-ZA", "local_name": "Leah", "name": "Microsoft Server Speech Text to Speech Voice (en-ZA, LeahNeural)", "short_name": "en-ZA-LeahNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "en-ZA", "local_name": "Luke", "name": "Microsoft Server Speech Text to Speech Voice (en-ZA, LukeNeural)", "short_name": "en-ZA-LukeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-AR", "local_name": "Elena", "name": "Microsoft Server Speech Text to Speech Voice (es-AR, ElenaNeural)", "short_name": "es-AR-ElenaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-AR", "local_name": "Tomas", "name": "Microsoft Server Speech Text to Speech Voice (es-AR, TomasNeural)", "short_name": "es-AR-TomasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-BO", "local_name": "Sofia", "name": "Microsoft Server Speech Text to Speech Voice (es-BO, SofiaNeural)", "short_name": "es-BO-SofiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-BO", "local_name": "Marcelo", "name": "Microsoft Server Speech Text to Speech Voice (es-BO, MarceloNeural)", "short_name": "es-BO-MarceloNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-CL", "local_name": "Catalina", "name": "Microsoft Server Speech Text to Speech Voice (es-CL, CatalinaNeural)", "short_name": "es-CL-CatalinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-CL", "local_name": "Lorenzo", "name": "Microsoft Server Speech Text to Speech Voice (es-CL, LorenzoNeural)", "short_name": "es-CL-LorenzoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-CO", "local_name": "Salome", "name": "Microsoft Server Speech Text to Speech Voice (es-CO, SalomeNeural)", "short_name": "es-CO-SalomeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-CO", "local_name": "Gonzalo", "name": "Microsoft Server Speech Text to Speech Voice (es-CO, GonzaloNeural)", "short_name": "es-CO-GonzaloNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-CR", "local_name": "María", "name": "Microsoft Server Speech Text to Speech Voice (es-CR, MariaNeural)", "short_name": "es-CR-MariaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-CR", "local_name": "Juan", "name": "Microsoft Server Speech Text to Speech Voice (es-CR, JuanNeural)", "short_name": "es-CR-JuanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-CU", "local_name": "Belkys", "name": "Microsoft Server Speech Text to Speech Voice (es-CU, BelkysNeural)", "short_name": "es-CU-BelkysNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-CU", "local_name": "Manuel", "name": "Microsoft Server Speech Text to Speech Voice (es-CU, ManuelNeural)", "short_name": "es-CU-ManuelNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-DO", "local_name": "Ramona", "name": "Microsoft Server Speech Text to Speech Voice (es-DO, RamonaNeural)", "short_name": "es-DO-RamonaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-DO", "local_name": "Emilio", "name": "Microsoft Server Speech Text to Speech Voice (es-DO, EmilioNeural)", "short_name": "es-DO-EmilioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-EC", "local_name": "Andrea", "name": "Microsoft Server Speech Text to Speech Voice (es-EC, AndreaNeural)", "short_name": "es-EC-AndreaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-EC", "local_name": "Luis", "name": "Microsoft Server Speech Text to Speech Voice (es-EC, LuisNeural)", "short_name": "es-EC-LuisNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Elvira", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, ElviraNeural)", "short_name": "es-ES-ElviraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Álvaro", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, AlvaroNeural)", "short_name": "es-ES-AlvaroNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Abril", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, AbrilNeural)", "short_name": "es-ES-AbrilNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Arnau", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, ArnauNeural)", "short_name": "es-ES-ArnauNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Dario", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, DarioNeural)", "short_name": "es-ES-DarioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Elias", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, EliasNeural)", "short_name": "es-ES-EliasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Estrella", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, EstrellaNeural)", "short_name": "es-ES-EstrellaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Irene", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, IreneNeural)", "short_name": "es-ES-IreneNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Laia", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, LaiaNeural)", "short_name": "es-ES-LaiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Lia", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, LiaNeural)", "short_name": "es-ES-LiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Nil", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, NilNeural)", "short_name": "es-ES-NilNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Saul", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, SaulNeural)", "short_name": "es-ES-SaulNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-ES", "local_name": "Teo", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, TeoNeural)", "short_name": "es-ES-TeoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Triana", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, TrianaNeural)", "short_name": "es-ES-TrianaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-ES", "local_name": "Vera", "name": "Microsoft Server Speech Text to Speech Voice (es-ES, VeraNeural)", "short_name": "es-ES-VeraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-GQ", "local_name": "Teresa", "name": "Microsoft Server Speech Text to Speech Voice (es-GQ, TeresaNeural)", "short_name": "es-GQ-TeresaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-GQ", "local_name": "Javier", "name": "Microsoft Server Speech Text to Speech Voice (es-GQ, JavierNeural)", "short_name": "es-GQ-JavierNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-GT", "local_name": "Marta", "name": "Microsoft Server Speech Text to Speech Voice (es-GT, MartaNeural)", "short_name": "es-GT-MartaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-GT", "local_name": "Andrés", "name": "Microsoft Server Speech Text to Speech Voice (es-GT, AndresNeural)", "short_name": "es-GT-AndresNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-HN", "local_name": "Karla", "name": "Microsoft Server Speech Text to Speech Voice (es-HN, KarlaNeural)", "short_name": "es-HN-KarlaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-HN", "local_name": "Carlos", "name": "Microsoft Server Speech Text to Speech Voice (es-HN, CarlosNeural)", "short_name": "es-HN-CarlosNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Dalia", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, DaliaNeural)", "short_name": "es-MX-DaliaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Jorge", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, JorgeNeural)", "short_name": "es-MX-JorgeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "chat"] }, { "gender": 1, "locale": "es-MX", "local_name": "Beatriz", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, BeatrizNeural)", "short_name": "es-MX-BeatrizNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Candela", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, CandelaNeural)", "short_name": "es-MX-CandelaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Carlota", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, CarlotaNeural)", "short_name": "es-MX-CarlotaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Cecilio", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, CecilioNeural)", "short_name": "es-MX-CecilioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Gerardo", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, GerardoNeural)", "short_name": "es-MX-GerardoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Larissa", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, LarissaNeural)", "short_name": "es-MX-LarissaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Liberto", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, LibertoNeural)", "short_name": "es-MX-LibertoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Luciano", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, LucianoNeural)", "short_name": "es-MX-LucianoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Marina", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, MarinaNeural)", "short_name": "es-MX-MarinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Nuria", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, NuriaNeural)", "short_name": "es-MX-NuriaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Pelayo", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, PelayoNeural)", "short_name": "es-MX-PelayoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-MX", "local_name": "Renata", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, RenataNeural)", "short_name": "es-MX-RenataNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-MX", "local_name": "Yago", "name": "Microsoft Server Speech Text to Speech Voice (es-MX, YagoNeural)", "short_name": "es-MX-YagoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-NI", "local_name": "Yolanda", "name": "Microsoft Server Speech Text to Speech Voice (es-NI, YolandaNeural)", "short_name": "es-NI-YolandaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-NI", "local_name": "Federico", "name": "Microsoft Server Speech Text to Speech Voice (es-NI, FedericoNeural)", "short_name": "es-NI-FedericoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-PA", "local_name": "Margarita", "name": "Microsoft Server Speech Text to Speech Voice (es-PA, MargaritaNeural)", "short_name": "es-PA-MargaritaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-PA", "local_name": "Roberto", "name": "Microsoft Server Speech Text to Speech Voice (es-PA, RobertoNeural)", "short_name": "es-PA-RobertoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-PE", "local_name": "Camila", "name": "Microsoft Server Speech Text to Speech Voice (es-PE, CamilaNeural)", "short_name": "es-PE-CamilaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-PE", "local_name": "Alex", "name": "Microsoft Server Speech Text to Speech Voice (es-PE, AlexNeural)", "short_name": "es-PE-AlexNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-PR", "local_name": "Karina", "name": "Microsoft Server Speech Text to Speech Voice (es-PR, KarinaNeural)", "short_name": "es-PR-KarinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-PR", "local_name": "Víctor", "name": "Microsoft Server Speech Text to Speech Voice (es-PR, VictorNeural)", "short_name": "es-PR-VictorNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-PY", "local_name": "Tania", "name": "Microsoft Server Speech Text to Speech Voice (es-PY, TaniaNeural)", "short_name": "es-PY-TaniaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-PY", "local_name": "Mario", "name": "Microsoft Server Speech Text to Speech Voice (es-PY, MarioNeural)", "short_name": "es-PY-MarioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-SV", "local_name": "Lorena", "name": "Microsoft Server Speech Text to Speech Voice (es-SV, LorenaNeural)", "short_name": "es-SV-LorenaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-SV", "local_name": "Rodrigo", "name": "Microsoft Server Speech Text to Speech Voice (es-SV, RodrigoNeural)", "short_name": "es-SV-RodrigoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-US", "local_name": "Paloma", "name": "Microsoft Server Speech Text to Speech Voice (es-US, PalomaNeural)", "short_name": "es-US-PalomaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-US", "local_name": "Alonso", "name": "Microsoft Server Speech Text to Speech Voice (es-US, AlonsoNeural)", "short_name": "es-US-AlonsoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-UY", "local_name": "Valentina", "name": "Microsoft Server Speech Text to Speech Voice (es-UY, ValentinaNeural)", "short_name": "es-UY-ValentinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-UY", "local_name": "Mateo", "name": "Microsoft Server Speech Text to Speech Voice (es-UY, MateoNeural)", "short_name": "es-UY-MateoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "es-VE", "local_name": "Paola", "name": "Microsoft Server Speech Text to Speech Voice (es-VE, PaolaNeural)", "short_name": "es-VE-PaolaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "es-VE", "local_name": "Sebastián", "name": "Microsoft Server Speech Text to Speech Voice (es-VE, SebastianNeural)", "short_name": "es-VE-SebastianNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "et-EE", "local_name": "Anu", "name": "Microsoft Server Speech Text to Speech Voice (et-EE, AnuNeural)", "short_name": "et-EE-AnuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "et-EE", "local_name": "Kert", "name": "Microsoft Server Speech Text to Speech Voice (et-EE, KertNeural)", "short_name": "et-EE-KertNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "eu-ES", "local_name": "Ainhoa", "name": "Microsoft Server Speech Text to Speech Voice (eu-ES, AinhoaNeural)", "short_name": "eu-ES-AinhoaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "eu-ES", "local_name": "Ander", "name": "Microsoft Server Speech Text to Speech Voice (eu-ES, AnderNeural)", "short_name": "eu-ES-AnderNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fa-IR", "local_name": "دلارا", "name": "Microsoft Server Speech Text to Speech Voice (fa-IR, DilaraNeural)", "short_name": "fa-IR-DilaraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fa-IR", "local_name": "فرید", "name": "Microsoft Server Speech Text to Speech Voice (fa-IR, FaridNeural)", "short_name": "fa-IR-FaridNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fi-FI", "local_name": "Selma", "name": "Microsoft Server Speech Text to Speech Voice (fi-FI, SelmaNeural)", "short_name": "fi-FI-SelmaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fi-FI", "local_name": "Harri", "name": "Microsoft Server Speech Text to Speech Voice (fi-FI, HarriNeural)", "short_name": "fi-FI-HarriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fi-FI", "local_name": "Noora", "name": "Microsoft Server Speech Text to Speech Voice (fi-FI, NooraNeural)", "short_name": "fi-FI-NooraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fil-PH", "local_name": "Blessica", "name": "Microsoft Server Speech Text to Speech Voice (fil-PH, BlessicaNeural)", "short_name": "fil-PH-BlessicaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fil-PH", "local_name": "Angelo", "name": "Microsoft Server Speech Text to Speech Voice (fil-PH, AngeloNeural)", "short_name": "fil-PH-AngeloNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-BE", "local_name": "Charline", "name": "Microsoft Server Speech Text to Speech Voice (fr-BE, CharlineNeural)", "short_name": "fr-BE-CharlineNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-BE", "local_name": "Gerard", "name": "Microsoft Server Speech Text to Speech Voice (fr-BE, GerardNeural)", "short_name": "fr-BE-GerardNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-CA", "local_name": "Sylvie", "name": "Microsoft Server Speech Text to Speech Voice (fr-CA, SylvieNeural)", "short_name": "fr-CA-SylvieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-CA", "local_name": "Jean", "name": "Microsoft Server Speech Text to Speech Voice (fr-CA, JeanNeural)", "short_name": "fr-CA-JeanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-CA", "local_name": "Antoine", "name": "Microsoft Server Speech Text to Speech Voice (fr-CA, AntoineNeural)", "short_name": "fr-CA-AntoineNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-CH", "local_name": "Ariane", "name": "Microsoft Server Speech Text to Speech Voice (fr-CH, ArianeNeural)", "short_name": "fr-CH-ArianeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-CH", "local_name": "Fabrice", "name": "Microsoft Server Speech Text to Speech Voice (fr-CH, FabriceNeural)", "short_name": "fr-CH-FabriceNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Denise", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, DeniseNeural)", "short_name": "fr-FR-DeniseNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "sad"] }, { "gender": 2, "locale": "fr-FR", "local_name": "Henri", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, HenriNeural)", "short_name": "fr-FR-HenriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "sad"] }, { "gender": 2, "locale": "fr-FR", "local_name": "Alain", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, AlainNeural)", "short_name": "fr-FR-AlainNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Brigitte", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, BrigitteNeural)", "short_name": "fr-FR-BrigitteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Celeste", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, CelesteNeural)", "short_name": "fr-FR-CelesteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-FR", "local_name": "Claude", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, ClaudeNeural)", "short_name": "fr-FR-ClaudeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Coralie", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, CoralieNeural)", "short_name": "fr-FR-CoralieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Eloise", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, EloiseNeural)", "short_name": "fr-FR-EloiseNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Jacqueline", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, JacquelineNeural)", "short_name": "fr-FR-JacquelineNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-FR", "local_name": "Jerome", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, JeromeNeural)", "short_name": "fr-FR-JeromeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Josephine", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, JosephineNeural)", "short_name": "fr-FR-JosephineNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-FR", "local_name": "Maurice", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, MauriceNeural)", "short_name": "fr-FR-MauriceNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "fr-FR", "local_name": "Yves", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, YvesNeural)", "short_name": "fr-FR-YvesNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "fr-FR", "local_name": "Yvette", "name": "Microsoft Server Speech Text to Speech Voice (fr-FR, YvetteNeural)", "short_name": "fr-FR-YvetteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ga-IE", "local_name": "Orla", "name": "Microsoft Server Speech Text to Speech Voice (ga-IE, OrlaNeural)", "short_name": "ga-IE-OrlaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ga-IE", "local_name": "Colm", "name": "Microsoft Server Speech Text to Speech Voice (ga-IE, ColmNeural)", "short_name": "ga-IE-ColmNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "gl-ES", "local_name": "Sabela", "name": "Microsoft Server Speech Text to Speech Voice (gl-ES, SabelaNeural)", "short_name": "gl-ES-SabelaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "gl-ES", "local_name": "Roi", "name": "Microsoft Server Speech Text to Speech Voice (gl-ES, RoiNeural)", "short_name": "gl-ES-RoiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "gu-IN", "local_name": "ધ્વની", "name": "Microsoft Server Speech Text to Speech Voice (gu-IN, DhwaniNeural)", "short_name": "gu-IN-DhwaniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "gu-IN", "local_name": "નિરંજન", "name": "Microsoft Server Speech Text to Speech Voice (gu-IN, NiranjanNeural)", "short_name": "gu-IN-NiranjanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "he-IL", "local_name": "הילה", "name": "Microsoft Server Speech Text to Speech Voice (he-IL, HilaNeural)", "short_name": "he-IL-HilaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "he-IL", "local_name": "אברי", "name": "Microsoft Server Speech Text to Speech Voice (he-IL, AvriNeural)", "short_name": "he-IL-AvriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "hi-IN", "local_name": "स्वरा", "name": "Microsoft Server Speech Text to Speech Voice (hi-IN, SwaraNeural)", "short_name": "hi-IN-SwaraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "hi-IN", "local_name": "मधुर", "name": "Microsoft Server Speech Text to Speech Voice (hi-IN, MadhurNeural)", "short_name": "hi-IN-MadhurNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "hr-HR", "local_name": "Gabrijela", "name": "Microsoft Server Speech Text to Speech Voice (hr-HR, GabrijelaNeural)", "short_name": "hr-HR-GabrijelaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "hr-HR", "local_name": "Srećko", "name": "Microsoft Server Speech Text to Speech Voice (hr-HR, SreckoNeural)", "short_name": "hr-HR-SreckoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "hu-HU", "local_name": "Noémi", "name": "Microsoft Server Speech Text to Speech Voice (hu-HU, NoemiNeural)", "short_name": "hu-HU-NoemiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "hu-HU", "local_name": "Tamás", "name": "Microsoft Server Speech Text to Speech Voice (hu-HU, TamasNeural)", "short_name": "hu-HU-TamasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "hy-AM", "local_name": "Անահիտ", "name": "Microsoft Server Speech Text to Speech Voice (hy-AM, AnahitNeural)", "short_name": "hy-AM-AnahitNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "hy-AM", "local_name": "Հայկ", "name": "Microsoft Server Speech Text to Speech Voice (hy-AM, HaykNeural)", "short_name": "hy-AM-HaykNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "id-ID", "local_name": "Gadis", "name": "Microsoft Server Speech Text to Speech Voice (id-ID, GadisNeural)", "short_name": "id-ID-GadisNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "id-ID", "local_name": "Ardi", "name": "Microsoft Server Speech Text to Speech Voice (id-ID, ArdiNeural)", "short_name": "id-ID-ArdiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "is-IS", "local_name": "Guðrún", "name": "Microsoft Server Speech Text to Speech Voice (is-IS, GudrunNeural)", "short_name": "is-IS-GudrunNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "is-IS", "local_name": "Gunnar", "name": "Microsoft Server Speech Text to Speech Voice (is-IS, GunnarNeural)", "short_name": "is-IS-GunnarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Elsa", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, ElsaNeural)", "short_name": "it-IT-ElsaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Isabella", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, IsabellaNeural)", "short_name": "it-IT-IsabellaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["cheerful", "chat"] }, { "gender": 2, "locale": "it-IT", "local_name": "Diego", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, DiegoNeural)", "short_name": "it-IT-DiegoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Benigno", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, BenignoNeural)", "short_name": "it-IT-BenignoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Calimero", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, CalimeroNeural)", "short_name": "it-IT-CalimeroNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Cataldo", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, CataldoNeural)", "short_name": "it-IT-CataldoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Fabiola", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, FabiolaNeural)", "short_name": "it-IT-FabiolaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Fiamma", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, FiammaNeural)", "short_name": "it-IT-FiammaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Gianni", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, GianniNeural)", "short_name": "it-IT-GianniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Imelda", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, ImeldaNeural)", "short_name": "it-IT-ImeldaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Irma", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, IrmaNeural)", "short_name": "it-IT-IrmaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Lisandro", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, LisandroNeural)", "short_name": "it-IT-LisandroNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Palmira", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, PalmiraNeural)", "short_name": "it-IT-PalmiraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "it-IT", "local_name": "Pierina", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, PierinaNeural)", "short_name": "it-IT-PierinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "it-IT", "local_name": "Rinaldo", "name": "Microsoft Server Speech Text to Speech Voice (it-IT, RinaldoNeural)", "short_name": "it-IT-RinaldoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ja-JP", "local_name": "七海", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, NanamiNeural)", "short_name": "ja-JP-NanamiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["chat", "customerservice", "cheerful"] }, { "gender": 2, "locale": "ja-JP", "local_name": "圭太", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, KeitaNeural)", "short_name": "ja-JP-KeitaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ja-JP", "local_name": "碧衣", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, AoiNeural)", "short_name": "ja-JP-AoiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ja-JP", "local_name": "大智", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, DaichiNeural)", "short_name": "ja-JP-DaichiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ja-JP", "local_name": "真夕", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, MayuNeural)", "short_name": "ja-JP-MayuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ja-JP", "local_name": "直紀", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, NaokiNeural)", "short_name": "ja-JP-NaokiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ja-JP", "local_name": "志織", "name": "Microsoft Server Speech Text to Speech Voice (ja-JP, ShioriNeural)", "short_name": "ja-JP-ShioriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "jv-ID", "local_name": "Siti", "name": "Microsoft Server Speech Text to Speech Voice (jv-ID, SitiNeural)", "short_name": "jv-ID-SitiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "jv-ID", "local_name": "Dimas", "name": "Microsoft Server Speech Text to Speech Voice (jv-ID, DimasNeural)", "short_name": "jv-ID-DimasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ka-GE", "local_name": "ეკა", "name": "Microsoft Server Speech Text to Speech Voice (ka-GE, EkaNeural)", "short_name": "ka-GE-EkaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ka-GE", "local_name": "გიორგი", "name": "Microsoft Server Speech Text to Speech Voice (ka-GE, GiorgiNeural)", "short_name": "ka-GE-GiorgiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "kk-KZ", "local_name": "Айгүл", "name": "Microsoft Server Speech Text to Speech Voice (kk-KZ, AigulNeural)", "short_name": "kk-KZ-AigulNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "kk-KZ", "local_name": "Дәулет", "name": "Microsoft Server Speech Text to Speech Voice (kk-KZ, DauletNeural)", "short_name": "kk-KZ-DauletNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "km-KH", "local_name": "ស្រីមុំ", "name": "Microsoft Server Speech Text to Speech Voice (km-KH, SreymomNeural)", "short_name": "km-KH-SreymomNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "km-KH", "local_name": "ពិសិដ្ឋ", "name": "Microsoft Server Speech Text to Speech Voice (km-KH, PisethNeural)", "short_name": "km-KH-PisethNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "kn-IN", "local_name": "ಸಪ್ನಾ", "name": "Microsoft Server Speech Text to Speech Voice (kn-IN, SapnaNeural)", "short_name": "kn-IN-SapnaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "kn-IN", "local_name": "ಗಗನ್", "name": "Microsoft Server Speech Text to Speech Voice (kn-IN, GaganNeural)", "short_name": "kn-IN-GaganNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ko-KR", "local_name": "선히", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, SunHiNeural)", "short_name": "ko-KR-SunHiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ko-KR", "local_name": "인준", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, InJoonNeural)", "short_name": "ko-KR-InJoonNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ko-KR", "local_name": "봉진", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, BongJinNeural)", "short_name": "ko-KR-BongJinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ko-KR", "local_name": "국민", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, GookMinNeural)", "short_name": "ko-KR-GookMinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ko-KR", "local_name": "지민", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, JiMinNeural)", "short_name": "ko-KR-JiMinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ko-KR", "local_name": "서현", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, SeoHyeonNeural)", "short_name": "ko-KR-SeoHyeonNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ko-KR", "local_name": "순복", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, SoonBokNeural)", "short_name": "ko-KR-SoonBokNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ko-KR", "local_name": "유진", "name": "Microsoft Server Speech Text to Speech Voice (ko-KR, YuJinNeural)", "short_name": "ko-KR-YuJinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "lo-LA", "local_name": "ແກ້ວມະນີ", "name": "Microsoft Server Speech Text to Speech Voice (lo-LA, KeomanyNeural)", "short_name": "lo-LA-KeomanyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "lo-LA", "local_name": "ຈັນທະວົງ", "name": "Microsoft Server Speech Text to Speech Voice (lo-LA, ChanthavongNeural)", "short_name": "lo-LA-ChanthavongNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "lt-LT", "local_name": "Ona", "name": "Microsoft Server Speech Text to Speech Voice (lt-LT, OnaNeural)", "short_name": "lt-LT-OnaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "lt-LT", "local_name": "Leonas", "name": "Microsoft Server Speech Text to Speech Voice (lt-LT, LeonasNeural)", "short_name": "lt-LT-LeonasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "lv-LV", "local_name": "Everita", "name": "Microsoft Server Speech Text to Speech Voice (lv-LV, EveritaNeural)", "short_name": "lv-LV-EveritaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "lv-LV", "local_name": "Nils", "name": "Microsoft Server Speech Text to Speech Voice (lv-LV, NilsNeural)", "short_name": "lv-LV-NilsNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "mk-MK", "local_name": "Марија", "name": "Microsoft Server Speech Text to Speech Voice (mk-MK, MarijaNeural)", "short_name": "mk-MK-MarijaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "mk-MK", "local_name": "Александар", "name": "Microsoft Server Speech Text to Speech Voice (mk-MK, AleksandarNeural)", "short_name": "mk-MK-AleksandarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ml-IN", "local_name": "ശോഭന", "name": "Microsoft Server Speech Text to Speech Voice (ml-IN, SobhanaNeural)", "short_name": "ml-IN-SobhanaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ml-IN", "local_name": "മിഥുൻ", "name": "Microsoft Server Speech Text to Speech Voice (ml-IN, MidhunNeural)", "short_name": "ml-IN-MidhunNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "mn-MN", "local_name": "Есүй", "name": "Microsoft Server Speech Text to Speech Voice (mn-MN, YesuiNeural)", "short_name": "mn-MN-YesuiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "mn-MN", "local_name": "Батаа", "name": "Microsoft Server Speech Text to Speech Voice (mn-MN, BataaNeural)", "short_name": "mn-MN-BataaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "mr-IN", "local_name": "आरोही", "name": "Microsoft Server Speech Text to Speech Voice (mr-IN, AarohiNeural)", "short_name": "mr-IN-AarohiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "mr-IN", "local_name": "मनोहर", "name": "Microsoft Server Speech Text to Speech Voice (mr-IN, ManoharNeural)", "short_name": "mr-IN-ManoharNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ms-MY", "local_name": "Yasmin", "name": "Microsoft Server Speech Text to Speech Voice (ms-MY, YasminNeural)", "short_name": "ms-MY-YasminNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ms-MY", "local_name": "Osman", "name": "Microsoft Server Speech Text to Speech Voice (ms-MY, OsmanNeural)", "short_name": "ms-MY-OsmanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "mt-MT", "local_name": "Grace", "name": "Microsoft Server Speech Text to Speech Voice (mt-MT, GraceNeural)", "short_name": "mt-MT-GraceNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "mt-MT", "local_name": "Joseph", "name": "Microsoft Server Speech Text to Speech Voice (mt-MT, JosephNeural)", "short_name": "mt-MT-JosephNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "my-MM", "local_name": "နီလာ", "name": "Microsoft Server Speech Text to Speech Voice (my-MM, NilarNeural)", "short_name": "my-MM-NilarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "my-MM", "local_name": "သီဟ", "name": "Microsoft Server Speech Text to Speech Voice (my-MM, ThihaNeural)", "short_name": "my-MM-ThihaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "nb-NO", "local_name": "Pernille", "name": "Microsoft Server Speech Text to Speech Voice (nb-NO, PernilleNeural)", "short_name": "nb-NO-PernilleNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "nb-NO", "local_name": "Finn", "name": "Microsoft Server Speech Text to Speech Voice (nb-NO, FinnNeural)", "short_name": "nb-NO-FinnNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "nb-NO", "local_name": "Iselin", "name": "Microsoft Server Speech Text to Speech Voice (nb-NO, IselinNeural)", "short_name": "nb-NO-IselinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ne-NP", "local_name": "हेमकला", "name": "Microsoft Server Speech Text to Speech Voice (ne-NP, HemkalaNeural)", "short_name": "ne-NP-HemkalaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ne-NP", "local_name": "सागर", "name": "Microsoft Server Speech Text to Speech Voice (ne-NP, SagarNeural)", "short_name": "ne-NP-SagarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "nl-BE", "local_name": "Dena", "name": "Microsoft Server Speech Text to Speech Voice (nl-BE, DenaNeural)", "short_name": "nl-BE-DenaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "nl-BE", "local_name": "Arnaud", "name": "Microsoft Server Speech Text to Speech Voice (nl-BE, ArnaudNeural)", "short_name": "nl-BE-ArnaudNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "nl-NL", "local_name": "Fenna", "name": "Microsoft Server Speech Text to Speech Voice (nl-NL, FennaNeural)", "short_name": "nl-NL-FennaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "nl-NL", "local_name": "Maarten", "name": "Microsoft Server Speech Text to Speech Voice (nl-NL, MaartenNeural)", "short_name": "nl-NL-MaartenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "nl-NL", "local_name": "Colette", "name": "Microsoft Server Speech Text to Speech Voice (nl-NL, ColetteNeural)", "short_name": "nl-NL-ColetteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pl-PL", "local_name": "Agnieszka", "name": "Microsoft Server Speech Text to Speech Voice (pl-PL, AgnieszkaNeural)", "short_name": "pl-PL-AgnieszkaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pl-PL", "local_name": "Marek", "name": "Microsoft Server Speech Text to Speech Voice (pl-PL, MarekNeural)", "short_name": "pl-PL-MarekNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pl-PL", "local_name": "Zofia", "name": "Microsoft Server Speech Text to Speech Voice (pl-PL, ZofiaNeural)", "short_name": "pl-PL-ZofiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ps-AF", "local_name": "لطيفه", "name": "Microsoft Server Speech Text to Speech Voice (ps-AF, LatifaNeural)", "short_name": "ps-AF-LatifaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ps-AF", "local_name": " ګل نواز", "name": "Microsoft Server Speech Text to Speech Voice (ps-AF, GulNawazNeural)", "short_name": "ps-AF-GulNawazNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Francisca", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, FranciscaNeural)", "short_name": "pt-BR-FranciscaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm"] }, { "gender": 2, "locale": "pt-BR", "local_name": "Antônio", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, AntonioNeural)", "short_name": "pt-BR-AntonioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Brenda", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, BrendaNeural)", "short_name": "pt-BR-BrendaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Donato", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, DonatoNeural)", "short_name": "pt-BR-DonatoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Elza", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, ElzaNeural)", "short_name": "pt-BR-ElzaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Fabio", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, FabioNeural)", "short_name": "pt-BR-FabioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Giovanna", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, GiovannaNeural)", "short_name": "pt-BR-GiovannaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Humberto", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, HumbertoNeural)", "short_name": "pt-BR-HumbertoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Julio", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, JulioNeural)", "short_name": "pt-BR-JulioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Leila", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, LeilaNeural)", "short_name": "pt-BR-LeilaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Leticia", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, LeticiaNeural)", "short_name": "pt-BR-LeticiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Manuela", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, ManuelaNeural)", "short_name": "pt-BR-ManuelaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Nicolau", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, NicolauNeural)", "short_name": "pt-BR-NicolauNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-BR", "local_name": "Valerio", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, ValerioNeural)", "short_name": "pt-BR-ValerioNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-BR", "local_name": "Yara", "name": "Microsoft Server Speech Text to Speech Voice (pt-BR, YaraNeural)", "short_name": "pt-BR-YaraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-PT", "local_name": "Raquel", "name": "Microsoft Server Speech Text to Speech Voice (pt-PT, RaquelNeural)", "short_name": "pt-PT-RaquelNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "pt-PT", "local_name": "Duarte", "name": "Microsoft Server Speech Text to Speech Voice (pt-PT, DuarteNeural)", "short_name": "pt-PT-DuarteNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "pt-PT", "local_name": "Fernanda", "name": "Microsoft Server Speech Text to Speech Voice (pt-PT, FernandaNeural)", "short_name": "pt-PT-FernandaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ro-RO", "local_name": "Alina", "name": "Microsoft Server Speech Text to Speech Voice (ro-RO, AlinaNeural)", "short_name": "ro-RO-AlinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ro-RO", "local_name": "Emil", "name": "Microsoft Server Speech Text to Speech Voice (ro-RO, EmilNeural)", "short_name": "ro-RO-EmilNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ru-RU", "local_name": "Светлана", "name": "Microsoft Server Speech Text to Speech Voice (ru-RU, SvetlanaNeural)", "short_name": "ru-RU-SvetlanaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ru-RU", "local_name": "Дмитрий", "name": "Microsoft Server Speech Text to Speech Voice (ru-RU, DmitryNeural)", "short_name": "ru-RU-DmitryNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ru-RU", "local_name": "Дария", "name": "Microsoft Server Speech Text to Speech Voice (ru-RU, DariyaNeural)", "short_name": "ru-RU-DariyaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "si-LK", "local_name": "තිළිණි", "name": "Microsoft Server Speech Text to Speech Voice (si-LK, ThiliniNeural)", "short_name": "si-LK-ThiliniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "si-LK", "local_name": "සමීර", "name": "Microsoft Server Speech Text to Speech Voice (si-LK, SameeraNeural)", "short_name": "si-LK-SameeraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sk-SK", "local_name": "Viktória", "name": "Microsoft Server Speech Text to Speech Voice (sk-SK, ViktoriaNeural)", "short_name": "sk-SK-ViktoriaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sk-SK", "local_name": "Lukáš", "name": "Microsoft Server Speech Text to Speech Voice (sk-SK, LukasNeural)", "short_name": "sk-SK-LukasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sl-SI", "local_name": "Petra", "name": "Microsoft Server Speech Text to Speech Voice (sl-SI, PetraNeural)", "short_name": "sl-SI-PetraNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sl-SI", "local_name": "Rok", "name": "Microsoft Server Speech Text to Speech Voice (sl-SI, RokNeural)", "short_name": "sl-SI-RokNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "so-SO", "local_name": "Ubax", "name": "Microsoft Server Speech Text to Speech Voice (so-SO, UbaxNeural)", "short_name": "so-SO-UbaxNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "so-SO", "local_name": "Muuse", "name": "Microsoft Server Speech Text to Speech Voice (so-SO, MuuseNeural)", "short_name": "so-SO-MuuseNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sq-AL", "local_name": "Anila", "name": "Microsoft Server Speech Text to Speech Voice (sq-AL, AnilaNeural)", "short_name": "sq-AL-AnilaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sq-AL", "local_name": "Ilir", "name": "Microsoft Server Speech Text to Speech Voice (sq-AL, IlirNeural)", "short_name": "sq-AL-IlirNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sr-Latn-RS", "local_name": "Nicholas", "name": "Microsoft Server Speech Text to Speech Voice (sr-Latn-RS, NicholasNeural)", "short_name": "sr-Latn-RS-NicholasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sr-Latn-RS", "local_name": "Sophie", "name": "Microsoft Server Speech Text to Speech Voice (sr-Latn-RS, SophieNeural)", "short_name": "sr-Latn-RS-SophieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sr-RS", "local_name": "Софија", "name": "Microsoft Server Speech Text to Speech Voice (sr-RS, SophieNeural)", "short_name": "sr-RS-SophieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sr-RS", "local_name": "Никола", "name": "Microsoft Server Speech Text to Speech Voice (sr-RS, NicholasNeural)", "short_name": "sr-RS-NicholasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "su-ID", "local_name": "Tuti", "name": "Microsoft Server Speech Text to Speech Voice (su-ID, TutiNeural)", "short_name": "su-ID-TutiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "su-ID", "local_name": "Jajang", "name": "Microsoft Server Speech Text to Speech Voice (su-ID, JajangNeural)", "short_name": "su-ID-JajangNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sv-SE", "local_name": "Sofie", "name": "Microsoft Server Speech Text to Speech Voice (sv-SE, SofieNeural)", "short_name": "sv-SE-SofieNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sv-SE", "local_name": "Mattias", "name": "Microsoft Server Speech Text to Speech Voice (sv-SE, MattiasNeural)", "short_name": "sv-SE-MattiasNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sv-SE", "local_name": "Hillevi", "name": "Microsoft Server Speech Text to Speech Voice (sv-SE, HilleviNeural)", "short_name": "sv-SE-HilleviNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sw-KE", "local_name": "Zuri", "name": "Microsoft Server Speech Text to Speech Voice (sw-KE, ZuriNeural)", "short_name": "sw-KE-ZuriNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sw-KE", "local_name": "Rafiki", "name": "Microsoft Server Speech Text to Speech Voice (sw-KE, RafikiNeural)", "short_name": "sw-KE-RafikiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "sw-TZ", "local_name": "Rehema", "name": "Microsoft Server Speech Text to Speech Voice (sw-TZ, RehemaNeural)", "short_name": "sw-TZ-RehemaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "sw-TZ", "local_name": "Daudi", "name": "Microsoft Server Speech Text to Speech Voice (sw-TZ, DaudiNeural)", "short_name": "sw-TZ-DaudiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ta-IN", "local_name": "பல்லவி", "name": "Microsoft Server Speech Text to Speech Voice (ta-IN, PallaviNeural)", "short_name": "ta-IN-PallaviNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ta-IN", "local_name": "வள்ளுவர்", "name": "Microsoft Server Speech Text to Speech Voice (ta-IN, ValluvarNeural)", "short_name": "ta-IN-ValluvarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ta-LK", "local_name": "சரண்யா", "name": "Microsoft Server Speech Text to Speech Voice (ta-LK, SaranyaNeural)", "short_name": "ta-LK-SaranyaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ta-LK", "local_name": "குமார்", "name": "Microsoft Server Speech Text to Speech Voice (ta-LK, KumarNeural)", "short_name": "ta-LK-KumarNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ta-MY", "local_name": "கனி", "name": "Microsoft Server Speech Text to Speech Voice (ta-MY, KaniNeural)", "short_name": "ta-MY-KaniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ta-MY", "local_name": "சூர்யா", "name": "Microsoft Server Speech Text to Speech Voice (ta-MY, SuryaNeural)", "short_name": "ta-MY-SuryaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ta-SG", "local_name": "வெண்பா", "name": "Microsoft Server Speech Text to Speech Voice (ta-SG, VenbaNeural)", "short_name": "ta-SG-VenbaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ta-SG", "local_name": "அன்பு", "name": "Microsoft Server Speech Text to Speech Voice (ta-SG, AnbuNeural)", "short_name": "ta-SG-AnbuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "te-IN", "local_name": "శ్రుతి", "name": "Microsoft Server Speech Text to Speech Voice (te-IN, ShrutiNeural)", "short_name": "te-IN-ShrutiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "te-IN", "local_name": "మోహన్", "name": "Microsoft Server Speech Text to Speech Voice (te-IN, MohanNeural)", "short_name": "te-IN-MohanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "th-TH", "local_name": "เปรมวดี", "name": "Microsoft Server Speech Text to Speech Voice (th-TH, PremwadeeNeural)", "short_name": "th-TH-PremwadeeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "th-TH", "local_name": "นิวัฒน์", "name": "Microsoft Server Speech Text to Speech Voice (th-TH, NiwatNeural)", "short_name": "th-TH-NiwatNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "th-TH", "local_name": "อัจฉรา", "name": "Microsoft Server Speech Text to Speech Voice (th-TH, AcharaNeural)", "short_name": "th-TH-AcharaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "tr-TR", "local_name": "Emel", "name": "Microsoft Server Speech Text to Speech Voice (tr-TR, EmelNeural)", "short_name": "tr-TR-EmelNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "tr-TR", "local_name": "Ahmet", "name": "Microsoft Server Speech Text to Speech Voice (tr-TR, AhmetNeural)", "short_name": "tr-TR-AhmetNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "uk-UA", "local_name": "Поліна", "name": "Microsoft Server Speech Text to Speech Voice (uk-UA, PolinaNeural)", "short_name": "uk-UA-PolinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "uk-UA", "local_name": "Остап", "name": "Microsoft Server Speech Text to Speech Voice (uk-UA, OstapNeural)", "short_name": "uk-UA-OstapNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ur-IN", "local_name": "گل", "name": "Microsoft Server Speech Text to Speech Voice (ur-IN, GulNeural)", "short_name": "ur-IN-GulNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ur-IN", "local_name": "سلمان", "name": "Microsoft Server Speech Text to Speech Voice (ur-IN, SalmanNeural)", "short_name": "ur-IN-SalmanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "ur-PK", "local_name": "عظمیٰ", "name": "Microsoft Server Speech Text to Speech Voice (ur-PK, UzmaNeural)", "short_name": "ur-PK-UzmaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "ur-PK", "local_name": "اسد", "name": "Microsoft Server Speech Text to Speech Voice (ur-PK, AsadNeural)", "short_name": "ur-PK-AsadNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "uz-UZ", "local_name": "Madina", "name": "Microsoft Server Speech Text to Speech Voice (uz-UZ, MadinaNeural)", "short_name": "uz-UZ-MadinaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "uz-UZ", "local_name": "Sardor", "name": "Microsoft Server Speech Text to Speech Voice (uz-UZ, SardorNeural)", "short_name": "uz-UZ-SardorNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "vi-VN", "local_name": "Hoài My", "name": "Microsoft Server Speech Text to Speech Voice (vi-VN, HoaiMyNeural)", "short_name": "vi-VN-HoaiMyNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "vi-VN", "local_name": "Nam Minh", "name": "Microsoft Server Speech Text to Speech Voice (vi-VN, NamMinhNeural)", "short_name": "vi-VN-NamMinhNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "wuu-CN", "local_name": "晓彤", "name": "Microsoft Server Speech Text to Speech Voice (wuu-CN, XiaotongNeural)", "short_name": "wuu-CN-XiaotongNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "wuu-CN", "local_name": "云哲", "name": "Microsoft Server Speech Text to Speech Voice (wuu-CN, YunzheNeural)", "short_name": "wuu-CN-YunzheNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "yue-CN", "local_name": "晓敏", "name": "Microsoft Server Speech Text to Speech Voice (yue-CN, XiaoMinNeural)", "short_name": "yue-CN-XiaoMinNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "yue-CN", "local_name": "云松", "name": "Microsoft Server Speech Text to Speech Voice (yue-CN, YunSongNeural)", "short_name": "yue-CN-YunSongNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓晓", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoxiaoNeural)", "short_name": "zh-CN-XiaoxiaoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["assistant", "chat", "customerservice", "newscast", "affectionate", "angry", "calm", "cheerful", "disgruntled", "fearful", "gentle", "lyrical", "sad", "serious", "poetry-reading", "friendly" ] }, { "gender": 2, "locale": "zh-CN", "local_name": "云希", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunxiNeural)", "short_name": "zh-CN-YunxiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["narration-relaxed", "embarrassed", "fearful", "cheerful", "disgruntled", "serious", "angry", "sad", "depressed", "chat", "assistant", "newscast" ] }, { "gender": 2, "locale": "zh-CN", "local_name": "云健", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunjianNeural)", "short_name": "zh-CN-YunjianNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["narration-relaxed", "sports-commentary", "sports-commentary-excited"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓伊", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoyiNeural)", "short_name": "zh-CN-XiaoyiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "disgruntled", "affectionate", "cheerful", "fearful", "sad", "embarrassed", "serious", "gentle" ] }, { "gender": 2, "locale": "zh-CN", "local_name": "云扬", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunyangNeural)", "short_name": "zh-CN-YunyangNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["customerservice", "narration-professional", "newscast-casual"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓辰", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaochenNeural)", "short_name": "zh-CN-XiaochenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓涵", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaohanNeural)", "short_name": "zh-CN-XiaohanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm", "fearful", "cheerful", "disgruntled", "serious", "angry", "sad", "gentle", "affectionate", "embarrassed" ] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓梦", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaomengNeural)", "short_name": "zh-CN-XiaomengNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["chat"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓墨", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaomoNeural)", "short_name": "zh-CN-XiaomoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["embarrassed", "calm", "fearful", "cheerful", "disgruntled", "serious", "angry", "sad", "depressed", "affectionate", "gentle", "envious" ] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓秋", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoqiuNeural)", "short_name": "zh-CN-XiaoqiuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓睿", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoruiNeural)", "short_name": "zh-CN-XiaoruiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm", "fearful", "angry", "sad"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓双", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoshuangNeural)", "short_name": "zh-CN-XiaoshuangNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["chat"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓萱", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoxuanNeural)", "short_name": "zh-CN-XiaoxuanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm", "fearful", "cheerful", "disgruntled", "serious", "angry", "gentle", "depressed"] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓颜", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoyanNeural)", "short_name": "zh-CN-XiaoyanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓悠", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaoyouNeural)", "short_name": "zh-CN-XiaoyouNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN", "local_name": "晓甄", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, XiaozhenNeural)", "short_name": "zh-CN-XiaozhenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "disgruntled", "cheerful", "fearful", "sad", "serious"] }, { "gender": 2, "locale": "zh-CN", "local_name": "云枫", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunfengNeural)", "short_name": "zh-CN-YunfengNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["angry", "disgruntled", "cheerful", "fearful", "sad", "serious", "depressed"] }, { "gender": 2, "locale": "zh-CN", "local_name": "云皓", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunhaoNeural)", "short_name": "zh-CN-YunhaoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["advertisement-upbeat"] }, { "gender": 2, "locale": "zh-CN", "local_name": "云夏", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunxiaNeural)", "short_name": "zh-CN-YunxiaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm", "fearful", "cheerful", "angry", "sad"] }, { "gender": 2, "locale": "zh-CN", "local_name": "云野", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunyeNeural)", "short_name": "zh-CN-YunyeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["embarrassed", "calm", "fearful", "cheerful", "disgruntled", "serious", "angry", "sad"] }, { "gender": 2, "locale": "zh-CN", "local_name": "云泽", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN, YunzeNeural)", "short_name": "zh-CN-YunzeNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": ["calm", "fearful", "cheerful", "disgruntled", "serious", "angry", "sad", "depressed", "documentary-narration" ] }, { "gender": 2, "locale": "zh-CN-henan", "local_name": "云登", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN-henan, YundengNeural)", "short_name": "zh-CN-henan-YundengNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN-liaoning", "local_name": "晓北", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN-liaoning, XiaobeiNeural)", "short_name": "zh-CN-liaoning-XiaobeiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-CN-shaanxi", "local_name": "晓妮", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN-shaanxi, XiaoniNeural)", "short_name": "zh-CN-shaanxi-XiaoniNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "zh-CN-shandong", "local_name": "云翔", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN-shandong, YunxiangNeural)", "short_name": "zh-CN-shandong-YunxiangNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "zh-CN-sichuan", "local_name": "云希", "name": "Microsoft Server Speech Text to Speech Voice (zh-CN-sichuan, YunxiNeural)", "short_name": "zh-CN-sichuan-YunxiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-HK", "local_name": "曉曼", "name": "Microsoft Server Speech Text to Speech Voice (zh-HK, HiuMaanNeural)", "short_name": "zh-HK-HiuMaanNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "zh-HK", "local_name": "雲龍", "name": "Microsoft Server Speech Text to Speech Voice (zh-HK, WanLungNeural)", "short_name": "zh-HK-WanLungNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-HK", "local_name": "曉佳", "name": "Microsoft Server Speech Text to Speech Voice (zh-HK, HiuGaaiNeural)", "short_name": "zh-HK-HiuGaaiNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-TW", "local_name": "曉臻", "name": "Microsoft Server Speech Text to Speech Voice (zh-TW, HsiaoChenNeural)", "short_name": "zh-TW-HsiaoChenNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "zh-TW", "local_name": "雲哲", "name": "Microsoft Server Speech Text to Speech Voice (zh-TW, YunJheNeural)", "short_name": "zh-TW-YunJheNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zh-TW", "local_name": "曉雨", "name": "Microsoft Server Speech Text to Speech Voice (zh-TW, HsiaoYuNeural)", "short_name": "zh-TW-HsiaoYuNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 1, "locale": "zu-ZA", "local_name": "Thando", "name": "Microsoft Server Speech Text to Speech Voice (zu-ZA, ThandoNeural)", "short_name": "zu-ZA-ThandoNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }, { "gender": 2, "locale": "zu-ZA", "local_name": "Themba", "name": "Microsoft Server Speech Text to Speech Voice (zu-ZA, ThembaNeural)", "short_name": "zu-ZA-ThembaNeural", "voice_type": { "name": "OnlineNeural", "value": 1 }, "style_list": [""] }] ================================================ FILE: talkieai-server/data/azure_style_label.json ================================================ [ { "value": "chat", "label": "聊天" }, { "value": "customerservice", "label": "服务" }, { "value": "narration-professional", "label": "专业" }, { "value": "newscast-casual", "label": "随意" }, { "value": "newscast-formal", "label": "正式" }, { "value": "cheerful", "label": "愉快" }, { "value": "empathetic", "label": "同情" }, { "value": "angry", "label": "生气" }, { "value": "sad", "label": "悲伤" }, { "value": "excited", "label": "兴奋" }, { "value": "friendly", "label": "友好" }, { "value": "terrified", "label": "害怕" }, { "value": "shouting", "label": "喊叫" }, { "value": "unfriendly", "label": "不友好" }, { "value": "whispering", "label": "低语" }, { "value": "hopeful", "label": "希望" }, { "value": "calm", "label": "平静" }, { "value": "fearful", "label": "害怕" }, { "value": "disgruntled", "label": "不满" }, { "value": "serious", "label": "认真" }, { "value": "gentle", "label": "温和" }, { "value": "affectionate", "label": "深情" }, { "value": "embarrassed", "label": "尴尬" }, { "value": "depressed", "label": "沮丧" }, { "value": "envious", "label": "嫉妒" }, { "value": "assistant", "label": "助手" }, { "value": "newscast", "label": "播报" }, { "value": "lyrical", "label": "抒情" }, { "value": "poetry-reading", "label": "朗诵" }, { "value": "advertisement-upbeat", "label": "广告" }, { "value": "narration-relaxed", "label": "叙述" }, { "value": "sports-commentary", "label": "体育评论" }, { "value": "sports-commentary-excited", "label": "体育评论" }, { "value": "documentary-narration", "label": "纪录片" } ] ================================================ FILE: talkieai-server/data/default_topic_data.json ================================================ { "groups": [ { "id": "group_1", "name": "自我介绍", "type": "ROLE_PLAY", "description": "做一个基本的自我介绍吧", "sequence": 10, "topics": [ { "id": "topic_1", "language": "en-US", "name": "自我介绍", "level": 2, "role_short_name": "en-US-JaneNeural", "role_speech_rate": "1.0", "topic_user_name": "Jack", "topic_bot_name": "Jane", "prompt": "你是用户的一位很好的朋友,现在开始引导用户来做一次简单的自我介绍吧,一步步的引导用户来介绍姓名、地址、爱好、身边朋友", "description": "做一个基本的自我介绍吧", "image_url": "https://qiniu.prejade.com/1597936949107363840/aitake/images/be8d50e5-17ba-4941-8960-af3ee9be6711.png", "sequence": 1, "phrases":[ {"phrase":"Hello, my name is Jack", "phrase_translation":"你好,我的名子叫Jack", "type":"PHRASE", "sequence": 1} ], "targets": [ { "type": "MAIN", "description": "自我介绍并进行轻松随意的交谈", "description_translation": "", "sequence": 100 }, { "type": "TRIAL", "description": "问新朋友俩个关于他/她自己的问题", "description_translation": "", "sequence": 3 }, { "type": "TRIAL", "description": "展开聊聊你的家乡和工作", "description_translation": "", "sequence": 2 }, { "type": "TRIAL", "description": "展开聊聊你的家乡和工作", "description_translation": "", "sequence": 1 } ] } ] }, { "id": "group_2", "name": "外贸业务", "type": "ROLE_PLAY", "description": "外贸业务的交流", "sequence": 1, "topics": [ { "id": "group_2_topic_1", "language": "en-US", "name": "外贸介绍", "level": 4, "role_short_name": "en-US-JaneNeural", "role_speech_rate": "1.0", "topic_user_name": "Jack", "topic_bot_name": "Jane", "prompt": "你是用户的一位外贸客户,现在开始引导用户来做一次简单的外贸介绍吧,一步步的引导用户来介绍产品、效果、使用方式", "description": "做一个基本的自我介绍吧", "image_url": "https://qiniu.prejade.com/1597936949107363840/aitake/images/be8d50e5-17ba-4941-8960-af3ee9be6711.png", "sequence": 1, "phrases":[ {"phrase":"Hello, my name is Jack", "phrase_translation":"你好,我的名子叫Jack", "type":"PHRASE", "sequence": 1} ], "targets": [ { "type": "MAIN", "description": "自我介绍并进行轻松随意的交谈", "description_translation": "", "sequence": 100 }, { "type": "TRIAL", "description": "讲清楚自己的产品", "description_translation": "", "sequence": 3 }, { "type": "TRIAL", "description": "讲清楚产品的使用方式", "description_translation": "", "sequence": 2 } ] } ] } ] } ================================================ FILE: talkieai-server/data/language_demo_map.json ================================================ { "en": "Hello, welcome to Talkie. We hope you have a good learning experience.", "ga": "Dia duit, fáilte chuig talkie, tá súil agam go mbeidh taithí foghlama maith agaibh.", "fr": "Bonjour, bienvenue sur TalkieAI. Nous espérons que vous passerez une bonne expérience d'apprentissage.", "ja": "こんにちは、TalkieAIをご利用いただきありがとうございます。良い学習体験をお楽しみください。", "ko": "안녕하세요, TalkieAI를 사용해 주셔서 감사합니다. 좋은 학습 경험을 가지시길 바랍니다.", "es": "Hola, bienvenido/a a TalkieAI. Espero que tengas una buena experiencia de aprendizaje.", "bn": "নমস্কার, TalkieAI ব্যবহারে স্বাগতম। আমি আশা করি আপনি একটি ভাল শিক্ষার অভিজ্ঞতা অর্জন করতে পারবেন।", "cs": "Dobrý den, vítejte v aplikaci TalkieAI. Doufáme, že budete mít dobrý zážitek při učení.", "ca": "Hola, benvingut a TalkieAI. Esperem que tinguis una bona experiència d'aprenentatge.", "bg": "Здравейте, добре дошли в TalkieAI. Надявам се да имате приятно учебно изживяване.", "da": "Hej, velkommen til at bruge TalkieAI. Vi håber, at du får en god læringsoplevelse.", "hr": "Dobar dan, dobrodošli na TalkieAI. Nadamo se da ćete imati dobro iskustvo u učenju.", "nl": "Hallo, welkom bij TalkieAI. We hopen dat je een goede leerervaring hebt.", "el": "Γεια σας, καλωσορίσατε στο TalkieAI, ελπίζουμε να έχετε μια καλή εμπειρία μάθησης.", "he": "שלום, ברוכים הבאים לשימוש ב-TalkieAI, מקווים שתהיה לך חווית למידה טובה.", "fi": "Hei, tervetuloa käyttämään talkiea, toivottavasti sinulla on hyvä oppimiskokemus.", "de": "Hallo, willkommen bei Talkie. Ich hoffe, Sie haben eine gute Lernerfahrung.", "hi": "नमस्ते, टॉकी का स्वागत करते हैं। हमें आशा है कि आपको एक अच्छा सीखने का अनुभव मिलेगा।", "hu": "Szia, üdvözöllek a TalkieAI-n. Reméljük, hogy jó tanulási élményben lesz részed.", "id": "Halo, selamat datang di TalkieAI. Kami harap Anda memiliki pengalaman belajar yang baik.", "it": "Ciao, benvenuto/a su TalkieAI. Spero che tu abbia una buona esperienza di apprendimento.", "ms": "Halo, selamat datang ke TalkieAI. Kami harap anda akan mempunyai pengalaman pembelajaran yang baik.", "nb": "Hei, velkommen til TalkieAI. Vi håper du har en god læringsopplevelse.", "pl": "Cześć, witaj w TalkieAI. Mamy nadzieję, że będziesz miał/a dobrą przygodę z nauką.", "pt": "Olá, bem-vindo ao TalkieAI. Espero que tenha uma boa experiência de aprendizagem.", "ro": "Salut, bun venit la TalkieAI. Sper că vei avea o experiență plăcută de învățare.", "ru": "Привет, добро пожаловать в TalkieAI. Желаем вам хорошего образовательного опыта.", "sk": "Ahoj, vitajte v TalkieAI. Dúfame, že budete mať dobrý zážitok pri učení.", "sl": "Pozdravljeni, dobrodošli na TalkieAI. Upamo, da boste imeli dobro izkušnjo pri učenju.", "sv": "Hej, välkommen till TalkieAI. Vi hoppas att du får en bra lärandeupplevelse.", "ta": "வணக்கம், தாக்கி என்ற இணையத்திற்கு வரவேற்கிறோம். நலமான கற்றல் அனுபவத்தை அடைய நமது ஆர்வம்.", "te": "హలో, TalkieAI కు స్వాగతం. మీకు ఒక మంచి అభ్యాస అనుభవం కావాలని ఆశిస్తున్నాం.", "th": "สวัสดีครับ ยินดีต้อนรับสู่ TalkieAI หวังว่าคุณจะมีประสบการณ์การเรียนรู้ที่ดี", "tr": "Merhaba, TalkieAI'yi kullanmaya hoş geldiniz. Umarız iyi bir öğrenme deneyimi yaşarsınız.", "uk": "Привіт, ласкаво просимо до TalkieAI. Бажаємо вам гарного досвіду навчання.", "vi": "Xin chào, chào mừng bạn đến với TalkieAI. Hy vọng bạn có trải nghiệm học tập tốt.", "zh": "你好,欢迎使用 TalkieAI。希望您有一个好的学习体验。" } ================================================ FILE: talkieai-server/data/sys_language.json ================================================ [ { "label": "英语(美国)", "value": "en-US", "default_voice_role_name":"en-US-JennyNeural" }, { "label": "英语(英国)", "value": "en-GB", "default_voice_role_name":"en-GB-SoniaNeural" }, { "label": "中文(普通话)", "value": "zh-CN", "default_voice_role_name":"zh-CN-XiaoxiaoNeural" }, { "label": "中文(粤语)", "value": "zh-HK", "default_voice_role_name":"" }, { "label": "德语", "value": "de-DE", "default_voice_role_name":"zh-HK-HiuMaanNeural" }, { "label": "日语", "value": "ja-JPR", "default_voice_role_name":"ja-JP-NanamiNeural" }, { "label": "韩语", "value": "ko-KR", "default_voice_role_name":"ko-KR-SunHiNeural" }, { "label": "俄语", "value": "ru-RU", "default_voice_role_name":"ru-RU-SvetlanaNeural" }, { "label": "法语", "value": "fr-FR", "default_voice_role_name":"fr-FR-DeniseNeural" } ] ================================================ FILE: talkieai-server/requirements.txt ================================================ uvicorn==0.20.0 azure_storage==0.37.0 fastapi==0.109.0 openai==1.9.0 pydantic==1.10.5 pydub==0.25.1 PyJWT==2.8.0 python-dotenv==1.0.1 SQLAlchemy==2.0.10 starlette==0.36.1 zhipuai==1.0.7 pymysql~=1.0.2 azure-cognitiveservices-speech~=1.34.1 requests~=2.28.2 python-multipart ================================================ FILE: talkieai-server/start.sh ================================================ nohup uvicorn app.main:app --host 0.0.0.0 --port 8097 & ================================================ FILE: talkieai-uniapp/index.html ================================================ TalkieAI
================================================ FILE: talkieai-uniapp/package.json ================================================ { "name": "uni-preset-vue", "version": "0.0.0", "scripts": { "dev:app": "uni -p app", "dev:app-android": "uni -p app-android", "dev:app-ios": "uni -p app-ios", "dev:custom": "uni -p", "dev:h5": "uni", "dev:h5:ssr": "uni --ssr", "dev:mp-alipay": "uni -p mp-alipay", "dev:mp-baidu": "uni -p mp-baidu", "dev:mp-jd": "uni -p mp-jd", "dev:mp-kuaishou": "uni -p mp-kuaishou", "dev:mp-lark": "uni -p mp-lark", "dev:mp-qq": "uni -p mp-qq", "dev:mp-toutiao": "uni -p mp-toutiao", "dev:mp-weixin": "uni -p mp-weixin", "dev:quickapp-webview": "uni -p quickapp-webview", "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei", "dev:quickapp-webview-union": "uni -p quickapp-webview-union", "build:app": "uni build -p app", "build:app-android": "uni build -p app-android", "build:app-ios": "uni build -p app-ios", "build:custom": "uni build -p", "build:h5": "uni build", "build:h5:ssr": "uni build --ssr", "build:mp-alipay": "uni build -p mp-alipay", "build:mp-baidu": "uni build -p mp-baidu", "build:mp-jd": "uni build -p mp-jd", "build:mp-kuaishou": "uni build -p mp-kuaishou", "build:mp-lark": "uni build -p mp-lark", "build:mp-qq": "uni build -p mp-qq", "build:mp-toutiao": "uni build -p mp-toutiao", "build:mp-weixin": "uni build -p mp-weixin", "build:quickapp-webview": "uni build -p quickapp-webview", "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei", "build:quickapp-webview-union": "uni build -p quickapp-webview-union", "type-check": "vue-tsc --noEmit" }, "dependencies": { "@dcloudio/uni-app": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-app-plus": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-components": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-h5": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-alipay": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-baidu": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-jd": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-lark": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-qq": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-weixin": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3070720230316001", "axios": "^0.21.4", "fingerprintjs2": "^2.1.4", "js-cookie": "^2.2.1", "less": "^4.1.3", "less-loader": "^11.1.2", "query-string": "^8.1.0", "recorder-core": "1.2.23070100", "sass": "^1.62.1", "sass-loader": "^13.3.1", "uview-ui": "^1.8.8", "vue": "^3.2.45", "vue-i18n": "^9.1.9" }, "devDependencies": { "@dcloudio/types": "^3.3.2", "@dcloudio/uni-automator": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-cli-shared": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-stacktracey": "3.0.0-alpha-3070720230316001", "@dcloudio/vite-plugin-uni": "3.0.0-alpha-3070720230316001", "@vue/tsconfig": "^0.1.3", "typescript": "^4.9.4", "vite": "4.0.4", "vue-tsc": "^1.0.24" } } ================================================ FILE: talkieai-uniapp/src/App.vue ================================================ ================================================ FILE: talkieai-uniapp/src/api/account.ts ================================================ import request from "@/axios/api"; export default { visitorLogin: (data: any) => { return request("/account/visitor-login", "POST", data, true); }, accountInfoGet: () => { return request("/account/info", "GET"); }, setSettings: (data: any) => { return request("/account/settings", "POST", data); }, getSettings: () => { return request("/account/settings", "GET"); }, setRole: (data: any) => { return request("/account/role", "POST", data); }, getRole: () => { return request("/account/role", "GET", null); }, setLearningLanguage: (data: any) => { return request("/account/language", "POST", data); }, getLearningLanguage: () => { return request("/account/language", "GET", null); }, collectGet: (data: any) => { return request("/account/collect", "GET", data, false); }, collect: (data: any) => { return request("/account/collect", "POST", data, false); }, cancelCollect: (data: any) => { return request("/account/collect", "DELETE", data, false); }, collectsGet: (data: any) => { return request("/account/collects", "GET", data, false); } }; ================================================ FILE: talkieai-uniapp/src/api/chat.ts ================================================ import request from "@/axios/api"; export default { sessionCreate: (data: any) => { return request("/sessions", "POST", data, true); }, sessionDefaultGet: (data: any) => { return request("/sessions/default", "GET", data, true); }, sessionDetailsGet: (data: any) => { return request("/sessions/" + data.sessionId, "GET", data, true); }, sessionInitGreeting: (sessionId: string) => { return request("/sessions/" + sessionId + "/greeting", "GET", {}, false); }, sessionChatInvoke: (data: any) => { return request(`/sessions/${data.sessionId}/chat`, "POST", data, false); }, transformText: (data: any) => { return request( `/sessions/${data.sessionId}/voice-translate`, "POST", data, false ); }, messagesLatestDelete: (sessionId: string) => { return request( `/sessions/${sessionId}/messages/latest`, "DELETE", null, false ); }, messagesAllDelete: (sessionId: string) => { return request(`/sessions/${sessionId}/messages`, "DELETE", null, false); }, translateInvoke: (data: any) => { return request( `/messages/${data.message_id}/translate`, "POST", data, false ); }, messagePractice: (data: any) => { return request( `/messages/${data.message_id}/practice`, "POST", data, false ); }, speechContent: (data: any) => { return request("/message/speech-content", "POST", data, false); }, speechDemo: (data: any) => { return request("/message/speech-demo", "POST", data, false); }, grammarInvoke: (data: any) => { return request("/message/grammar", "POST", data, false); }, pronunciationInvoke: (data: any) => { return request("/message/pronunciation", "POST", data, false); }, translateSettingLanguage: (data: any) => { return request("/message/translate-setting-language", "POST", data, false); }, translateSourceLanguage: (data: any) => { return request("/message/translate-source-language", "POST", data, false); }, transferSpeech: (data: any) => { return request("/message/speech", "POST", data, false); }, wordDetail: (data: any) => { return request("/message/word/detail", "POST", data, false); }, wordPractice: (data: any) => { return request("/message/word/practice", "POST", data, false); }, promptInvoke: (data: any) => { return request("/message/prompt", "POST", data, false); }, languageExampleGet: (data?: any) => { return request("/languages/example", "GET", data, false); }, rolesGet: (data?: any) => { return request("/roles", "GET", data, false); }, }; ================================================ FILE: talkieai-uniapp/src/api/sys.ts ================================================ import request from "@/axios/api"; export default { feedbackAdd: (data: any) => { return request("/sys/feedback", "POST", data, false); }, getLanguages: () => { return request("/sys/languages", "GET", null); }, getRoles: (data: any) => { return request("/sys/roles", "GET", data); }, setLearningLanguage: (data: any) => { return request("/sys/language", "POST", data); }, settingsPost: (data: any) => { return request("/sys/settings", "POST", data); }, settingsGet: () => { return request("/sys/settings", "GET"); }, }; ================================================ FILE: talkieai-uniapp/src/api/topic.ts ================================================ import request from "@/axios/api"; export default { getTopicData: (params: any) => { return request("/topics", "GET", params, false); }, getTopicDetail: (id: string) => { return request(`/topics/${id}`, "GET", null, false); }, getTopicHistory: (id: string) => { return request(`/topics/${id}/history`, "GET", null, false); }, createSession: (data: any) => { return request(`/topics/${data.topic_id}/session`, "POST", data, true); }, completeTopic: (data: any) => { return request(`/topics/sessions/${data.session_id}/complete`, "POST", data, true); }, getTopicCompletation: (data: any) => { return request(`/topics/${data.topic_id}/session/${data.session_id}/completion`, "GET", null, false); }, getPhrase: (data: any) => { return request(`/topics/${data.topic_id}/phrases`, "GET", null, false); }, deleteTopicHistory: (data: any) => { return request(`/topics/${data.topic_id}/session/${data.session_id}`, "DELETE", null, false); } }; ================================================ FILE: talkieai-uniapp/src/axios/api.ts ================================================ import __config from "@/config/env"; const request = ( url: string, method?: | "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT", data?: any, showLoading?: boolean ): Promise => { let _url = __config.basePath + url; return new Promise((resolve, reject) => { if (showLoading) { uni.showLoading(); } uni.request({ url: _url, method: method, data: data, header: { "Content-Type": "application/json", "X-Token": uni.getStorageSync("x-token") ? uni.getStorageSync("x-token") : "", }, success(res) { if (res.statusCode == 200) { resolve(res.data); } else if (res.statusCode == 401) { uni.showToast({ title: "登录过期,重新登录", icon: "none", duration: 2000, }); uni.removeStorageSync("x-token"); uni.navigateTo({ url: "/pages/login/index", }); } else { reject(res.data); } }, fail(error) { console.error(error); reject(error); }, complete(res) { // 判断是否在loading中 if (showLoading) { uni?.hideLoading(); } }, }); }); }; export default request; ================================================ FILE: talkieai-uniapp/src/axios/axiosServer.ts ================================================ // import axios from "./axiosService"; // import qs from "query-string"; // export const PostJson = (url: string, params: any) => { // return axios.post(url, params); // }; // export const GetUrl = (url: string, params: any = {}) => { // return axios.get(`${url}?${qs.stringify(params)}`, params); // }; ================================================ FILE: talkieai-uniapp/src/axios/axiosService.ts ================================================ // import axios from "axios"; // import Cookies from "js-cookie"; // axios.interceptors.request.use( // function (config) { // // 在发送请求之前做些什么 // let configCp = { ...config }; // const token = Cookies.get("token"); // const authHeader = { // Authorization: `${token}`, // headers: { // "Content-Type": "application/json", // }, // }; // configCp.headers = { // ...configCp.headers, // ...authHeader, // } as any; // return configCp; // }, // function (error) { // // 对请求错误做些什么 // return Promise.reject(error); // } // ); // // 添加响应拦截器 // axios.interceptors.response.use( // function (response) { // // 2xx 范围内的状态码都会触发该函数。 // // 对响应数据做点什么 // console.log("response", response); // if (response.status == 200) { // if (response.data.code === 401) { // Cookies.remove("token"); // window.location.href = "./login"; // // Toast.show(response.data.message || "Server Internal Error"); // } else if (response.data.code !== 200) { // // Toast.show(response.data.message || "Server Internal Error"); // } // } else if (response.status == 200) { // // Toast.show("Server Internal Error"); // } else if (response.status != 200) { // // Toast.show("Server Internal Error"); // } // return response; // }, // function (response) { // // 超出 2xx 范围的状态码都会触发该函数。 // // 对响应错误做点什么 // if (response.status == 200 && response.data.code !== 200) { // // Toast.show(response.data.message || "Server Internal Error"); // } else if (response.status != 200) { // // Toast.show("Server Internal Error"); // } // return response; // } // ); // export default axios; ================================================ FILE: talkieai-uniapp/src/components/AudioPlayer.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Checkbox.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Collect.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/CommonHeader.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/FunctionalText.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/GithubLink.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Loading.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/LoadingRound.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Rare2.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Rate.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/Speech.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/WordAnalysisPopup.vue ================================================ ================================================ FILE: talkieai-uniapp/src/components/audioPlayerExecuter.ts ================================================ import __config from "@/config/env"; import { ref } from "vue"; interface Listener { playing?: () => void; success?: () => void; error?: () => void; } class AudioPlayer { private audioContext: any = null; constructor() {} /** * 录音的时候使用,录音前要先关闭所有音频播放 */ stopAudio() { if (this.audioContext) { this.audioContext.stop(); } } playAudio({ audioUrl, listener }: { audioUrl: string; listener: Listener }) { let audioSrc = audioUrl; if (this.audioContext) { } if (this.audioContext) { const oldSrc = this.audioContext.src; this.audioContext.stop(); if (oldSrc === audioSrc) { this.audioContext = null; return; } } let innerAudioContext = this.createInnerAudioContext(audioUrl, listener); this.audioContext = innerAudioContext; innerAudioContext.play(); // 播放 } createInnerAudioContext(src: string, listener: Listener) { let innerAudioContext: any = null; // #ifdef MP-WEIXIN innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: true, }); // #endif // #ifndef MP-WEIXIN innerAudioContext = uni.createInnerAudioContext(); // #endif innerAudioContext.src = src; innerAudioContext.onPlay(() => { if (listener.playing) { listener.playing(); } }); innerAudioContext.onStop(() => { if (listener.success) { listener.success(); } innerAudioContext.destory && innerAudioContext.destory(); this.audioContext = null; }); innerAudioContext.onEnded(() => { if (listener.success) { listener.success(); } innerAudioContext.destory && innerAudioContext.destory(); this.audioContext = null; }); innerAudioContext.onError((res: any) => { if (listener.error) { listener.error(); } }); return innerAudioContext; } } const audioPlayer = new AudioPlayer(); // export default audioPlayer; export default audioPlayer; ================================================ FILE: talkieai-uniapp/src/components/speechExecuter.ts ================================================ import __config from "@/config/env"; // #ifdef H5 import Recorder from "recorder-core"; import "recorder-core/src/engine/wav"; // #endif /** * 录音后自动上传文件,成功后回调文件名 */ const MAXIMUM_RECORDING_TIME = 60; class Speech { private recorder = { start: false, processing: false, remainingTime: 0, rec: null as any | null, wxRecorderManager: null, }; private intervalId: any = null; private listener = { success: null as Function | null, cancel: null as Function | null, error: null as Function | null, interval: null as Function | null, processing: null as Function | null, }; constructor() { // ... Constructor logic ... } handleVoiceStart({ success, cancel, error, interval, processing, }: { success: Function; cancel: Function; error: Function; interval: Function; processing: Function; }) { let self = this; self.listener.success = success; self.listener.cancel = cancel; self.listener.error = error; self.listener.interval = interval; self.listener.processing = processing; // #ifndef H5 self.mpWeixinVoiceStart(); // #endif // #ifdef H5 self.h5VoiceStart(); // #endif } mpWeixinVoiceStart() { let self = this; let recorderManager = uni.getRecorderManager(); console.log(recorderManager); // 如果是andoird手机使用,则须要设置mp3格式,否则无法播放 let format = "wav"; // #ifdef APP-PLUS if (uni.getSystemInfoSync().platform === "android") { format = "mp3"; } // #endif self.recorder.wxRecorderManager = recorderManager; recorderManager.start({ duration: MAXIMUM_RECORDING_TIME * 1000, sampleRate: 44100, encodeBitRate: 192000, format: format, }); console.log("speech start.."); self.recorder.start = true; self.recorder.remainingTime = MAXIMUM_RECORDING_TIME; self.intervalId = setInterval(() => { if (self.recorder.remainingTime === 0) { self.handleEndVoice(); } else { if (self.listener.interval) { self.listener.interval(self.recorder.remainingTime); } self.recorder.remainingTime--; } }, 1000); recorderManager.onStop((res: any) => { console.log("speech on stop.." + res.tempFilePath); self.handleProcessWxEndVoice({ filePath: res.tempFilePath, }); }); } clearInterval() { const self = this; if (self.intervalId) { clearInterval(self.intervalId); } } h5VoiceStart() { let self = this; self.recorder.rec = Recorder({ type: "wav", bitRate: 32, sampleRate: 32000, }); self.recorder.rec.open( () => { self.recorder.start = true; self.recorder.rec.start(); self.recorder.remainingTime = MAXIMUM_RECORDING_TIME; self.intervalId = setInterval(() => { if (self.listener.interval) { self.listener.interval(self.recorder.remainingTime); } if (self.recorder.remainingTime === 0) { clearInterval(self.intervalId); self.handleEndVoice(); } else { self.recorder.remainingTime--; } }, 1000); }, (msg: string, isUserNotAllow: any) => { uni.showToast({ title: "请开启录音权限", icon: "none", }); if (self.listener.error) { self.listener.error(msg); } } ); } handleCancleVoice() { let self = this; self.clearInterval(); // #ifndef H5 if (self.recorder.wxRecorderManager) { self.recorder.wxRecorderManager.stop(); self.recorder.start = false; self.recorder.processing = false; self.recorder.wxRecorderManager = null; } // #endif // #ifdef H5 if (self.recorder.rec) { self.recorder.rec.stop(() => { self.recorder.start = false; self.recorder.processing = false; self.recorder.rec = null; }); } // #endif if (self.listener.cancel) { self.listener.cancel(); } } handleEndVoice() { let self = this; self.clearInterval(); if (self.recorder.processing) { return; } // #ifndef H5 console.log("speech trigger end.."); self.handleWxEndVoice(); // #endif // #ifdef H5 self.handleH5EndVoice(); // #endif } handleWxEndVoice() { let self = this; console.log("execute stop1"); console.log(self.recorder); self.recorder.wxRecorderManager.stop(); console.log("execute stop"); } handleProcessWxEndVoice({ filePath }: { filePath: string }) { console.log("speech end..."); let self = this; if (self.listener.processing) { self.listener.processing(); } uni.uploadFile({ url: __config.basePath + "/voice/upload", filePath: filePath, header: { "X-Token": uni.getStorageSync("x-token"), }, name: "file", success: (res: any) => { var resData = res; self.handleUploadResult({ resData, }); }, fail(e: any) { console.error(e, "失败原因"); uni.showToast({ title: "上传失败", icon: "none", }); if (self.listener.error) { self.listener.error(e); } }, complete: () => { self.recorder.start = false; self.recorder.processing = false; self.recorder.rec = null; }, }); } handleH5EndVoice() { let self = this; if (self.listener.processing) { self.listener.processing(); } self.recorder.rec.stop( (blob: any, duration: any) => { self.recorder.processing = true; var reader = new FileReader(); reader.addEventListener("load", function () {}, false); reader.readAsDataURL(blob); let blobURL = window.URL.createObjectURL(blob); uni.uploadFile({ file: blob, header: { "X-Token": uni.getStorageSync("x-token"), }, name: "file", formData: { file: blob, }, url: __config.basePath + "/voices/upload", success: (res) => { var resData = res; self.handleUploadResult({ resData, }); }, fail(e) { console.error(e, "失败原因"); uni.showToast({ title: "上传失败", icon: "none", }); }, complete: () => { self.recorder.start = false; self.recorder.processing = false; self.recorder.rec = null; }, }); }, function (s: any) { if (self.listener.error) { self.listener.error(s); } console.error("结束出错"); }, true ); } handleUploadResult({ resData }: { resData: any }) { const self = this; if (resData.statusCode == 200) { let resultJson = JSON.parse(resData.data); if (resultJson.code != "200") { uni.showToast({ title: resultJson.message, icon: "none", }); if (self.listener.error) { self.listener.error(resultJson); } return; } let dataJson = resultJson.data; if (self.listener.success) { self.listener.success({ inputValue: dataJson.result, voiceFileName: dataJson.file, }); } } } } const speech = new Speech(); export default speech; ================================================ FILE: talkieai-uniapp/src/config/env.ts ================================================ export default { // basePath: "http://192.168.0.102:8098/api/v1" basePath: "http://localhost:8097/api/v1" // basePath: "https://talkie.prejade.com/api/v1" }; ================================================ FILE: talkieai-uniapp/src/env.d.ts ================================================ /// declare module '*.vue' { import { DefineComponent } from 'vue' // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types const component: DefineComponent<{}, {}, any> export default component } ================================================ FILE: talkieai-uniapp/src/global/globalCount.hooks.ts ================================================ import { ref, onMounted } from "vue"; import chatRequest from "@/api/chat"; // 全局状态,创建在模块作用域下 const globalUserInfo = ref(1); const globalLoading = ref(false); export function useUserInfo() { // 局部状态,每个组件都会创建 const localCount = ref(1); onMounted(() => { globalLoading.value = true; chatRequest.sessionDefaultGet({}).then((data) => { globalUserInfo.value = data.data; }); globalLoading.value = false; }); return { globalUserInfo, globalLoading, localCount, }; } ================================================ FILE: talkieai-uniapp/src/less/global.less ================================================ // 使用 * 的话 微信小程序会报错 // [ WXSS 文件编译错误] ./pages/contact/index.wxss unexpected token * view { margin: 0; padding: 0; } @common-color:#6236FF; @common-bg-gray-color: #F5F5FE; .common-button{ width: 590rpx; height: 120rpx; background: @common-color; color:#fff; font-size: 14px; border-radius: 60rpx; display: flex; align-items: center; justify-content: center; } button:active{ opacity: .8; } view{ box-sizing: border-box; } .row-bc { display: flex; flex-direction: row; justify-content: space-between; align-items: center; } .row-sc { display: flex; flex-direction: row; justify-content: flex-start; align-items: center; } .common-button{ width: 590rpx; height: 120rpx; background: @common-color; color:#fff; font-size: 14px; border-radius: 60rpx; display: flex; align-items: center; justify-content: center; } .atk-btn-box { padding: 28rpx 0; background: @common-color; display: flex; align-items: center; justify-content: center; font-size: 18px; border-radius: 60rpx; color:#fff; .atk-btn { letter-spacing: 4rpx; } &.gray { background: #999; } } .bottom-box { margin: 32rpx; width: calc(100vw - 64rpx); position: fixed; bottom: 0; padding-bottom: calc(env(safe-area-inset-bottom) / 2); } .icon { width: 32rpx; height: 32rpx; } ================================================ FILE: talkieai-uniapp/src/main.ts ================================================ import { createSSRApp } from "vue"; import App from "./App.vue"; import EventBus from "@/utils/bus"; const getHeight = (global: any) => { uni.getSystemInfo({ success: (e) => { global.StatusBar = e.statusBarHeight || 0; // Platform-specific logic if (e.platform === "android") { global.CustomBar = global.StatusBar + 50; } else { global.CustomBar = global.StatusBar + 45; } // MP-WEIXIN specific logic // #ifdef MP-WEIXIN global.Custom = wx.getMenuButtonBoundingClientRect(); let customBar = global.Custom.bottom + global.Custom.top - global.StatusBar + 4; global.CustomBar = customBar; // #endif // MP-ALIPAY specific logic // #ifdef MP-ALIPAY const titleBarHeight = e.titleBarHeight || 0; global.CustomBar = global.StatusBar + titleBarHeight; // #endif }, }); }; export function createApp() { const $bus = new EventBus(); const app = createSSRApp(App); app.provide("$bus", $bus); app.config.globalProperties.$bus = $bus; getHeight(app.config.globalProperties); return { app, }; } ================================================ FILE: talkieai-uniapp/src/manifest.json ================================================ { "name" : "talkie", "appid" : "__UNI__1EE8DB0", "description" : "", "versionName" : "1.0.0", "versionCode" : "100", "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { "usingComponents" : true, "nvueStyleCompiler" : "uni-app", "compilerVersion" : 3, "splashscreen" : { "alwaysShowBeforeRender" : true, "waiting" : true, "autoclose" : true, "delay" : 0 }, /* 模块配置 */ "modules" : { "Record" : {} }, /* 应用发布信息 */ "distribute" : { /* android打包配置 */ "android" : { "permissions" : [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ], "minSdkVersion" : 21 }, /* ios打包配置 */ "ios" : { "dSYMs" : false }, /* SDK配置 */ "sdkConfigs" : { "ad" : {}, "speech" : {} }, "icons" : { "android" : { "hdpi" : "unpackage/res/icons/72x72.png", "xhdpi" : "unpackage/res/icons/96x96.png", "xxhdpi" : "unpackage/res/icons/144x144.png", "xxxhdpi" : "unpackage/res/icons/192x192.png" }, "ios" : { "appstore" : "unpackage/res/icons/1024x1024.png", "ipad" : { "app" : "unpackage/res/icons/76x76.png", "app@2x" : "unpackage/res/icons/152x152.png", "notification" : "unpackage/res/icons/20x20.png", "notification@2x" : "unpackage/res/icons/40x40.png", "proapp@2x" : "unpackage/res/icons/167x167.png", "settings" : "unpackage/res/icons/29x29.png", "settings@2x" : "unpackage/res/icons/58x58.png", "spotlight" : "unpackage/res/icons/40x40.png", "spotlight@2x" : "unpackage/res/icons/80x80.png" }, "iphone" : { "app@2x" : "unpackage/res/icons/120x120.png", "app@3x" : "unpackage/res/icons/180x180.png", "notification@2x" : "unpackage/res/icons/40x40.png", "notification@3x" : "unpackage/res/icons/60x60.png", "settings@2x" : "unpackage/res/icons/58x58.png", "settings@3x" : "unpackage/res/icons/87x87.png", "spotlight@2x" : "unpackage/res/icons/80x80.png", "spotlight@3x" : "unpackage/res/icons/120x120.png" } } } } }, "quickapp" : {}, "mp-weixin" : { "appid" : "wxd0d975d602551eb6", "setting" : { "urlCheck" : false }, "requiredBackgroundModes" : [ "audio", "location" ], "usingComponents" : true }, "mp-alipay" : { "usingComponents" : true }, "mp-baidu" : { "usingComponents" : true }, "mp-toutiao" : { "usingComponents" : true }, "uniStatistics" : { "enable" : false }, "vueVersion" : "3", "h5" : { "template" : "index.html", "title" : "TalkieAI" } } ================================================ FILE: talkieai-uniapp/src/models/chat.ts ================================================ ================================================ FILE: talkieai-uniapp/src/models/models.ts ================================================ export interface AccountInfo { account_id: string; today_chat_count: number; total_chat_count: number; target_language_label: string; } export interface AccountSettings { auto_playing_voice:number; auto_text_shadow:number; auto_pronunciation:number; playing_voice_speed:string; speech_role_name_label:string; speech_role_name:string; target_language:string; } export interface Collect { id?: string | null; type: string; content: string; translation: string; message_id?: string | null; create_time?: string | null; } export interface Message { id?: string | null; content?: string | null; owner: boolean; file_name?: string | null; role: string | "USER" | "ASSISTANT"; session_id?: string | null; auto_play?: boolean | null; auto_hint?: boolean | null; auto_pronunciation?: boolean | null; pronunciation?: Pronunciation | null | undefined; } export interface Phoneme { phoneme: string; accuracy_score: number; } export interface Word { word: string; accuracy_score: number; error_type: string; phonemes: Phoneme[]; } export interface Pronunciation { accuracy_score: number; fluency_score: number; completeness_score: number; pronunciation_score: number; words: Word[]; } export interface MessagePage { list: Message[]; total: number; } export interface Session { id?: string; type?: string; messages: MessagePage; } export interface Prompt { text?: string; translateShow?: boolean; } ================================================ FILE: talkieai-uniapp/src/models/sys.ts ================================================ export interface Language { id?: string | null; language?: string | null; label: boolean; description?: string | null; } export interface Role { id?: string | null; short_name: string; country: string; gender: string; avatar: string; audio: string; sequence: number; } ================================================ FILE: talkieai-uniapp/src/pages/chat/components/CommonAudioPlayer.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/MessageContent.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/MessageGrammar.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/MessageGrammarPopup.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/MessagePronunciation.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/MessageSpeech.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/PhonemeBox.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/Prompt.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/PromptPopup.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/TextPronunciation.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/TranslationPopup.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/components/WordDetail.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/index.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/chat/settings.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/contact/index.vue ================================================ ================================================ FILE: talkieai-uniapp/src/pages/contact/less/index.less ================================================ @import url('../../../less/global.less'); .contact{ display: flex; padding-top: 100rpx; justify-content: center; flex-direction: column; align-items: center; .contact-text{ font-size: 28rpx; } .contact-image{ width: 600rpx; height: 1075rpx; margin-top: 40rpx; } } ================================================ FILE: talkieai-uniapp/src/pages/feedback/index.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-easyinput/package.json ================================================ { "id": "uni-easyinput", "displayName": "uni-easyinput 增强输入框", "version": "1.1.9", "description": "Easyinput 组件是对原生input组件的增强", "keywords": [ "uni-ui", "uniui", "input", "uni-easyinput", "输入框" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-easyinput/readme.md ================================================ ### Easyinput 增强输入框 > **组件名:uni-easyinput** > 代码块: `uEasyinput` easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fab/changelog.md ================================================ ## 1.2.5(2023-03-29) - 新增 pattern.icon 属性,可自定义图标 ## 1.2.4(2022-09-07) 小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) ## 1.2.3(2022-09-05) - 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) ## 1.2.2(2021-12-29) - 更新 组件依赖 ## 1.2.1(2021-11-19) - 修复 阴影颜色不正确的bug ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab) ## 1.1.1(2021-11-09) - 新增 提供组件设计资源,组件样式调整 ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-05-12) - 新增 组件示例地址 ## 1.0.6(2021-02-05) - 调整为uni_modules目录规范 - 优化 按钮背景色调整 - 优化 兼容pc端 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fab/components/uni-fab/uni-fab.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fab/package.json ================================================ { "id": "uni-fab", "displayName": "uni-fab 悬浮按钮", "version": "1.2.5", "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。", "keywords": [ "uni-ui", "uniui", "按钮", "悬浮按钮", "fab" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss","uni-icons"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fab/readme.md ================================================ ## Fab 悬浮按钮 > **组件名:uni-fab** > 代码块: `uFab` 点击可展开一个图形按钮菜单 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/changelog.md ================================================ ## 1.2.1(2022-05-30) - 新增 stat 属性 ,是否开启uni统计功能 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav) ## 1.1.1(2021-08-24) - 新增 支持国际化 ## 1.1.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.6(2021-05-12) - 新增 组件示例地址 ## 1.0.5(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.4(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.3(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.2(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/components/uni-fav/i18n/en.json ================================================ { "uni-fav.collect": "collect", "uni-fav.collected": "collected" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/components/uni-fav/i18n/index.js ================================================ import en from './en.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json ================================================ { "uni-fav.collect": "收藏", "uni-fav.collected": "已收藏" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json ================================================ { "uni-fav.collect": "收藏", "uni-fav.collected": "已收藏" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/components/uni-fav/uni-fav.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/package.json ================================================ { "id": "uni-fav", "displayName": "uni-fav 收藏按钮", "version": "1.2.1", "description": " Fav 收藏组件,可自定义颜色、大小。", "keywords": [ "fav", "uni-ui", "uniui", "收藏" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-fav/readme.md ================================================ ## Fav 收藏按钮 > **组件名:uni-fav** > 代码块: `uFav` 用于收藏功能,可点击切换选中、不选中的状态。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/changelog.md ================================================ ## 1.0.4(2023-03-29) - 修复 手动上传删除一个文件后不能再上传的bug ## 1.0.3(2022-12-19) - 新增 sourceType 属性, 可以自定义图片和视频选择的来源 ## 1.0.2(2022-07-04) - 修复 在uni-forms下样式不生效的bug ## 1.0.1(2021-11-23) - 修复 参数为对象的情况下,url在某些情况显示错误的bug ## 1.0.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker) ## 0.2.16(2021-11-08) - 修复 传入空对象 ,显示错误的Bug ## 0.2.15(2021-08-30) - 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug ## 0.2.14(2021-08-23) - 新增 参数中返回 fileID 字段 ## 0.2.13(2021-08-23) - 修复 腾讯云传入fileID 不能回显的bug - 修复 选择图片后,不能放大的问题 ## 0.2.12(2021-08-17) - 修复 由于 0.2.11 版本引起的不能回显图片的Bug ## 0.2.11(2021-08-16) - 新增 clearFiles(index) 方法,可以手动删除指定文件 - 修复 v-model 值设为 null 报错的Bug ## 0.2.10(2021-08-13) - 修复 return-type="object" 时,无法删除文件的Bug ## 0.2.9(2021-08-03) - 修复 auto-upload 属性失效的Bug ## 0.2.8(2021-07-31) - 修复 fileExtname属性不指定值报错的Bug ## 0.2.7(2021-07-31) - 修复 在某种场景下图片不回显的Bug ## 0.2.6(2021-07-30) - 修复 return-type为object下,返回值不正确的Bug ## 0.2.5(2021-07-30) - 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 ## 0.2.3(2021-07-28) - 优化 调整示例代码 ## 0.2.2(2021-07-27) - 修复 vue3 下赋值错误的Bug - 优化 h5平台下上传文件导致页面卡死的问题 ## 0.2.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 0.1.1(2021-07-02) - 修复 sourceType 缺少默认值导致 ios 无法选择文件 ## 0.1.0(2021-06-30) - 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 ## 0.0.11(2021-06-30) - 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 ## 0.0.10(2021-06-29) - 优化 文件上传后进度条消失时机 ## 0.0.9(2021-06-29) - 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug ## 0.0.8(2021-06-15) - 修复 删除文件时无法触发 v-model 的Bug ## 0.0.7(2021-05-12) - 新增 组件示例地址 ## 0.0.6(2021-04-09) - 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug ## 0.0.5(2021-04-09) - 优化 更新组件示例 ## 0.0.4(2021-04-09) - 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 ## 0.0.3(2021-02-05) - 调整为uni_modules目录规范 - 修复 微信小程序不指定 fileExtname 属性选择失败的Bug ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js ================================================ 'use strict'; const ERR_MSG_OK = 'chooseAndUploadFile:ok'; const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; function chooseImage(opts) { const { count, sizeType = ['original', 'compressed'], sourceType, extension } = opts return new Promise((resolve, reject) => { uni.chooseImage({ count, sizeType, sourceType, extension, success(res) { resolve(normalizeChooseAndUploadFileRes(res, 'image')); }, fail(res) { reject({ errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), }); }, }); }); } function chooseVideo(opts) { const { camera, compressed, maxDuration, sourceType, extension } = opts; return new Promise((resolve, reject) => { uni.chooseVideo({ camera, compressed, maxDuration, sourceType, extension, success(res) { const { tempFilePath, duration, size, height, width } = res; resolve(normalizeChooseAndUploadFileRes({ errMsg: 'chooseVideo:ok', tempFilePaths: [tempFilePath], tempFiles: [ { name: (res.tempFile && res.tempFile.name) || '', path: tempFilePath, size, type: (res.tempFile && res.tempFile.type) || '', width, height, duration, fileType: 'video', cloudPath: '', }, ], }, 'video')); }, fail(res) { reject({ errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), }); }, }); }); } function chooseAll(opts) { const { count, extension } = opts; return new Promise((resolve, reject) => { let chooseFile = uni.chooseFile; if (typeof wx !== 'undefined' && typeof wx.chooseMessageFile === 'function') { chooseFile = wx.chooseMessageFile; } if (typeof chooseFile !== 'function') { return reject({ errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', }); } chooseFile({ type: 'all', count, extension, success(res) { resolve(normalizeChooseAndUploadFileRes(res)); }, fail(res) { reject({ errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), }); }, }); }); } function normalizeChooseAndUploadFileRes(res, fileType) { res.tempFiles.forEach((item, index) => { if (!item.name) { item.name = item.path.substring(item.path.lastIndexOf('/') + 1); } if (fileType) { item.fileType = fileType; } item.cloudPath = Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); }); if (!res.tempFilePaths) { res.tempFilePaths = res.tempFiles.map((file) => file.path); } return res; } function uploadCloudFiles(files, max = 5, onUploadProgress) { files = JSON.parse(JSON.stringify(files)) const len = files.length let count = 0 let self = this return new Promise(resolve => { while (count < max) { next() } function next() { let cur = count++ if (cur >= len) { !files.find(item => !item.url && !item.errMsg) && resolve(files) return } const fileItem = files[cur] const index = self.files.findIndex(v => v.uuid === fileItem.uuid) fileItem.url = '' delete fileItem.errMsg uniCloud .uploadFile({ filePath: fileItem.path, cloudPath: fileItem.cloudPath, fileType: fileItem.fileType, onUploadProgress: res => { res.index = index onUploadProgress && onUploadProgress(res) } }) .then(res => { fileItem.url = res.fileID fileItem.index = index if (cur < len) { next() } }) .catch(res => { fileItem.errMsg = res.errMsg || res.message fileItem.index = index if (cur < len) { next() } }) } }) } function uploadFiles(choosePromise, { onChooseFile, onUploadProgress }) { return choosePromise .then((res) => { if (onChooseFile) { const customChooseRes = onChooseFile(res); if (typeof customChooseRes !== 'undefined') { return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? res : chooseRes); } } return res; }) .then((res) => { if (res === false) { return { errMsg: ERR_MSG_OK, tempFilePaths: [], tempFiles: [], }; } return res }) } function chooseAndUploadFile(opts = { type: 'all' }) { if (opts.type === 'image') { return uploadFiles(chooseImage(opts), opts); } else if (opts.type === 'video') { return uploadFiles(chooseVideo(opts), opts); } return uploadFiles(chooseAll(opts), opts); } export { chooseAndUploadFile, uploadCloudFiles }; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/components/uni-file-picker/utils.js ================================================ /** * 获取文件名和后缀 * @param {String} name */ export const get_file_ext = (name) => { const last_len = name.lastIndexOf('.') const len = name.length return { name: name.substring(0, last_len), ext: name.substring(last_len + 1, len) } } /** * 获取扩展名 * @param {Array} fileExtname */ export const get_extname = (fileExtname) => { if (!Array.isArray(fileExtname)) { let extname = fileExtname.replace(/(\[|\])/g, '') return extname.split(',') } else { return fileExtname } return [] } /** * 获取文件和检测是否可选 */ export const get_files_and_is_max = (res, _extname) => { let filePaths = [] let files = [] if(!_extname || _extname.length === 0){ return { filePaths, files } } res.tempFiles.forEach(v => { let fileFullName = get_file_ext(v.name) const extname = fileFullName.ext.toLowerCase() if (_extname.indexOf(extname) !== -1) { files.push(v) filePaths.push(v.path) } }) if (files.length !== res.tempFiles.length) { uni.showToast({ title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, icon: 'none', duration: 5000 }) } return { filePaths, files } } /** * 获取图片信息 * @param {Object} filepath */ export const get_file_info = (filepath) => { return new Promise((resolve, reject) => { uni.getImageInfo({ src: filepath, success(res) { resolve(res) }, fail(err) { reject(err) } }) }) } /** * 获取封装数据 */ export const get_file_data = async (files, type = 'image') => { // 最终需要上传数据库的数据 let fileFullName = get_file_ext(files.name) const extname = fileFullName.ext.toLowerCase() let filedata = { name: files.name, uuid: files.uuid, extname: extname || '', cloudPath: files.cloudPath, fileType: files.fileType, url: files.path || files.path, size: files.size, //单位是字节 image: {}, path: files.path, video: {} } if (type === 'image') { const imageinfo = await get_file_info(files.path) delete filedata.video filedata.image.width = imageinfo.width filedata.image.height = imageinfo.height filedata.image.location = imageinfo.path } else { delete filedata.image } return filedata } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/package.json ================================================ { "id": "uni-file-picker", "displayName": "uni-file-picker 文件选择上传", "version": "1.0.4", "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", "keywords": [ "uni-ui", "uniui", "图片上传", "文件上传" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "n" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-file-picker/readme.md ================================================ ## FilePicker 文件选择上传 > **组件名:uni-file-picker** > 代码块: `uFilePicker` 文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/changelog.md ================================================ ## 1.4.9(2023-02-10) - 修复 required 参数无法动态绑定 ## 1.4.8(2022-08-23) - 优化 根据 rules 自动添加 required 的问题 ## 1.4.7(2022-08-22) - 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) ## 1.4.6(2022-07-13) - 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug ## 1.4.5(2022-07-05) - 新增 更多表单示例 - 优化 子表单组件过期提示的问题 - 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 ## 1.4.4(2022-07-04) - 更新 删除组件日志 ## 1.4.3(2022-07-04) - 修复 由 1.4.0 引发的 label 插槽不生效的bug ## 1.4.2(2022-07-04) - 修复 子组件找不到 setValue 报错的bug ## 1.4.1(2022-07-04) - 修复 uni-data-picker 在 uni-forms-item 中报错的bug - 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug ## 1.4.0(2022-06-30) - 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题 - 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 - 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 - 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 - 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 - 新增 子表单的 setRules 方法,配合自定义校验函数使用 - 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 - 优化 动态表单校验方式,废弃拼接name的方式 ## 1.3.3(2022-06-22) - 修复 表单校验顺序无序问题 ## 1.3.2(2021-12-09) - ## 1.3.1(2021-11-19) - 修复 label 插槽不生效的bug ## 1.3.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms) ## 1.2.7(2021-08-13) - 修复 没有添加校验规则的字段依然报错的Bug ## 1.2.6(2021-08-11) - 修复 重置表单错误信息无法清除的问题 ## 1.2.5(2021-08-11) - 优化 组件文档 ## 1.2.4(2021-08-11) - 修复 表单验证只生效一次的问题 ## 1.2.3(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.2.2(2021-07-26) - 修复 vue2 下条件编译导致destroyed生命周期失效的Bug - 修复 1.2.1 引起的示例在小程序平台报错的Bug ## 1.2.1(2021-07-22) - 修复 动态校验表单,默认值为空的情况下校验失效的Bug - 修复 不指定name属性时,运行报错的Bug - 优化 label默认宽度从65调整至70,使required为true且四字时不换行 - 优化 组件示例,新增动态校验示例代码 - 优化 组件文档,使用方式更清晰 ## 1.2.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.1.2(2021-06-25) - 修复 pattern 属性在微信小程序平台无效的问题 ## 1.1.1(2021-06-22) - 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug ## 1.1.0(2021-06-22) - 修复 只写setRules方法而导致校验不生效的Bug - 修复 由上个办法引发的错误提示文字错位的Bug ## 1.0.48(2021-06-21) - 修复 不设置 label 属性 ,无法设置label插槽的问题 ## 1.0.47(2021-06-21) - 修复 不设置label属性,label-width属性不生效的bug - 修复 setRules 方法与rules属性冲突的问题 ## 1.0.46(2021-06-04) - 修复 动态删减数据导致报错的问题 ## 1.0.45(2021-06-04) - 新增 modelValue 属性 ,value 即将废弃 ## 1.0.44(2021-06-02) - 新增 uni-forms-item 可以设置单独的 rules - 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 - 优化 submit 事件重命名为 validate ## 1.0.43(2021-05-12) - 新增 组件示例地址 ## 1.0.42(2021-04-30) - 修复 自定义检验器失效的问题 ## 1.0.41(2021-03-05) - 更新 校验器 - 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug ## 1.0.40(2021-03-04) - 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug ## 1.0.39(2021-02-05) - 调整为uni_modules目录规范 - 修复 校验器传入 int 等类型 ,返回String类型的Bug ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/components/uni-forms/uni-forms.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/components/uni-forms/utils.js ================================================ /** * 简单处理对象拷贝 * @param {Obejct} 被拷贝对象 * @@return {Object} 拷贝对象 */ export const deepCopy = (val) => { return JSON.parse(JSON.stringify(val)) } /** * 过滤数字类型 * @param {String} format 数字类型 * @@return {Boolean} 返回是否为数字类型 */ export const typeFilter = (format) => { return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp'; } /** * 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined * @param {String} key 字段名 * @param {any} value 字段值 * @param {Object} rules 表单校验规则 */ export const getValue = (key, value, rules) => { const isRuleNumType = rules.find(val => val.format && typeFilter(val.format)); const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool'); // 输入类型为 number if (!!isRuleNumType) { if (!value && value !== 0) { value = null } else { value = isNumber(Number(value)) ? Number(value) : value } } // 输入类型为 boolean if (!!isRuleBoolType) { value = isBoolean(value) ? value : false } return value; } /** * 获取表单数据 * @param {String|Array} name 真实名称,需要使用 realName 获取 * @param {Object} data 原始数据 * @param {any} value 需要设置的值 */ export const setDataValue = (field, formdata, value) => { formdata[field] = value return value || '' } /** * 获取表单数据 * @param {String|Array} field 真实名称,需要使用 realName 获取 * @param {Object} data 原始数据 */ export const getDataValue = (field, data) => { return objGet(data, field) } /** * 获取表单类型 * @param {String|Array} field 真实名称,需要使用 realName 获取 */ export const getDataValueType = (field, data) => { const value = getDataValue(field, data) return { type: type(value), value } } /** * 获取表单可用的真实name * @param {String|Array} name 表单name * @@return {String} 表单可用的真实name */ export const realName = (name, data = {}) => { const base_name = _basePath(name) if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) { const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_') return realname } return base_name[0] || name } /** * 判断是否表单可用的真实name * @param {String|Array} name 表单name * @@return {String} 表单可用的真实name */ export const isRealName = (name) => { const reg = /^_formdata_#*/ return reg.test(name) } /** * 获取表单数据的原始格式 * @@return {Object|Array} object 需要解析的数据 */ export const rawData = (object = {}, name) => { let newData = JSON.parse(JSON.stringify(object)) let formData = {} for(let i in newData){ let path = name2arr(i) objSet(formData,path,newData[i]) } return formData } /** * 真实name还原为 array * @param {*} name */ export const name2arr = (name) => { let field = name.replace('_formdata_#', '') field = field.split('#').map(v => (isNumber(v) ? Number(v) : v)) return field } /** * 对象中设置值 * @param {Object|Array} object 源数据 * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] * @param {String} value 需要设置的值 */ export const objSet = (object, path, value) => { if (typeof object !== 'object') return object; _basePath(path).reduce((o, k, i, _) => { if (i === _.length - 1) { // 若遍历结束直接赋值 o[k] = value return null } else if (k in o) { // 若存在对应路径,则返回找到的对象,进行下一次遍历 return o[k] } else { // 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象 o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {} return o[k] } }, object) // 返回object return object; } // 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用 function _basePath(path) { // 若是数组,则直接返回 if (Array.isArray(path)) return path // 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']' return path.replace(/\[/g, '.').replace(/\]/g, '').split('.') } /** * 从对象中获取值 * @param {Object|Array} object 源数据 * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] * @param {String} defaultVal 如果无法从调用链中获取值的默认值 */ export const objGet = (object, path, defaultVal = 'undefined') => { // 先将path处理成统一格式 let newPath = _basePath(path) // 递归处理,返回最后结果 let val = newPath.reduce((o, k) => { return (o || {})[k] }, object); return !val || val !== undefined ? val : defaultVal } /** * 是否为 number 类型 * @param {any} num 需要判断的值 * @return {Boolean} 是否为 number */ export const isNumber = (num) => { return !isNaN(Number(num)) } /** * 是否为 boolean 类型 * @param {any} bool 需要判断的值 * @return {Boolean} 是否为 boolean */ export const isBoolean = (bool) => { return (typeof bool === 'boolean') } /** * 是否有必填字段 * @param {Object} rules 规则 * @return {Boolean} 是否有必填字段 */ export const isRequiredField = (rules) => { let isNoField = false; for (let i = 0; i < rules.length; i++) { const ruleData = rules[i]; if (ruleData.required) { isNoField = true; break; } } return isNoField; } /** * 获取数据类型 * @param {Any} obj 需要获取数据类型的值 */ export const type = (obj) => { var class2type = {}; // 生成class2type映射 "Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) { class2type["[object " + item + "]"] = item.toLowerCase(); }) if (obj == null) { return obj + ""; } return typeof obj === "object" || typeof obj === "function" ? class2type[Object.prototype.toString.call(obj)] || "object" : typeof obj; } /** * 判断两个值是否相等 * @param {any} a 值 * @param {any} b 值 * @return {Boolean} 是否相等 */ export const isEqual = (a, b) => { //如果a和b本来就全等 if (a === b) { //判断是否为0和-0 return a !== 0 || 1 / a === 1 / b; } //判断是否为null和undefined if (a == null || b == null) { return a === b; } //接下来判断a和b的数据类型 var classNameA = toString.call(a), classNameB = toString.call(b); //如果数据类型不相等,则返回false if (classNameA !== classNameB) { return false; } //如果数据类型相等,再根据不同数据类型分别判断 switch (classNameA) { case '[object RegExp]': case '[object String]': //进行字符串转换比较 return '' + a === '' + b; case '[object Number]': //进行数字转换比较,判断是否为NaN if (+a !== +a) { return +b !== +b; } //判断是否为0或-0 return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': return +a === +b; } //如果是对象类型 if (classNameA == '[object Object]') { //获取a和b的属性长度 var propsA = Object.getOwnPropertyNames(a), propsB = Object.getOwnPropertyNames(b); if (propsA.length != propsB.length) { return false; } for (var i = 0; i < propsA.length; i++) { var propName = propsA[i]; //如果对应属性对应值不相等,则返回false if (a[propName] !== b[propName]) { return false; } } return true; } //如果是数组类型 if (classNameA == '[object Array]') { if (a.toString() == b.toString()) { return true; } return false; } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/components/uni-forms/validate.js ================================================ var pattern = { email: /^\S+?@\S+?\.\S+?$/, idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, url: new RegExp( "^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", 'i') }; const FORMAT_MAPPING = { "int": 'integer', "bool": 'boolean', "double": 'number', "long": 'number', "password": 'string' // "fileurls": 'array' } function formatMessage(args, resources = '') { var defaultMessage = ['label'] defaultMessage.forEach((item) => { if (args[item] === undefined) { args[item] = '' } }) let str = resources for (let key in args) { let reg = new RegExp('{' + key + '}') str = str.replace(reg, args[key]) } return str } function isEmptyValue(value, type) { if (value === undefined || value === null) { return true; } if (typeof value === 'string' && !value) { return true; } if (Array.isArray(value) && !value.length) { return true; } if (type === 'object' && !Object.keys(value).length) { return true; } return false; } const types = { integer(value) { return types.number(value) && parseInt(value, 10) === value; }, string(value) { return typeof value === 'string'; }, number(value) { if (isNaN(value)) { return false; } return typeof value === 'number'; }, "boolean": function(value) { return typeof value === 'boolean'; }, "float": function(value) { return types.number(value) && !types.integer(value); }, array(value) { return Array.isArray(value); }, object(value) { return typeof value === 'object' && !types.array(value); }, date(value) { return value instanceof Date; }, timestamp(value) { if (!this.integer(value) || Math.abs(value).toString().length > 16) { return false } return true; }, file(value) { return typeof value.url === 'string'; }, email(value) { return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; }, url(value) { return typeof value === 'string' && !!value.match(pattern.url); }, pattern(reg, value) { try { return new RegExp(reg).test(value); } catch (e) { return false; } }, method(value) { return typeof value === 'function'; }, idcard(value) { return typeof value === 'string' && !!value.match(pattern.idcard); }, 'url-https'(value) { return this.url(value) && value.startsWith('https://'); }, 'url-scheme'(value) { return value.startsWith('://'); }, 'url-web'(value) { return false; } } class RuleValidator { constructor(message) { this._message = message } async validateRule(fieldKey, fieldValue, value, data, allData) { var result = null let rules = fieldValue.rules let hasRequired = rules.findIndex((item) => { return item.required }) if (hasRequired < 0) { if (value === null || value === undefined) { return result } if (typeof value === 'string' && !value.length) { return result } } var message = this._message if (rules === undefined) { return message['default'] } for (var i = 0; i < rules.length; i++) { let rule = rules[i] let vt = this._getValidateType(rule) Object.assign(rule, { label: fieldValue.label || `["${fieldKey}"]` }) if (RuleValidatorHelper[vt]) { result = RuleValidatorHelper[vt](rule, value, message) if (result != null) { break } } if (rule.validateExpr) { let now = Date.now() let resultExpr = rule.validateExpr(value, allData, now) if (resultExpr === false) { result = this._getMessage(rule, rule.errorMessage || this._message['default']) break } } if (rule.validateFunction) { result = await this.validateFunction(rule, value, data, allData, vt) if (result !== null) { break } } } if (result !== null) { result = message.TAG + result } return result } async validateFunction(rule, value, data, allData, vt) { let result = null try { let callbackMessage = null const res = await rule.validateFunction(rule, value, allData || data, (message) => { callbackMessage = message }) if (callbackMessage || (typeof res === 'string' && res) || res === false) { result = this._getMessage(rule, callbackMessage || res, vt) } } catch (e) { result = this._getMessage(rule, e.message, vt) } return result } _getMessage(rule, message, vt) { return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) } _getValidateType(rule) { var result = '' if (rule.required) { result = 'required' } else if (rule.format) { result = 'format' } else if (rule.arrayType) { result = 'arrayTypeFormat' } else if (rule.range) { result = 'range' } else if (rule.maximum !== undefined || rule.minimum !== undefined) { result = 'rangeNumber' } else if (rule.maxLength !== undefined || rule.minLength !== undefined) { result = 'rangeLength' } else if (rule.pattern) { result = 'pattern' } else if (rule.validateFunction) { result = 'validateFunction' } return result } } const RuleValidatorHelper = { required(rule, value, message) { if (rule.required && isEmptyValue(value, rule.format || typeof value)) { return formatMessage(rule, rule.errorMessage || message.required); } return null }, range(rule, value, message) { const { range, errorMessage } = rule; let list = new Array(range.length); for (let i = 0; i < range.length; i++) { const item = range[i]; if (types.object(item) && item.value !== undefined) { list[i] = item.value; } else { list[i] = item; } } let result = false if (Array.isArray(value)) { result = (new Set(value.concat(list)).size === list.length); } else { if (list.indexOf(value) > -1) { result = true; } } if (!result) { return formatMessage(rule, errorMessage || message['enum']); } return null }, rangeNumber(rule, value, message) { if (!types.number(value)) { return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); } let { minimum, maximum, exclusiveMinimum, exclusiveMaximum } = rule; let min = exclusiveMinimum ? value <= minimum : value < minimum; let max = exclusiveMaximum ? value >= maximum : value > maximum; if (minimum !== undefined && min) { return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? 'exclusiveMinimum' : 'minimum' ]) } else if (maximum !== undefined && max) { return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? 'exclusiveMaximum' : 'maximum' ]) } else if (minimum !== undefined && maximum !== undefined && (min || max)) { return formatMessage(rule, rule.errorMessage || message['number'].range) } return null }, rangeLength(rule, value, message) { if (!types.string(value) && !types.array(value)) { return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); } let min = rule.minLength; let max = rule.maxLength; let val = value.length; if (min !== undefined && val < min) { return formatMessage(rule, rule.errorMessage || message['length'].minLength) } else if (max !== undefined && val > max) { return formatMessage(rule, rule.errorMessage || message['length'].maxLength) } else if (min !== undefined && max !== undefined && (val < min || val > max)) { return formatMessage(rule, rule.errorMessage || message['length'].range) } return null }, pattern(rule, value, message) { if (!types['pattern'](rule.pattern, value)) { return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); } return null }, format(rule, value, message) { var customTypes = Object.keys(types); var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); if (customTypes.indexOf(format) > -1) { if (!types[format](value)) { return formatMessage(rule, rule.errorMessage || message.typeError); } } return null }, arrayTypeFormat(rule, value, message) { if (!Array.isArray(value)) { return formatMessage(rule, rule.errorMessage || message.typeError); } for (let i = 0; i < value.length; i++) { const element = value[i]; let formatResult = this.format(rule, element, message) if (formatResult !== null) { return formatResult } } return null } } class SchemaValidator extends RuleValidator { constructor(schema, options) { super(SchemaValidator.message); this._schema = schema this._options = options || null } updateSchema(schema) { this._schema = schema } async validate(data, allData) { let result = this._checkFieldInSchema(data) if (!result) { result = await this.invokeValidate(data, false, allData) } return result.length ? result[0] : null } async validateAll(data, allData) { let result = this._checkFieldInSchema(data) if (!result) { result = await this.invokeValidate(data, true, allData) } return result } async validateUpdate(data, allData) { let result = this._checkFieldInSchema(data) if (!result) { result = await this.invokeValidateUpdate(data, false, allData) } return result.length ? result[0] : null } async invokeValidate(data, all, allData) { let result = [] let schema = this._schema for (let key in schema) { let value = schema[key] let errorMessage = await this.validateRule(key, value, data[key], data, allData) if (errorMessage != null) { result.push({ key, errorMessage }) if (!all) break } } return result } async invokeValidateUpdate(data, all, allData) { let result = [] for (let key in data) { let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) if (errorMessage != null) { result.push({ key, errorMessage }) if (!all) break } } return result } _checkFieldInSchema(data) { var keys = Object.keys(data) var keys2 = Object.keys(this._schema) if (new Set(keys.concat(keys2)).size === keys2.length) { return '' } var noExistFields = keys.filter((key) => { return keys2.indexOf(key) < 0; }) var errorMessage = formatMessage({ field: JSON.stringify(noExistFields) }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) return [{ key: 'invalid', errorMessage }] } } function Message() { return { TAG: "", default: '验证错误', defaultInvalid: '提交的字段{field}在数据库中并不存在', validateFunction: '验证无效', required: '{label}必填', 'enum': '{label}超出范围', timestamp: '{label}格式无效', whitespace: '{label}不能为空', typeError: '{label}类型无效', date: { format: '{label}日期{value}格式无效', parse: '{label}日期无法解析,{value}无效', invalid: '{label}日期{value}无效' }, length: { minLength: '{label}长度不能少于{minLength}', maxLength: '{label}长度不能超过{maxLength}', range: '{label}必须介于{minLength}和{maxLength}之间' }, number: { minimum: '{label}不能小于{minimum}', maximum: '{label}不能大于{maximum}', exclusiveMinimum: '{label}不能小于等于{minimum}', exclusiveMaximum: '{label}不能大于等于{maximum}', range: '{label}必须介于{minimum}and{maximum}之间' }, pattern: { mismatch: '{label}格式不匹配' } }; } SchemaValidator.message = new Message(); export default SchemaValidator ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/package.json ================================================ { "id": "uni-forms", "displayName": "uni-forms 表单", "version": "1.4.9", "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", "keywords": [ "uni-ui", "表单", "校验", "表单校验", "表单验证" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y", "京东": "u" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-forms/readme.md ================================================ ## Forms 表单 > **组件名:uni-forms** > 代码块: `uForms`、`uni-forms-item` > 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。 uni-app的内置组件已经有了 `
`组件,用于提交表单内容。 然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 ``组件封装了 ``组件,内置了表单验证功能。 `` 提供了 `rules`属性来描述校验规则、``子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。 每个要校验的表单项,不管input还是checkbox,都必须放在``组件中,且一个``组件只能放置一个表单项。 ``组件内部预留了显示error message的区域,默认是在表单项的底部。 另外,``组件下面的各个表单项,可以通过``包裹为不同的分组。同一``下的不同表单项目将聚拢在一起,同其他group保持垂直间距。``仅影响视觉效果。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/changelog.md ================================================ ## 1.2.1(2022-05-30) - 新增 stat属性,是否开启uni统计功能 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-goods-nav](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) ## 1.1.1(2021-08-24) - 新增 支持国际化 ## 1.1.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-05-12) - 新增 组件示例地址 ## 1.0.6(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.5(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json ================================================ { "uni-goods-nav.options.shop": "shop", "uni-goods-nav.options.cart": "cart", "uni-goods-nav.buttonGroup.addToCart": "add to cart", "uni-goods-nav.buttonGroup.buyNow": "buy now" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js ================================================ import en from './en.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json ================================================ { "uni-goods-nav.options.shop": "店铺", "uni-goods-nav.options.cart": "购物车", "uni-goods-nav.buttonGroup.addToCart": "加入购物车", "uni-goods-nav.buttonGroup.buyNow": "立即购买" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json ================================================ { "uni-goods-nav.options.shop": "店鋪", "uni-goods-nav.options.cart": "購物車", "uni-goods-nav.buttonGroup.addToCart": "加入購物車", "uni-goods-nav.buttonGroup.buyNow": "立即購買" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/package.json ================================================ { "id": "uni-goods-nav", "displayName": "uni-goods-nav 商品导航", "version": "1.2.1", "description": "商品导航组件主要用于电商类应用底部导航,可自定义加入购物车,购买等操作", "keywords": [ "uni-ui", "uniui", "商品导航" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-goods-nav/readme.md ================================================ ## GoodsNav 商品导航 > **组件名:uni-goods-nav** > 代码块: `uGoodsNav` 商品加入购物车,立即购买等。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-grid/changelog.md ================================================ ## 1.4.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-grid](https://uniapp.dcloud.io/component/uniui/uni-grid) ## 1.3.2(2021-11-09) - 新增 提供组件设计资源,组件样式调整 ## 1.3.1(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.3.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.2.4(2021-05-12) - 新增 组件示例地址 ## 1.2.3(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-grid/components/uni-grid/uni-grid.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-grid/package.json ================================================ { "id": "uni-grid", "displayName": "uni-grid 宫格", "version": "1.4.0", "description": "Grid 宫格组件,提供移动端常见的宫格布局,如九宫格。", "keywords": [ "uni-ui", "uniui", "九宫格", "表格" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss","uni-icons"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-grid/readme.md ================================================ ## Grid 宫格 > **组件名:uni-grid** > 代码块: `uGrid` 宫格组件。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-grid) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-group/changelog.md ================================================ ## 1.2.2(2022-05-30) - 新增 stat属性,是否开启uni统计功能 ## 1.2.1(2021-11-22) - 修复 vue3中某些scss变量无法找到的问题 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-group](https://uniapp.dcloud.io/component/uniui/uni-group) ## 1.1.7(2021-11-08) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) - 优化 组件文档 ## 1.0.3(2021-05-12) - 新增 组件示例地址 ## 1.0.2(2021-02-05) - 调整为uni_modules目录规范 - 优化 兼容 nvue 页面 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-group/components/uni-group/uni-group.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-group/package.json ================================================ { "id": "uni-group", "displayName": "uni-group 分组", "version": "1.2.2", "description": "分组组件可用于将组件用于分组,添加间隔,以产生明显的区块", "keywords": [ "uni-ui", "uniui", "group", "分组", "" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-group/readme.md ================================================ ## Group 分组 > **组件名:uni-group** > 代码块: `uGroup` 分组组件可用于将组件分组,添加间隔,以产生明显的区块。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-group) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/changelog.md ================================================ ## 1.3.5(2022-01-24) - 优化 size 属性可以传入不带单位的字符串数值 ## 1.3.4(2022-01-24) - 优化 size 支持其他单位 ## 1.3.3(2022-01-17) - 修复 nvue 有些图标不显示的bug,兼容老版本图标 ## 1.3.2(2021-12-01) - 优化 示例可复制图标名称 ## 1.3.1(2021-11-23) - 优化 兼容旧组件 type 值 ## 1.3.0(2021-11-19) - 新增 更多图标 - 优化 自定义图标使用方式 - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons) ## 1.1.7(2021-11-08) ## 1.2.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.1.5(2021-05-12) - 新增 组件示例地址 ## 1.1.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/components/uni-icons/icons.js ================================================ export default { "id": "2852637", "name": "uniui图标库", "font_family": "uniicons", "css_prefix_text": "uniui-", "description": "", "glyphs": [ { "icon_id": "25027049", "name": "yanse", "font_class": "color", "unicode": "e6cf", "unicode_decimal": 59087 }, { "icon_id": "25027048", "name": "wallet", "font_class": "wallet", "unicode": "e6b1", "unicode_decimal": 59057 }, { "icon_id": "25015720", "name": "settings-filled", "font_class": "settings-filled", "unicode": "e6ce", "unicode_decimal": 59086 }, { "icon_id": "25015434", "name": "shimingrenzheng-filled", "font_class": "auth-filled", "unicode": "e6cc", "unicode_decimal": 59084 }, { "icon_id": "24934246", "name": "shop-filled", "font_class": "shop-filled", "unicode": "e6cd", "unicode_decimal": 59085 }, { "icon_id": "24934159", "name": "staff-filled-01", "font_class": "staff-filled", "unicode": "e6cb", "unicode_decimal": 59083 }, { "icon_id": "24932461", "name": "VIP-filled", "font_class": "vip-filled", "unicode": "e6c6", "unicode_decimal": 59078 }, { "icon_id": "24932462", "name": "plus_circle_fill", "font_class": "plus-filled", "unicode": "e6c7", "unicode_decimal": 59079 }, { "icon_id": "24932463", "name": "folder_add-filled", "font_class": "folder-add-filled", "unicode": "e6c8", "unicode_decimal": 59080 }, { "icon_id": "24932464", "name": "yanse-filled", "font_class": "color-filled", "unicode": "e6c9", "unicode_decimal": 59081 }, { "icon_id": "24932465", "name": "tune-filled", "font_class": "tune-filled", "unicode": "e6ca", "unicode_decimal": 59082 }, { "icon_id": "24932455", "name": "a-rilidaka-filled", "font_class": "calendar-filled", "unicode": "e6c0", "unicode_decimal": 59072 }, { "icon_id": "24932456", "name": "notification-filled", "font_class": "notification-filled", "unicode": "e6c1", "unicode_decimal": 59073 }, { "icon_id": "24932457", "name": "wallet-filled", "font_class": "wallet-filled", "unicode": "e6c2", "unicode_decimal": 59074 }, { "icon_id": "24932458", "name": "paihangbang-filled", "font_class": "medal-filled", "unicode": "e6c3", "unicode_decimal": 59075 }, { "icon_id": "24932459", "name": "gift-filled", "font_class": "gift-filled", "unicode": "e6c4", "unicode_decimal": 59076 }, { "icon_id": "24932460", "name": "fire-filled", "font_class": "fire-filled", "unicode": "e6c5", "unicode_decimal": 59077 }, { "icon_id": "24928001", "name": "refreshempty", "font_class": "refreshempty", "unicode": "e6bf", "unicode_decimal": 59071 }, { "icon_id": "24926853", "name": "location-ellipse", "font_class": "location-filled", "unicode": "e6af", "unicode_decimal": 59055 }, { "icon_id": "24926735", "name": "person-filled", "font_class": "person-filled", "unicode": "e69d", "unicode_decimal": 59037 }, { "icon_id": "24926703", "name": "personadd-filled", "font_class": "personadd-filled", "unicode": "e698", "unicode_decimal": 59032 }, { "icon_id": "24923351", "name": "back", "font_class": "back", "unicode": "e6b9", "unicode_decimal": 59065 }, { "icon_id": "24923352", "name": "forward", "font_class": "forward", "unicode": "e6ba", "unicode_decimal": 59066 }, { "icon_id": "24923353", "name": "arrowthinright", "font_class": "arrow-right", "unicode": "e6bb", "unicode_decimal": 59067 }, { "icon_id": "24923353", "name": "arrowthinright", "font_class": "arrowthinright", "unicode": "e6bb", "unicode_decimal": 59067 }, { "icon_id": "24923354", "name": "arrowthinleft", "font_class": "arrow-left", "unicode": "e6bc", "unicode_decimal": 59068 }, { "icon_id": "24923354", "name": "arrowthinleft", "font_class": "arrowthinleft", "unicode": "e6bc", "unicode_decimal": 59068 }, { "icon_id": "24923355", "name": "arrowthinup", "font_class": "arrow-up", "unicode": "e6bd", "unicode_decimal": 59069 }, { "icon_id": "24923355", "name": "arrowthinup", "font_class": "arrowthinup", "unicode": "e6bd", "unicode_decimal": 59069 }, { "icon_id": "24923356", "name": "arrowthindown", "font_class": "arrow-down", "unicode": "e6be", "unicode_decimal": 59070 },{ "icon_id": "24923356", "name": "arrowthindown", "font_class": "arrowthindown", "unicode": "e6be", "unicode_decimal": 59070 }, { "icon_id": "24923349", "name": "arrowdown", "font_class": "bottom", "unicode": "e6b8", "unicode_decimal": 59064 },{ "icon_id": "24923349", "name": "arrowdown", "font_class": "arrowdown", "unicode": "e6b8", "unicode_decimal": 59064 }, { "icon_id": "24923346", "name": "arrowright", "font_class": "right", "unicode": "e6b5", "unicode_decimal": 59061 }, { "icon_id": "24923346", "name": "arrowright", "font_class": "arrowright", "unicode": "e6b5", "unicode_decimal": 59061 }, { "icon_id": "24923347", "name": "arrowup", "font_class": "top", "unicode": "e6b6", "unicode_decimal": 59062 }, { "icon_id": "24923347", "name": "arrowup", "font_class": "arrowup", "unicode": "e6b6", "unicode_decimal": 59062 }, { "icon_id": "24923348", "name": "arrowleft", "font_class": "left", "unicode": "e6b7", "unicode_decimal": 59063 }, { "icon_id": "24923348", "name": "arrowleft", "font_class": "arrowleft", "unicode": "e6b7", "unicode_decimal": 59063 }, { "icon_id": "24923334", "name": "eye", "font_class": "eye", "unicode": "e651", "unicode_decimal": 58961 }, { "icon_id": "24923335", "name": "eye-filled", "font_class": "eye-filled", "unicode": "e66a", "unicode_decimal": 58986 }, { "icon_id": "24923336", "name": "eye-slash", "font_class": "eye-slash", "unicode": "e6b3", "unicode_decimal": 59059 }, { "icon_id": "24923337", "name": "eye-slash-filled", "font_class": "eye-slash-filled", "unicode": "e6b4", "unicode_decimal": 59060 }, { "icon_id": "24923305", "name": "info-filled", "font_class": "info-filled", "unicode": "e649", "unicode_decimal": 58953 }, { "icon_id": "24923299", "name": "reload-01", "font_class": "reload", "unicode": "e6b2", "unicode_decimal": 59058 }, { "icon_id": "24923195", "name": "mic_slash_fill", "font_class": "micoff-filled", "unicode": "e6b0", "unicode_decimal": 59056 }, { "icon_id": "24923165", "name": "map-pin-ellipse", "font_class": "map-pin-ellipse", "unicode": "e6ac", "unicode_decimal": 59052 }, { "icon_id": "24923166", "name": "map-pin", "font_class": "map-pin", "unicode": "e6ad", "unicode_decimal": 59053 }, { "icon_id": "24923167", "name": "location", "font_class": "location", "unicode": "e6ae", "unicode_decimal": 59054 }, { "icon_id": "24923064", "name": "starhalf", "font_class": "starhalf", "unicode": "e683", "unicode_decimal": 59011 }, { "icon_id": "24923065", "name": "star", "font_class": "star", "unicode": "e688", "unicode_decimal": 59016 }, { "icon_id": "24923066", "name": "star-filled", "font_class": "star-filled", "unicode": "e68f", "unicode_decimal": 59023 }, { "icon_id": "24899646", "name": "a-rilidaka", "font_class": "calendar", "unicode": "e6a0", "unicode_decimal": 59040 }, { "icon_id": "24899647", "name": "fire", "font_class": "fire", "unicode": "e6a1", "unicode_decimal": 59041 }, { "icon_id": "24899648", "name": "paihangbang", "font_class": "medal", "unicode": "e6a2", "unicode_decimal": 59042 }, { "icon_id": "24899649", "name": "font", "font_class": "font", "unicode": "e6a3", "unicode_decimal": 59043 }, { "icon_id": "24899650", "name": "gift", "font_class": "gift", "unicode": "e6a4", "unicode_decimal": 59044 }, { "icon_id": "24899651", "name": "link", "font_class": "link", "unicode": "e6a5", "unicode_decimal": 59045 }, { "icon_id": "24899652", "name": "notification", "font_class": "notification", "unicode": "e6a6", "unicode_decimal": 59046 }, { "icon_id": "24899653", "name": "staff", "font_class": "staff", "unicode": "e6a7", "unicode_decimal": 59047 }, { "icon_id": "24899654", "name": "VIP", "font_class": "vip", "unicode": "e6a8", "unicode_decimal": 59048 }, { "icon_id": "24899655", "name": "folder_add", "font_class": "folder-add", "unicode": "e6a9", "unicode_decimal": 59049 }, { "icon_id": "24899656", "name": "tune", "font_class": "tune", "unicode": "e6aa", "unicode_decimal": 59050 }, { "icon_id": "24899657", "name": "shimingrenzheng", "font_class": "auth", "unicode": "e6ab", "unicode_decimal": 59051 }, { "icon_id": "24899565", "name": "person", "font_class": "person", "unicode": "e699", "unicode_decimal": 59033 }, { "icon_id": "24899566", "name": "email-filled", "font_class": "email-filled", "unicode": "e69a", "unicode_decimal": 59034 }, { "icon_id": "24899567", "name": "phone-filled", "font_class": "phone-filled", "unicode": "e69b", "unicode_decimal": 59035 }, { "icon_id": "24899568", "name": "phone", "font_class": "phone", "unicode": "e69c", "unicode_decimal": 59036 }, { "icon_id": "24899570", "name": "email", "font_class": "email", "unicode": "e69e", "unicode_decimal": 59038 }, { "icon_id": "24899571", "name": "personadd", "font_class": "personadd", "unicode": "e69f", "unicode_decimal": 59039 }, { "icon_id": "24899558", "name": "chatboxes-filled", "font_class": "chatboxes-filled", "unicode": "e692", "unicode_decimal": 59026 }, { "icon_id": "24899559", "name": "contact", "font_class": "contact", "unicode": "e693", "unicode_decimal": 59027 }, { "icon_id": "24899560", "name": "chatbubble-filled", "font_class": "chatbubble-filled", "unicode": "e694", "unicode_decimal": 59028 }, { "icon_id": "24899561", "name": "contact-filled", "font_class": "contact-filled", "unicode": "e695", "unicode_decimal": 59029 }, { "icon_id": "24899562", "name": "chatboxes", "font_class": "chatboxes", "unicode": "e696", "unicode_decimal": 59030 }, { "icon_id": "24899563", "name": "chatbubble", "font_class": "chatbubble", "unicode": "e697", "unicode_decimal": 59031 }, { "icon_id": "24881290", "name": "upload-filled", "font_class": "upload-filled", "unicode": "e68e", "unicode_decimal": 59022 }, { "icon_id": "24881292", "name": "upload", "font_class": "upload", "unicode": "e690", "unicode_decimal": 59024 }, { "icon_id": "24881293", "name": "weixin", "font_class": "weixin", "unicode": "e691", "unicode_decimal": 59025 }, { "icon_id": "24881274", "name": "compose", "font_class": "compose", "unicode": "e67f", "unicode_decimal": 59007 }, { "icon_id": "24881275", "name": "qq", "font_class": "qq", "unicode": "e680", "unicode_decimal": 59008 }, { "icon_id": "24881276", "name": "download-filled", "font_class": "download-filled", "unicode": "e681", "unicode_decimal": 59009 }, { "icon_id": "24881277", "name": "pengyouquan", "font_class": "pyq", "unicode": "e682", "unicode_decimal": 59010 }, { "icon_id": "24881279", "name": "sound", "font_class": "sound", "unicode": "e684", "unicode_decimal": 59012 }, { "icon_id": "24881280", "name": "trash-filled", "font_class": "trash-filled", "unicode": "e685", "unicode_decimal": 59013 }, { "icon_id": "24881281", "name": "sound-filled", "font_class": "sound-filled", "unicode": "e686", "unicode_decimal": 59014 }, { "icon_id": "24881282", "name": "trash", "font_class": "trash", "unicode": "e687", "unicode_decimal": 59015 }, { "icon_id": "24881284", "name": "videocam-filled", "font_class": "videocam-filled", "unicode": "e689", "unicode_decimal": 59017 }, { "icon_id": "24881285", "name": "spinner-cycle", "font_class": "spinner-cycle", "unicode": "e68a", "unicode_decimal": 59018 }, { "icon_id": "24881286", "name": "weibo", "font_class": "weibo", "unicode": "e68b", "unicode_decimal": 59019 }, { "icon_id": "24881288", "name": "videocam", "font_class": "videocam", "unicode": "e68c", "unicode_decimal": 59020 }, { "icon_id": "24881289", "name": "download", "font_class": "download", "unicode": "e68d", "unicode_decimal": 59021 }, { "icon_id": "24879601", "name": "help", "font_class": "help", "unicode": "e679", "unicode_decimal": 59001 }, { "icon_id": "24879602", "name": "navigate-filled", "font_class": "navigate-filled", "unicode": "e67a", "unicode_decimal": 59002 }, { "icon_id": "24879603", "name": "plusempty", "font_class": "plusempty", "unicode": "e67b", "unicode_decimal": 59003 }, { "icon_id": "24879604", "name": "smallcircle", "font_class": "smallcircle", "unicode": "e67c", "unicode_decimal": 59004 }, { "icon_id": "24879605", "name": "minus-filled", "font_class": "minus-filled", "unicode": "e67d", "unicode_decimal": 59005 }, { "icon_id": "24879606", "name": "micoff", "font_class": "micoff", "unicode": "e67e", "unicode_decimal": 59006 }, { "icon_id": "24879588", "name": "closeempty", "font_class": "closeempty", "unicode": "e66c", "unicode_decimal": 58988 }, { "icon_id": "24879589", "name": "clear", "font_class": "clear", "unicode": "e66d", "unicode_decimal": 58989 }, { "icon_id": "24879590", "name": "navigate", "font_class": "navigate", "unicode": "e66e", "unicode_decimal": 58990 }, { "icon_id": "24879591", "name": "minus", "font_class": "minus", "unicode": "e66f", "unicode_decimal": 58991 }, { "icon_id": "24879592", "name": "image", "font_class": "image", "unicode": "e670", "unicode_decimal": 58992 }, { "icon_id": "24879593", "name": "mic", "font_class": "mic", "unicode": "e671", "unicode_decimal": 58993 }, { "icon_id": "24879594", "name": "paperplane", "font_class": "paperplane", "unicode": "e672", "unicode_decimal": 58994 }, { "icon_id": "24879595", "name": "close", "font_class": "close", "unicode": "e673", "unicode_decimal": 58995 }, { "icon_id": "24879596", "name": "help-filled", "font_class": "help-filled", "unicode": "e674", "unicode_decimal": 58996 }, { "icon_id": "24879597", "name": "plus-filled", "font_class": "paperplane-filled", "unicode": "e675", "unicode_decimal": 58997 }, { "icon_id": "24879598", "name": "plus", "font_class": "plus", "unicode": "e676", "unicode_decimal": 58998 }, { "icon_id": "24879599", "name": "mic-filled", "font_class": "mic-filled", "unicode": "e677", "unicode_decimal": 58999 }, { "icon_id": "24879600", "name": "image-filled", "font_class": "image-filled", "unicode": "e678", "unicode_decimal": 59000 }, { "icon_id": "24855900", "name": "locked-filled", "font_class": "locked-filled", "unicode": "e668", "unicode_decimal": 58984 }, { "icon_id": "24855901", "name": "info", "font_class": "info", "unicode": "e669", "unicode_decimal": 58985 }, { "icon_id": "24855903", "name": "locked", "font_class": "locked", "unicode": "e66b", "unicode_decimal": 58987 }, { "icon_id": "24855884", "name": "camera-filled", "font_class": "camera-filled", "unicode": "e658", "unicode_decimal": 58968 }, { "icon_id": "24855885", "name": "chat-filled", "font_class": "chat-filled", "unicode": "e659", "unicode_decimal": 58969 }, { "icon_id": "24855886", "name": "camera", "font_class": "camera", "unicode": "e65a", "unicode_decimal": 58970 }, { "icon_id": "24855887", "name": "circle", "font_class": "circle", "unicode": "e65b", "unicode_decimal": 58971 }, { "icon_id": "24855888", "name": "checkmarkempty", "font_class": "checkmarkempty", "unicode": "e65c", "unicode_decimal": 58972 }, { "icon_id": "24855889", "name": "chat", "font_class": "chat", "unicode": "e65d", "unicode_decimal": 58973 }, { "icon_id": "24855890", "name": "circle-filled", "font_class": "circle-filled", "unicode": "e65e", "unicode_decimal": 58974 }, { "icon_id": "24855891", "name": "flag", "font_class": "flag", "unicode": "e65f", "unicode_decimal": 58975 }, { "icon_id": "24855892", "name": "flag-filled", "font_class": "flag-filled", "unicode": "e660", "unicode_decimal": 58976 }, { "icon_id": "24855893", "name": "gear-filled", "font_class": "gear-filled", "unicode": "e661", "unicode_decimal": 58977 }, { "icon_id": "24855894", "name": "home", "font_class": "home", "unicode": "e662", "unicode_decimal": 58978 }, { "icon_id": "24855895", "name": "home-filled", "font_class": "home-filled", "unicode": "e663", "unicode_decimal": 58979 }, { "icon_id": "24855896", "name": "gear", "font_class": "gear", "unicode": "e664", "unicode_decimal": 58980 }, { "icon_id": "24855897", "name": "smallcircle-filled", "font_class": "smallcircle-filled", "unicode": "e665", "unicode_decimal": 58981 }, { "icon_id": "24855898", "name": "map-filled", "font_class": "map-filled", "unicode": "e666", "unicode_decimal": 58982 }, { "icon_id": "24855899", "name": "map", "font_class": "map", "unicode": "e667", "unicode_decimal": 58983 }, { "icon_id": "24855825", "name": "refresh-filled", "font_class": "refresh-filled", "unicode": "e656", "unicode_decimal": 58966 }, { "icon_id": "24855826", "name": "refresh", "font_class": "refresh", "unicode": "e657", "unicode_decimal": 58967 }, { "icon_id": "24855808", "name": "cloud-upload", "font_class": "cloud-upload", "unicode": "e645", "unicode_decimal": 58949 }, { "icon_id": "24855809", "name": "cloud-download-filled", "font_class": "cloud-download-filled", "unicode": "e646", "unicode_decimal": 58950 }, { "icon_id": "24855810", "name": "cloud-download", "font_class": "cloud-download", "unicode": "e647", "unicode_decimal": 58951 }, { "icon_id": "24855811", "name": "cloud-upload-filled", "font_class": "cloud-upload-filled", "unicode": "e648", "unicode_decimal": 58952 }, { "icon_id": "24855813", "name": "redo", "font_class": "redo", "unicode": "e64a", "unicode_decimal": 58954 }, { "icon_id": "24855814", "name": "images-filled", "font_class": "images-filled", "unicode": "e64b", "unicode_decimal": 58955 }, { "icon_id": "24855815", "name": "undo-filled", "font_class": "undo-filled", "unicode": "e64c", "unicode_decimal": 58956 }, { "icon_id": "24855816", "name": "more", "font_class": "more", "unicode": "e64d", "unicode_decimal": 58957 }, { "icon_id": "24855817", "name": "more-filled", "font_class": "more-filled", "unicode": "e64e", "unicode_decimal": 58958 }, { "icon_id": "24855818", "name": "undo", "font_class": "undo", "unicode": "e64f", "unicode_decimal": 58959 }, { "icon_id": "24855819", "name": "images", "font_class": "images", "unicode": "e650", "unicode_decimal": 58960 }, { "icon_id": "24855821", "name": "paperclip", "font_class": "paperclip", "unicode": "e652", "unicode_decimal": 58962 }, { "icon_id": "24855822", "name": "settings", "font_class": "settings", "unicode": "e653", "unicode_decimal": 58963 }, { "icon_id": "24855823", "name": "search", "font_class": "search", "unicode": "e654", "unicode_decimal": 58964 }, { "icon_id": "24855824", "name": "redo-filled", "font_class": "redo-filled", "unicode": "e655", "unicode_decimal": 58965 }, { "icon_id": "24841702", "name": "list", "font_class": "list", "unicode": "e644", "unicode_decimal": 58948 }, { "icon_id": "24841489", "name": "mail-open-filled", "font_class": "mail-open-filled", "unicode": "e63a", "unicode_decimal": 58938 }, { "icon_id": "24841491", "name": "hand-thumbsdown-filled", "font_class": "hand-down-filled", "unicode": "e63c", "unicode_decimal": 58940 }, { "icon_id": "24841492", "name": "hand-thumbsdown", "font_class": "hand-down", "unicode": "e63d", "unicode_decimal": 58941 }, { "icon_id": "24841493", "name": "hand-thumbsup-filled", "font_class": "hand-up-filled", "unicode": "e63e", "unicode_decimal": 58942 }, { "icon_id": "24841494", "name": "hand-thumbsup", "font_class": "hand-up", "unicode": "e63f", "unicode_decimal": 58943 }, { "icon_id": "24841496", "name": "heart-filled", "font_class": "heart-filled", "unicode": "e641", "unicode_decimal": 58945 }, { "icon_id": "24841498", "name": "mail-open", "font_class": "mail-open", "unicode": "e643", "unicode_decimal": 58947 }, { "icon_id": "24841488", "name": "heart", "font_class": "heart", "unicode": "e639", "unicode_decimal": 58937 }, { "icon_id": "24839963", "name": "loop", "font_class": "loop", "unicode": "e633", "unicode_decimal": 58931 }, { "icon_id": "24839866", "name": "pulldown", "font_class": "pulldown", "unicode": "e632", "unicode_decimal": 58930 }, { "icon_id": "24813798", "name": "scan", "font_class": "scan", "unicode": "e62a", "unicode_decimal": 58922 }, { "icon_id": "24813786", "name": "bars", "font_class": "bars", "unicode": "e627", "unicode_decimal": 58919 }, { "icon_id": "24813788", "name": "cart-filled", "font_class": "cart-filled", "unicode": "e629", "unicode_decimal": 58921 }, { "icon_id": "24813790", "name": "checkbox", "font_class": "checkbox", "unicode": "e62b", "unicode_decimal": 58923 }, { "icon_id": "24813791", "name": "checkbox-filled", "font_class": "checkbox-filled", "unicode": "e62c", "unicode_decimal": 58924 }, { "icon_id": "24813794", "name": "shop", "font_class": "shop", "unicode": "e62f", "unicode_decimal": 58927 }, { "icon_id": "24813795", "name": "headphones", "font_class": "headphones", "unicode": "e630", "unicode_decimal": 58928 }, { "icon_id": "24813796", "name": "cart", "font_class": "cart", "unicode": "e631", "unicode_decimal": 58929 } ] } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/components/uni-icons/uni-icons.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/components/uni-icons/uniicons.css ================================================ .uniui-color:before { content: "\e6cf"; } .uniui-wallet:before { content: "\e6b1"; } .uniui-settings-filled:before { content: "\e6ce"; } .uniui-auth-filled:before { content: "\e6cc"; } .uniui-shop-filled:before { content: "\e6cd"; } .uniui-staff-filled:before { content: "\e6cb"; } .uniui-vip-filled:before { content: "\e6c6"; } .uniui-plus-filled:before { content: "\e6c7"; } .uniui-folder-add-filled:before { content: "\e6c8"; } .uniui-color-filled:before { content: "\e6c9"; } .uniui-tune-filled:before { content: "\e6ca"; } .uniui-calendar-filled:before { content: "\e6c0"; } .uniui-notification-filled:before { content: "\e6c1"; } .uniui-wallet-filled:before { content: "\e6c2"; } .uniui-medal-filled:before { content: "\e6c3"; } .uniui-gift-filled:before { content: "\e6c4"; } .uniui-fire-filled:before { content: "\e6c5"; } .uniui-refreshempty:before { content: "\e6bf"; } .uniui-location-filled:before { content: "\e6af"; } .uniui-person-filled:before { content: "\e69d"; } .uniui-personadd-filled:before { content: "\e698"; } .uniui-back:before { content: "\e6b9"; } .uniui-forward:before { content: "\e6ba"; } .uniui-arrow-right:before { content: "\e6bb"; } .uniui-arrowthinright:before { content: "\e6bb"; } .uniui-arrow-left:before { content: "\e6bc"; } .uniui-arrowthinleft:before { content: "\e6bc"; } .uniui-arrow-up:before { content: "\e6bd"; } .uniui-arrowthinup:before { content: "\e6bd"; } .uniui-arrow-down:before { content: "\e6be"; } .uniui-arrowthindown:before { content: "\e6be"; } .uniui-bottom:before { content: "\e6b8"; } .uniui-arrowdown:before { content: "\e6b8"; } .uniui-right:before { content: "\e6b5"; } .uniui-arrowright:before { content: "\e6b5"; } .uniui-top:before { content: "\e6b6"; } .uniui-arrowup:before { content: "\e6b6"; } .uniui-left:before { content: "\e6b7"; } .uniui-arrowleft:before { content: "\e6b7"; } .uniui-eye:before { content: "\e651"; } .uniui-eye-filled:before { content: "\e66a"; } .uniui-eye-slash:before { content: "\e6b3"; } .uniui-eye-slash-filled:before { content: "\e6b4"; } .uniui-info-filled:before { content: "\e649"; } .uniui-reload:before { content: "\e6b2"; } .uniui-micoff-filled:before { content: "\e6b0"; } .uniui-map-pin-ellipse:before { content: "\e6ac"; } .uniui-map-pin:before { content: "\e6ad"; } .uniui-location:before { content: "\e6ae"; } .uniui-starhalf:before { content: "\e683"; } .uniui-star:before { content: "\e688"; } .uniui-star-filled:before { content: "\e68f"; } .uniui-calendar:before { content: "\e6a0"; } .uniui-fire:before { content: "\e6a1"; } .uniui-medal:before { content: "\e6a2"; } .uniui-font:before { content: "\e6a3"; } .uniui-gift:before { content: "\e6a4"; } .uniui-link:before { content: "\e6a5"; } .uniui-notification:before { content: "\e6a6"; } .uniui-staff:before { content: "\e6a7"; } .uniui-vip:before { content: "\e6a8"; } .uniui-folder-add:before { content: "\e6a9"; } .uniui-tune:before { content: "\e6aa"; } .uniui-auth:before { content: "\e6ab"; } .uniui-person:before { content: "\e699"; } .uniui-email-filled:before { content: "\e69a"; } .uniui-phone-filled:before { content: "\e69b"; } .uniui-phone:before { content: "\e69c"; } .uniui-email:before { content: "\e69e"; } .uniui-personadd:before { content: "\e69f"; } .uniui-chatboxes-filled:before { content: "\e692"; } .uniui-contact:before { content: "\e693"; } .uniui-chatbubble-filled:before { content: "\e694"; } .uniui-contact-filled:before { content: "\e695"; } .uniui-chatboxes:before { content: "\e696"; } .uniui-chatbubble:before { content: "\e697"; } .uniui-upload-filled:before { content: "\e68e"; } .uniui-upload:before { content: "\e690"; } .uniui-weixin:before { content: "\e691"; } .uniui-compose:before { content: "\e67f"; } .uniui-qq:before { content: "\e680"; } .uniui-download-filled:before { content: "\e681"; } .uniui-pyq:before { content: "\e682"; } .uniui-sound:before { content: "\e684"; } .uniui-trash-filled:before { content: "\e685"; } .uniui-sound-filled:before { content: "\e686"; } .uniui-trash:before { content: "\e687"; } .uniui-videocam-filled:before { content: "\e689"; } .uniui-spinner-cycle:before { content: "\e68a"; } .uniui-weibo:before { content: "\e68b"; } .uniui-videocam:before { content: "\e68c"; } .uniui-download:before { content: "\e68d"; } .uniui-help:before { content: "\e679"; } .uniui-navigate-filled:before { content: "\e67a"; } .uniui-plusempty:before { content: "\e67b"; } .uniui-smallcircle:before { content: "\e67c"; } .uniui-minus-filled:before { content: "\e67d"; } .uniui-micoff:before { content: "\e67e"; } .uniui-closeempty:before { content: "\e66c"; } .uniui-clear:before { content: "\e66d"; } .uniui-navigate:before { content: "\e66e"; } .uniui-minus:before { content: "\e66f"; } .uniui-image:before { content: "\e670"; } .uniui-mic:before { content: "\e671"; } .uniui-paperplane:before { content: "\e672"; } .uniui-close:before { content: "\e673"; } .uniui-help-filled:before { content: "\e674"; } .uniui-paperplane-filled:before { content: "\e675"; } .uniui-plus:before { content: "\e676"; } .uniui-mic-filled:before { content: "\e677"; } .uniui-image-filled:before { content: "\e678"; } .uniui-locked-filled:before { content: "\e668"; } .uniui-info:before { content: "\e669"; } .uniui-locked:before { content: "\e66b"; } .uniui-camera-filled:before { content: "\e658"; } .uniui-chat-filled:before { content: "\e659"; } .uniui-camera:before { content: "\e65a"; } .uniui-circle:before { content: "\e65b"; } .uniui-checkmarkempty:before { content: "\e65c"; } .uniui-chat:before { content: "\e65d"; } .uniui-circle-filled:before { content: "\e65e"; } .uniui-flag:before { content: "\e65f"; } .uniui-flag-filled:before { content: "\e660"; } .uniui-gear-filled:before { content: "\e661"; } .uniui-home:before { content: "\e662"; } .uniui-home-filled:before { content: "\e663"; } .uniui-gear:before { content: "\e664"; } .uniui-smallcircle-filled:before { content: "\e665"; } .uniui-map-filled:before { content: "\e666"; } .uniui-map:before { content: "\e667"; } .uniui-refresh-filled:before { content: "\e656"; } .uniui-refresh:before { content: "\e657"; } .uniui-cloud-upload:before { content: "\e645"; } .uniui-cloud-download-filled:before { content: "\e646"; } .uniui-cloud-download:before { content: "\e647"; } .uniui-cloud-upload-filled:before { content: "\e648"; } .uniui-redo:before { content: "\e64a"; } .uniui-images-filled:before { content: "\e64b"; } .uniui-undo-filled:before { content: "\e64c"; } .uniui-more:before { content: "\e64d"; } .uniui-more-filled:before { content: "\e64e"; } .uniui-undo:before { content: "\e64f"; } .uniui-images:before { content: "\e650"; } .uniui-paperclip:before { content: "\e652"; } .uniui-settings:before { content: "\e653"; } .uniui-search:before { content: "\e654"; } .uniui-redo-filled:before { content: "\e655"; } .uniui-list:before { content: "\e644"; } .uniui-mail-open-filled:before { content: "\e63a"; } .uniui-hand-down-filled:before { content: "\e63c"; } .uniui-hand-down:before { content: "\e63d"; } .uniui-hand-up-filled:before { content: "\e63e"; } .uniui-hand-up:before { content: "\e63f"; } .uniui-heart-filled:before { content: "\e641"; } .uniui-mail-open:before { content: "\e643"; } .uniui-heart:before { content: "\e639"; } .uniui-loop:before { content: "\e633"; } .uniui-pulldown:before { content: "\e632"; } .uniui-scan:before { content: "\e62a"; } .uniui-bars:before { content: "\e627"; } .uniui-cart-filled:before { content: "\e629"; } .uniui-checkbox:before { content: "\e62b"; } .uniui-checkbox-filled:before { content: "\e62c"; } .uniui-shop:before { content: "\e62f"; } .uniui-headphones:before { content: "\e630"; } .uniui-cart:before { content: "\e631"; } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/package.json ================================================ { "id": "uni-icons", "displayName": "uni-icons 图标", "version": "1.3.5", "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。", "keywords": [ "uni-ui", "uniui", "icon", "图标" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "^3.2.14" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-icons/readme.md ================================================ ## Icons 图标 > **组件名:uni-icons** > 代码块: `uIcons` 用于展示 icons 图标 。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-indexed-list/changelog.md ================================================ ## 1.2.1(2021-11-22) - 修复 vue3中某些scss变量无法找到的问题 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.11(2021-05-12) - 新增 组件示例地址 ## 1.0.10(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.9(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.8(2021-02-05) - 调整为uni_modules目录规范 - 新增 支持 PC 端 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-indexed-list/package.json ================================================ { "id": "uni-indexed-list", "displayName": "uni-indexed-list 索引列表", "version": "1.2.1", "description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景", "keywords": [ "uni-ui", "索引列表", "索引", "列表" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-indexed-list/readme.md ================================================ ## IndexedList 索引列表 > **组件名:uni-indexed-list** > 代码块: `uIndexedList` 用于展示索引列表。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-link/changelog.md ================================================ ## 1.0.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-link](https://uniapp.dcloud.io/component/uniui/uni-link) ## 1.1.7(2021-11-08) ## 0.0.7(2021-09-03) - 修复 在 nvue 下不显示的 bug ## 0.0.6(2021-07-30) - 新增 支持自定义插槽 ## 0.0.5(2021-06-21) - 新增 download 属性,H5平台下载文件名 ## 0.0.4(2021-05-12) - 新增 组件示例地址 ## 0.0.3(2021-03-09) - 新增 href 属性支持 tel:|mailto: ## 0.0.2(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-link/components/uni-link/uni-link.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-link/package.json ================================================ { "id": "uni-link", "displayName": "uni-link 超链接", "version": "1.0.0", "description": "uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打", "keywords": [ "uni-ui", "uniui", "link", "超链接", "" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "y", "联盟": "y" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-link/readme.md ================================================ ## Link 链接 > **组件名:uni-link** > 代码块: `uLink` uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-link) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/changelog.md ================================================ ## 1.2.14(2023-04-14) - 优化 uni-list-chat 具名插槽`header` 非app端套一层元素,方便使用时通过外层元素定位实现样式修改 ## 1.2.13(2023-03-03) - uni-list-chat 新增 支持具名插槽`header` ## 1.2.12(2023-02-01) - 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props) ## 1.2.11(2023-01-31) - 修复 无反馈效果呈现的bug ## 1.2.9(2022-11-22) - 修复 uni-list-chat 在vue3下跳转报错的bug ## 1.2.8(2022-11-21) - 修复 uni-list-chat avatar属性 值为本地路径时错误的问题 ## 1.2.7(2022-11-21) - 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题 ## 1.2.6(2022-11-18) - 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题 ## 1.2.5(2022-11-15) - 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug ## 1.2.4(2022-11-15) - 修复 uni-list-item 的 customStyle 属性 padding值在nvue(vue2)下无效的bug ## 1.2.3(2022-11-14) - uni-list-chat 新增 avatar 支持 fileId ## 1.2.2(2022-11-11) - uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html) - uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im:[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im) - uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor ## 1.2.1(2022-03-30) - 删除无用文件 ## 1.2.0(2021-11-23) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-list](https://uniapp.dcloud.io/component/uniui/uni-list) ## 1.1.3(2021-08-30) - 修复 在vue3中to属性在发行应用的时候报错的bug ## 1.1.2(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.1.1(2021-07-21) - 修复 与其他组件嵌套使用时,点击失效的Bug ## 1.1.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.17(2021-05-12) - 新增 组件示例地址 ## 1.0.16(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.15(2021-02-05) - 调整为uni_modules目录规范 - 修复 uni-list-chat 角标显示不正常的问题 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list/uni-list.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list/uni-refresh.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list/uni-refresh.wxs ================================================ var pullDown = { threshold: 95, maxHeight: 200, callRefresh: 'onrefresh', callPullingDown: 'onpullingdown', refreshSelector: '.uni-refresh' }; function ready(newValue, oldValue, ownerInstance, instance) { var state = instance.getState() state.canPullDown = newValue; // console.log(newValue); } function touchStart(e, instance) { var state = instance.getState(); state.refreshInstance = instance.selectComponent(pullDown.refreshSelector); state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined); if (!state.canPullDown) { return } // console.log("touchStart"); state.height = 0; state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY; state.refreshInstance.setStyle({ 'height': 0 }); state.refreshInstance.callMethod("onchange", true); } function touchMove(e, ownerInstance) { var instance = e.instance; var state = instance.getState(); if (!state.canPullDown) { return } var oldHeight = state.height; var endY = e.touches[0].pageY || e.changedTouches[0].pageY; var height = endY - state.touchStartY; if (height > pullDown.maxHeight) { return; } var refreshInstance = state.refreshInstance; refreshInstance.setStyle({ 'height': height + 'px' }); height = height < pullDown.maxHeight ? height : pullDown.maxHeight; state.height = height; refreshInstance.callMethod(pullDown.callPullingDown, { height: height }); } function touchEnd(e, ownerInstance) { var state = e.instance.getState(); if (!state.canPullDown) { return } state.refreshInstance.callMethod("onchange", false); var refreshInstance = state.refreshInstance; if (state.height > pullDown.threshold) { refreshInstance.callMethod(pullDown.callRefresh); return; } refreshInstance.setStyle({ 'height': 0 }); } function propObserver(newValue, oldValue, instance) { pullDown = newValue; } module.exports = { touchmove: touchMove, touchstart: touchStart, touchend: touchEnd, propObserver: propObserver } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss ================================================ /** * 这里是 uni-list 组件内置的常用样式变量 * 如果需要覆盖样式,这里提供了基本的组件样式变量,您可以尝试修改这里的变量,去完成样式替换,而不用去修改源码 * */ // 背景色 $background-color : #fff; // 分割线颜色 $divide-line-color : #e5e5e5; // 默认头像大小,如需要修改此值,注意同步修改 js 中的值 const avatarWidth = xx ,目前只支持方形头像 // nvue 页面不支持修改头像大小 $avatar-width : 45px ; // 头像边框 $avatar-border-radius: 5px; $avatar-border-color: #eee; $avatar-border-width: 1px; // 标题文字样式 $title-size : 16px; $title-color : #3b4144; $title-weight : normal; // 描述文字样式 $note-size : 12px; $note-color : #999; $note-weight : normal; // 右侧额外内容默认样式 $right-text-size : 12px; $right-text-color : #999; $right-text-weight : normal; // 角标样式 // nvue 页面不支持修改圆点位置以及大小 // 角标在左侧时,角标的位置,默认为 0 ,负数左/下移动,正数右/上移动 $badge-left: 0px; $badge-top: 0px; // 显示圆点时,圆点大小 $dot-width: 10px; $dot-height: 10px; // 显示角标时,角标大小和字体大小 $badge-size : 18px; $badge-font : 12px; // 显示角标时,角标前景色 $badge-color : #fff; // 显示角标时,角标背景色 $badge-background-color : #ff5a5f; // 显示角标时,角标左右间距 $badge-space : 6px; // 状态样式 // 选中颜色 $hover : #f5f5f5; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/package.json ================================================ { "id": "uni-list", "displayName": "uni-list 列表", "version": "1.2.14", "description": "List 组件 ,帮助使用者快速构建列表。", "keywords": [ "", "uni-ui", "uniui", "列表", "", "list" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-badge", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-list/readme.md ================================================ ## List 列表 > **组件名:uni-list** > 代码块: `uList`、`uListItem` > 关联组件:`uni-list-item`、`uni-badge`、`uni-icons`、`uni-list-chat`、`uni-list-ad` List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。 在vue页面里,它默认使用页面级滚动。在app-nvue页面里,它默认使用原生list组件滚动。这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。 uni-list组件是父容器,里面的核心是uni-list-item子组件,它代表列表中的一个可重复行,子组件可以无限循环。 uni-list-item有很多风格,uni-list-item组件通过内置的属性,满足一些常用的场景。当内置属性不满足需求时,可以通过扩展插槽来自定义列表内容。 内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表、通信录列表、聊天记录列表。 涉及很多大图或丰富内容的列表,比如类今日头条的新闻列表、类淘宝的电商列表,需要通过扩展插槽实现。 下文均有样例给出。 uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29) ### 安装方式 本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) > **注意事项** > 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 > - 组件需要依赖 `sass` 插件 ,请自行手动安装 > - 组件内部依赖 `'uni-icons'` 、`uni-badge` 组件 > - `uni-list` 和 `uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item` > - 只有开启点击反馈后,会有点击选中效果 > - 使用插槽时,可以完全自定义内容 > - note 、rightText 属性暂时没做限制,不支持文字溢出隐藏,使用时应该控制长度显示或通过默认插槽自行扩展 > - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: 详情 --> 项目配置 --> 启用 component2 编译 > - 如果需要修改 `switch`、`badge` 样式,请使用插槽自定义 > - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli` > - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ### 基本用法 - 设置 `title` 属性,可以显示列表标题 - 设置 `disabled` 属性,可以禁用当前项 ```html ``` ### 多行内容显示 - 设置 `note` 属性 ,可以在第二行显示描述文本信息 ```html ``` ### 右侧显示角标、switch - 设置 `show-badge` 属性 ,可以显示角标内容 - 设置 `show-switch` 属性,可以显示 switch 开关 ```html ``` ### 左侧显示略缩图、图标 - 设置 `thumb` 属性 ,可以在列表左侧显示略缩图 - 设置 `show-extra-icon` 属性,并指定 `extra-icon` 可以在左侧显示图标 ```html ``` ### 开启点击反馈和右侧箭头 - 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 - 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头 - 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` ```html ``` ### 聊天列表示例 - 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 - 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` - 设置 `to` 属性,可以跳转页面 - `time` 属性,通常会设置成时间显示,但是这个属性不仅仅可以设置时间,你可以传入任何文本,注意文本长度可能会影响显示 - `avatar` 和 `avatarList` 属性同时只会有一个生效,同时设置的话,`avatarList` 属性的长度大于1 ,`avatar` 属性将失效 - 可以通过默认插槽自定义列表右侧内容 ```html 刚刚 ``` ```javascript export default { components: {}, data() { return { avatarList: [{ url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' }, { url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' }, { url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' }] } } } ``` ```css .chat-custom-right { flex: 1; /* #ifndef APP-NVUE */ display: flex; /* #endif */ flex-direction: column; justify-content: space-between; align-items: flex-end; } .chat-custom-text { font-size: 12px; color: #999; } ``` ## API ### List Props 属性名 |类型 |默认值 | 说明 :-: |:-: |:-: | :-: border |Boolean |true | 是否显示边框 ### ListItem Props 属性名 |类型 |默认值 | 说明 :-: |:-: |:-: | :-: title |String |- | 标题 note |String |- | 描述 ellipsis |Number |0 | title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行;【nvue 暂不支持】 thumb |String |- | 左侧缩略图,若thumb有值,则不会显示扩展图标 thumbSize |String |medium | 略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图; showBadge |Boolean |false | 是否显示数字角标 badgeText |String |- | 数字角标内容 badgeType |String |- | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) badgeStyle |Object |- | 数字角标样式,使用uni-badge的custom-style参数 rightText |String |- | 右侧文字内容 disabled |Boolean |false | 是否禁用 showArrow |Boolean |true | 是否显示箭头图标 link |String |navigateTo | 新页面跳转方式,可选值见下表 to |String |- | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功 clickable |Boolean |false | 是否开启点击反馈 showSwitch |Boolean |false | 是否显示Switch switchChecked |Boolean |false | Switch是否被选中 showExtraIcon |Boolean |false | 左侧是否显示扩展图标 extraIcon |Object |- | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28) direction | String |row | 排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制 #### Link Options 属性名 | 说明 :-: | :-: navigateTo | 同 uni.navigateTo() redirectTo | 同 uni.reLaunch() reLaunch | 同 uni.reLaunch() switchTab | 同 uni.switchTab() ### ListItem Events 事件称名 |说明 |返回参数 :-: |:-: |:-: click |点击 uniListItem 触发事件,需开启点击反馈 |- switchChange |点击切换 Switch 时触发,需显示 switch |e={value:checked} ### ListItem Slots 名称 | 说明 :-: | :-: header | 左/上内容插槽,可完全自定义默认显示 body | 中间内容插槽,可完全自定义中间内容 footer | 右/下内容插槽,可完全自定义右侧内容 > **通过插槽扩展** > 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 > 如果 `uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 > uni-list-item提供了3个可扩展的插槽:`header`、`body`、`footer` > - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分 > - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分 > 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。 **示例** ```html 自定义插槽 ``` ### ListItemChat Props 属性名 |类型 |默认值 | 说明 :-: |:-: |:-: | :-: title |String |- | 标题 note |String |- | 描述 clickable |Boolean |false | 是否开启点击反馈 badgeText |String |- | 数字角标内容,设置为 `dot` 将显示圆点 badgePositon |String |right | 角标位置 link |String |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表 clickable |Boolean |false | 是否开启点击反馈 to |String |- | 跳转页面地址,如填写此属性,click 会返回页面是否跳转成功 time |String |- | 右侧时间显示 avatarCircle |Boolean |false | 是否显示圆形头像 avatar |String |- | 头像地址,avatarCircle 不填时生效 avatarList |Array |- | 头像组,格式为 [{url:''}] #### Link Options 属性名 | 说明 :-: | :-: navigateTo | 同 uni.navigateTo() redirectTo | 同 uni.reLaunch() reLaunch | 同 uni.reLaunch() switchTab | 同 uni.switchTab() ### ListItemChat Slots 名称 | 说明 :- | :- default | 自定义列表右侧内容(包括时间和角标显示) ### ListItemChat Events 事件称名 | 说明 | 返回参数 :-: | :-: | :-: @click | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息 ## 基于uni-list扩展的页面模板 通过扩展插槽,可实现多种常见样式的列表 **新闻列表类** 1. 云端一体混合布局:[https://ext.dcloud.net.cn/plugin?id=2546](https://ext.dcloud.net.cn/plugin?id=2546) 2. 云端一体垂直布局,大图模式:[https://ext.dcloud.net.cn/plugin?id=2583](https://ext.dcloud.net.cn/plugin?id=2583) 3. 云端一体垂直布局,多行图文混排:[https://ext.dcloud.net.cn/plugin?id=2584](https://ext.dcloud.net.cn/plugin?id=2584) 4. 云端一体垂直布局,多图模式:[https://ext.dcloud.net.cn/plugin?id=2585](https://ext.dcloud.net.cn/plugin?id=2585) 5. 云端一体水平布局,左图右文:[https://ext.dcloud.net.cn/plugin?id=2586](https://ext.dcloud.net.cn/plugin?id=2586) 6. 云端一体水平布局,左文右图:[https://ext.dcloud.net.cn/plugin?id=2587](https://ext.dcloud.net.cn/plugin?id=2587) 7. 云端一体垂直布局,无图模式,主标题+副标题:[https://ext.dcloud.net.cn/plugin?id=2588](https://ext.dcloud.net.cn/plugin?id=2588) **商品列表类** 1. 云端一体列表/宫格视图互切:[https://ext.dcloud.net.cn/plugin?id=2651](https://ext.dcloud.net.cn/plugin?id=2651) 2. 云端一体列表(宫格模式):[https://ext.dcloud.net.cn/plugin?id=2671](https://ext.dcloud.net.cn/plugin?id=2671) 3. 云端一体列表(列表模式):[https://ext.dcloud.net.cn/plugin?id=2672](https://ext.dcloud.net.cn/plugin?id=2672) ## 组件示例 点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list) ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/changelog.md ================================================ ## 1.3.3(2022-01-20) - 新增 showText属性 ,是否显示文本 ## 1.3.2(2022-01-19) - 修复 nvue 平台下不显示文本的bug ## 1.3.1(2022-01-19) - 修复 微信小程序平台样式选择器报警告的问题 ## 1.3.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more) ## 1.2.1(2021-08-24) - 新增 支持国际化 ## 1.2.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.1.8(2021-05-12) - 新增 组件示例地址 ## 1.1.7(2021-03-30) - 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug ## 1.1.6(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json ================================================ { "uni-load-more.contentdown": "Pull up to show more", "uni-load-more.contentrefresh": "loading...", "uni-load-more.contentnomore": "No more data" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js ================================================ import en from './en.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json ================================================ { "uni-load-more.contentdown": "上拉显示更多", "uni-load-more.contentrefresh": "正在加载...", "uni-load-more.contentnomore": "没有更多数据了" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json ================================================ { "uni-load-more.contentdown": "上拉顯示更多", "uni-load-more.contentrefresh": "正在加載...", "uni-load-more.contentnomore": "沒有更多數據了" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/package.json ================================================ { "id": "uni-load-more", "displayName": "uni-load-more 加载更多", "version": "1.3.3", "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。", "keywords": [ "uni-ui", "uniui", "加载更多", "load-more" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-load-more/readme.md ================================================ ### LoadMore 加载更多 > **组件名:uni-load-more** > 代码块: `uLoadMore` 用于列表中,做滚动加载使用,展示 loading 的各种状态。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-nav-bar/changelog.md ================================================ ## 1.3.11(2023-03-29) - 修复 自定义状态栏高度闪动BUG ## 1.3.10(2023-03-29) - 修复 暗黑模式下边线颜色错误的bug ## 1.3.9(2022-10-13) - 修复 条件编译错误的bug ## 1.3.8(2022-10-12) - 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug ## 1.3.7(2022-08-11) - 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug ## 1.3.6(2022-06-30) - 修复 组件示例中插槽用法无法显示内容的bug ## 1.3.5(2022-05-24) - 新增 stat 属性 ,可开启统计title 上报 ,仅使用了title 属性且项目开启了uni统计生效 ## 1.3.4(2022-01-24) - 更新 组件示例 ## 1.3.3(2022-01-24) - 新增 left-width/right-width属性 ,可修改左右两侧的宽度 ## 1.3.2(2022-01-18) - 修复 在vue下,标题不垂直居中的bug ## 1.3.1(2022-01-18) - 修复 height 属性类型错误 ## 1.3.0(2022-01-18) - 新增 height 属性,可修改组件高度 - 新增 dark 属性可可开启暗黑模式 - 优化 标题字数过多显示省略号 - 优化 插槽,插入内容可完全覆盖 ## 1.2.1(2022-01-10) - 修复 color 属性不生效的bug ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-nav-bar](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.11(2021-05-12) - 新增 组件示例地址 ## 1.0.10(2021-04-30) - 修复 在nvue下fixed为true,宽度不能撑满的Bug ## 1.0.9(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.8(2021-04-14) - uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug ## 1.0.7(2021-02-25) - 修复 easycom 下,找不到 uni-status-bar 的bug ## 1.0.6(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.5(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-nav-bar/package.json ================================================ { "id": "uni-nav-bar", "displayName": "uni-nav-bar 自定义导航栏", "version": "1.3.11", "description": "自定义导航栏组件,主要用于头部导航。", "keywords": [ "uni-ui", "导航", "导航栏", "自定义导航栏" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-nav-bar/readme.md ================================================ ## NavBar 导航栏 > **组件名:uni-nav-bar** > 代码块: `uNavBar` 导航栏组件,主要用于头部导航。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-notice-bar/changelog.md ================================================ ## 1.2.1(2022-09-05) - 新增 属性 fontSize,可修改文字大小。 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-notice-bar](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) ## 1.1.1(2021-11-09) - 新增 提供组件设计资源,组件样式调整 ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.9(2021-05-12) - 新增 组件示例地址 ## 1.0.8(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.7(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.6(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-notice-bar/package.json ================================================ { "id": "uni-notice-bar", "displayName": "uni-notice-bar 通告栏", "version": "1.2.1", "description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告", "keywords": [ "uni-ui", "uniui", "通告栏", "公告", "跑马灯" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-notice-bar/readme.md ================================================ ## NoticeBar 通告栏 > **组件名:uni-notice-bar** > 代码块: `uNoticeBar` 通告栏组件 。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-number-box/changelog.md ================================================ ## 1.2.3(2023-05-23) 更新示例工程 ## 1.2.2(2023-05-08) - 修复 change 事件执行顺序错误的问题 ## 1.2.1(2021-11-22) - 修复 vue3中某些scss变量无法找到的问题 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-number-box](https://uniapp.dcloud.io/component/uniui/uni-number-box) ## 1.1.2(2021-11-09) - 新增 提供组件设计资源,组件样式调整 ## 1.1.1(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.1.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-05-12) - 新增 组件示例地址 ## 1.0.6(2021-04-20) - 修复 uni-number-box 浮点数运算不精确的 bug - 修复 uni-number-box change 事件触发不正确的 bug - 新增 uni-number-box v-model 双向绑定 ## 1.0.5(2021-02-05) - 调整为uni_modules目录规范 ## 1.0.7(2021-02-05) - 调整为uni_modules目录规范 - 新增 支持 v-model - 新增 支持 focus、blur 事件 - 新增 支持 PC 端 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-number-box/package.json ================================================ { "id": "uni-number-box", "displayName": "uni-number-box 数字输入框", "version": "1.2.3", "description": "NumberBox 带加减按钮的数字输入框组件,用户可以控制每次点击增加的数值,支持小数。", "keywords": [ "uni-ui", "uniui", "数字输入框" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-number-box/readme.md ================================================ ## NumberBox 数字输入框 > **组件名:uni-number-box** > 代码块: `uNumberBox` 带加减按钮的数字输入框。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-number-box) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/changelog.md ================================================ ## 1.2.4(2022-09-19) - 修复,未对主题色设置默认色,导致未引入 uni-scss 变量文件报错。 - 修复,未对移动端当前页文字做主题色适配。 ## 1.2.3(2022-09-15) - 修复未使用 uni-scss 主题色的 bug。 ## 1.2.2(2022-07-06) - 修复 es 语言 i18n 错误 ## 1.2.1(2021-11-22) - 修复 vue3中某些scss变量无法找到的问题 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-pagination](https://uniapp.dcloud.io/component/uniui/uni-pagination) ## 1.1.2(2021-10-08) - 修复 current 、value 属性未监听,导致高亮样式失效的 bug ## 1.1.1(2021-08-20) - 新增 支持国际化 ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-05-12) - 新增 组件示例地址 ## 1.0.6(2021-04-12) - 新增 PC 和 移动端适配不同的 ui ## 1.0.5(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json ================================================ { "uni-pagination.prevText": "prev", "uni-pagination.nextText": "next", "uni-pagination.piecePerPage": "piece/page" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json ================================================ { "uni-pagination.prevText": "anterior", "uni-pagination.nextText": "prxima", "uni-pagination.piecePerPage": "Artculo/Pgina" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json ================================================ { "uni-pagination.prevText": "précédente", "uni-pagination.nextText": "suivante", "uni-pagination.piecePerPage": "Articles/Pages" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js ================================================ import en from './en.json' import es from './es.json' import fr from './fr.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, es, fr, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json ================================================ { "uni-pagination.prevText": "上一页", "uni-pagination.nextText": "下一页", "uni-pagination.piecePerPage": "条/页" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json ================================================ { "uni-pagination.prevText": "上一頁", "uni-pagination.nextText": "下一頁", "uni-pagination.piecePerPage": "條/頁" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/package.json ================================================ { "id": "uni-pagination", "displayName": "uni-pagination 分页器", "version": "1.2.4", "description": "Pagination 分页器组件,用于展示页码、请求数据等。", "keywords": [ "uni-ui", "uniui", "分页器", "页码" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss","uni-icons"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-pagination/readme.md ================================================ ## Pagination 分页器 > **组件名:uni-pagination** > 代码块: `uPagination` 分页器组件,用于展示页码、请求数据等。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-pagination) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/changelog.md ================================================ ## 1.8.3(2023-04-17) - 修复 uni-popup 重复打开时的 bug ## 1.8.2(2023-02-02) - uni-popup-dialog 组件新增 inputType 属性 ## 1.8.1(2022-12-01) - 修复 nvue 下 v-show 报错 ## 1.8.0(2022-11-29) - 优化 主题样式 ## 1.7.9(2022-04-02) - 修复 弹出层内部无法滚动的bug ## 1.7.8(2022-03-28) - 修复 小程序中高度错误的bug ## 1.7.7(2022-03-17) - 修复 快速调用open出现问题的Bug ## 1.7.6(2022-02-14) - 修复 safeArea 属性不能设置为false的bug ## 1.7.5(2022-01-19) - 修复 isMaskClick 失效的bug ## 1.7.4(2022-01-19) - 新增 cancelText \ confirmText 属性 ,可自定义文本 - 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 - 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 ## 1.7.3(2022-01-13) - 修复 设置 safeArea 属性不生效的bug ## 1.7.2(2021-11-26) - 优化 组件示例 ## 1.7.1(2021-11-26) - 修复 vuedoc 文字错误 ## 1.7.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup) ## 1.6.2(2021-08-24) - 新增 支持国际化 ## 1.6.1(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.6.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.5.0(2021-06-23) - 新增 mask-click 遮罩层点击事件 ## 1.4.5(2021-06-22) - 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug ## 1.4.4(2021-06-18) - 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug ## 1.4.3(2021-06-08) - 修复 错误的 watch 字段 - 修复 safeArea 属性不生效的问题 - 修复 点击内容,再点击遮罩无法关闭的Bug ## 1.4.2(2021-05-12) - 新增 组件示例地址 ## 1.4.1(2021-04-29) - 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 ## 1.4.0 (2021-04-29) - 新增 type 属性的 left\right 值,支持左右弹出 - 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 - 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 - 新增 safeArea 属性,是否适配底部安全区 - 修复 App\h5\微信小程序底部安全区占位不对的Bug - 修复 App 端弹出等待的Bug - 优化 提升低配设备性能,优化动画卡顿问题 - 优化 更简单的组件自定义方式 ## 1.2.9(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.2.8(2021-02-05) - 调整为uni_modules目录规范 ## 1.2.7(2021-02-05) - 调整为uni_modules目录规范 - 新增 支持 PC 端 - 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/i18n/en.json ================================================ { "uni-popup.cancel": "cancel", "uni-popup.ok": "ok", "uni-popup.placeholder": "pleace enter", "uni-popup.title": "Hint", "uni-popup.shareTitle": "Share to" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/i18n/index.js ================================================ import en from './en.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json ================================================ { "uni-popup.cancel": "取消", "uni-popup.ok": "确定", "uni-popup.placeholder": "请输入", "uni-popup.title": "提示", "uni-popup.shareTitle": "分享到" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json ================================================ { "uni-popup.cancel": "取消", "uni-popup.ok": "確定", "uni-popup.placeholder": "請輸入", "uni-popup.title": "提示", "uni-popup.shareTitle": "分享到" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/keypress.js ================================================ // #ifdef H5 export default { name: 'Keypress', props: { disable: { type: Boolean, default: false } }, mounted () { const keyNames = { esc: ['Esc', 'Escape'], tab: 'Tab', enter: 'Enter', space: [' ', 'Spacebar'], up: ['Up', 'ArrowUp'], left: ['Left', 'ArrowLeft'], right: ['Right', 'ArrowRight'], down: ['Down', 'ArrowDown'], delete: ['Backspace', 'Delete', 'Del'] } const listener = ($event) => { if (this.disable) { return } const keyName = Object.keys(keyNames).find(key => { const keyName = $event.key const value = keyNames[key] return value === keyName || (Array.isArray(value) && value.includes(keyName)) }) if (keyName) { // 避免和其他按键事件冲突 setTimeout(() => { this.$emit(keyName, {}) }, 0) } } document.addEventListener('keyup', listener) // this.$once('hook:beforeDestroy', () => { // document.removeEventListener('keyup', listener) // }) }, render: () => {} } // #endif ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/popup.js ================================================ export default { data() { return { } }, created(){ this.popup = this.getParent() }, methods:{ /** * 获取父元素实例 */ getParent(name = 'uniPopup') { let parent = this.$parent; let parentName = parent.$options.name; while (parentName !== name) { parent = parent.$parent; if (!parent) return false parentName = parent.$options.name; } return parent; }, } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup/uni-popup.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js ================================================ // #ifdef H5 export default { name: 'Keypress', props: { disable: { type: Boolean, default: false } }, mounted () { const keyNames = { esc: ['Esc', 'Escape'], tab: 'Tab', enter: 'Enter', space: [' ', 'Spacebar'], up: ['Up', 'ArrowUp'], left: ['Left', 'ArrowLeft'], right: ['Right', 'ArrowRight'], down: ['Down', 'ArrowDown'], delete: ['Backspace', 'Delete', 'Del'] } const listener = ($event) => { if (this.disable) { return } const keyName = Object.keys(keyNames).find(key => { const keyName = $event.key const value = keyNames[key] return value === keyName || (Array.isArray(value) && value.includes(keyName)) }) if (keyName) { // 避免和其他按键事件冲突 setTimeout(() => { this.$emit(keyName, {}) }, 0) } } document.addEventListener('keyup', listener) this.$once('hook:beforeDestroy', () => { document.removeEventListener('keyup', listener) }) }, render: () => {} } // #endif ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/package.json ================================================ { "id": "uni-popup", "displayName": "uni-popup 弹出层", "version": "1.8.3", "description": " Popup 组件,提供常用的弹层", "keywords": [ "uni-ui", "弹出层", "弹窗", "popup", "弹框" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-transition" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-popup/readme.md ================================================ ## Popup 弹出层 > **组件名:uni-popup** > 代码块: `uPopup` > 关联组件:`uni-transition` 弹出层组件,在应用中弹出一个消息提示窗口、提示框等 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-rate/changelog.md ================================================ ## 1.3.1(2022-02-25) - 修复 条件判断 `NaN` 错误的 bug ## 1.3.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-rate](https://uniapp.dcloud.io/component/uniui/uni-rate) ## 1.2.2(2021-09-10) - 优化 默认值修改为 0 颗星 ## 1.2.1(2021-07-30) - 优化 vue3下事件警告的问题 ## 1.2.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.1.2(2021-05-12) - 新增 组件示例地址 ## 1.1.1(2021-04-21) - 修复 布局变化后 uni-rate 星星计算不准确的 bug - 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 ## 1.1.0(2021-04-16) - 修复 uni-rate 属性 margin 值为 string 组件失效的 bug ## 1.0.9(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.8(2021-02-05) - 调整为uni_modules目录规范 - 支持 pc 端 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-rate/components/uni-rate/uni-rate.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-rate/package.json ================================================ { "id": "uni-rate", "displayName": "uni-rate 评分", "version": "1.3.1", "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。", "keywords": [ "uni-ui", "uniui", "评分" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-rate/readme.md ================================================ ## Rate 评分 > **组件名:uni-rate** > 代码块: `uRate` > 关联组件:`uni-icons` 评分组件,多用于购买商品后,对商品进行评价等场景 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-rate) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-row/changelog.md ================================================ ## 1.0.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-row](https://uniapp.dcloud.io/component/uniui/uni-row) ## 0.1.0(2021-07-13) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 0.0.4(2021-05-12) - 新增 组件示例地址 ## 0.0.3(2021-02-05) - 调整为uni_modules目录规范 - 新增uni-row组件 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-row/components/uni-col/uni-col.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-row/components/uni-row/uni-row.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-row/package.json ================================================ { "id": "uni-row", "displayName": "uni-row 布局-行", "version": "1.0.0", "description": "流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。", "keywords": [ "uni-ui", "uniui", "栅格", "布局", "layout" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "u" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-row/readme.md ================================================ ## Layout 布局 > **组件名 uni-row、uni-col** > 代码块: `uRow`、`uCol` 流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-row) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/changelog.md ================================================ ## 1.0.3(2022-01-21) - 优化 组件示例 ## 1.0.2(2021-11-22) - 修复 / 符号在 vue 不同版本兼容问题引起的报错问题 ## 1.0.1(2021-11-22) - 修复 vue3中scss语法兼容问题 ## 1.0.0(2021-11-18) - init ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/index.scss ================================================ @import './styles/index.scss'; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/package.json ================================================ { "id": "uni-scss", "displayName": "uni-scss 辅助样式", "version": "1.0.3", "description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。", "keywords": [ "uni-scss", "uni-ui", "辅助样式" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "^3.1.0" }, "dcloudext": { "category": [ "JS SDK", "通用 SDK" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "u" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "n", "联盟": "n" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/readme.md ================================================ `uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/index.scss ================================================ @import './setting/_variables.scss'; @import './setting/_border.scss'; @import './setting/_color.scss'; @import './setting/_space.scss'; @import './setting/_radius.scss'; @import './setting/_text.scss'; @import './setting/_styles.scss'; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_border.scss ================================================ .uni-border { border: 1px $uni-border-1 solid; } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_color.scss ================================================ // TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐 // @mixin get-styles($k,$c) { // @if $k == size or $k == weight{ // font-#{$k}:#{$c} // }@else{ // #{$k}:#{$c} // } // } $uni-ui-color:( // 主色 primary: $uni-primary, primary-disable: $uni-primary-disable, primary-light: $uni-primary-light, // 辅助色 success: $uni-success, success-disable: $uni-success-disable, success-light: $uni-success-light, warning: $uni-warning, warning-disable: $uni-warning-disable, warning-light: $uni-warning-light, error: $uni-error, error-disable: $uni-error-disable, error-light: $uni-error-light, info: $uni-info, info-disable: $uni-info-disable, info-light: $uni-info-light, // 中性色 main-color: $uni-main-color, base-color: $uni-base-color, secondary-color: $uni-secondary-color, extra-color: $uni-extra-color, // 背景色 bg-color: $uni-bg-color, // 边框颜色 border-1: $uni-border-1, border-2: $uni-border-2, border-3: $uni-border-3, border-4: $uni-border-4, // 黑色 black:$uni-black, // 白色 white:$uni-white, // 透明 transparent:$uni-transparent ) !default; @each $key, $child in $uni-ui-color { .uni-#{"" + $key} { color: $child; } .uni-#{"" + $key}-bg { background-color: $child; } } .uni-shadow-sm { box-shadow: $uni-shadow-sm; } .uni-shadow-base { box-shadow: $uni-shadow-base; } .uni-shadow-lg { box-shadow: $uni-shadow-lg; } .uni-mask { background-color:$uni-mask; } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_radius.scss ================================================ @mixin radius($r,$d:null ,$important: false){ $radius-value:map-get($uni-radius, $r) if($important, !important, null); // Key exists within the $uni-radius variable @if (map-has-key($uni-radius, $r) and $d){ @if $d == t { border-top-left-radius:$radius-value; border-top-right-radius:$radius-value; }@else if $d == r { border-top-right-radius:$radius-value; border-bottom-right-radius:$radius-value; }@else if $d == b { border-bottom-left-radius:$radius-value; border-bottom-right-radius:$radius-value; }@else if $d == l { border-top-left-radius:$radius-value; border-bottom-left-radius:$radius-value; }@else if $d == tl { border-top-left-radius:$radius-value; }@else if $d == tr { border-top-right-radius:$radius-value; }@else if $d == br { border-bottom-right-radius:$radius-value; }@else if $d == bl { border-bottom-left-radius:$radius-value; } }@else{ border-radius:$radius-value; } } @each $key, $child in $uni-radius { @if($key){ .uni-radius-#{"" + $key} { @include radius($key) } }@else{ .uni-radius { @include radius($key) } } } @each $direction in t, r, b, l,tl, tr, br, bl { @each $key, $child in $uni-radius { @if($key){ .uni-radius-#{"" + $direction}-#{"" + $key} { @include radius($key,$direction,false) } }@else{ .uni-radius-#{$direction} { @include radius($key,$direction,false) } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_space.scss ================================================ @mixin fn($space,$direction,$size,$n) { @if $n { #{$space}-#{$direction}: #{$size*$uni-space-root}px } @else { #{$space}-#{$direction}: #{-$size*$uni-space-root}px } } @mixin get-styles($direction,$i,$space,$n){ @if $direction == t { @include fn($space, top,$i,$n); } @if $direction == r { @include fn($space, right,$i,$n); } @if $direction == b { @include fn($space, bottom,$i,$n); } @if $direction == l { @include fn($space, left,$i,$n); } @if $direction == x { @include fn($space, left,$i,$n); @include fn($space, right,$i,$n); } @if $direction == y { @include fn($space, top,$i,$n); @include fn($space, bottom,$i,$n); } @if $direction == a { @if $n { #{$space}:#{$i*$uni-space-root}px; } @else { #{$space}:#{-$i*$uni-space-root}px; } } } @each $orientation in m,p { $space: margin; @if $orientation == m { $space: margin; } @else { $space: padding; } @for $i from 0 through 16 { @each $direction in t, r, b, l, x, y, a { .uni-#{$orientation}#{$direction}-#{$i} { @include get-styles($direction,$i,$space,true); } .uni-#{$orientation}#{$direction}-n#{$i} { @include get-styles($direction,$i,$space,false); } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_styles.scss ================================================ /* #ifndef APP-NVUE */ $-color-white:#fff; $-color-black:#000; @mixin base-style($color) { color: #fff; background-color: $color; border-color: mix($-color-black, $color, 8%); &:not([hover-class]):active { background: mix($-color-black, $color, 10%); border-color: mix($-color-black, $color, 20%); color: $-color-white; outline: none; } } @mixin is-color($color) { @include base-style($color); &[loading] { @include base-style($color); &::before { margin-right:5px; } } &[disabled] { &, &[loading], &:not([hover-class]):active { color: $-color-white; border-color: mix(darken($color,10%), $-color-white); background-color: mix($color, $-color-white); } } } @mixin base-plain-style($color) { color:$color; background-color: mix($-color-white, $color, 90%); border-color: mix($-color-white, $color, 70%); &:not([hover-class]):active { background: mix($-color-white, $color, 80%); color: $color; outline: none; border-color: mix($-color-white, $color, 50%); } } @mixin is-plain($color){ &[plain] { @include base-plain-style($color); &[loading] { @include base-plain-style($color); &::before { margin-right:5px; } } &[disabled] { &, &:active { color: mix($-color-white, $color, 40%); background-color: mix($-color-white, $color, 90%); border-color: mix($-color-white, $color, 80%); } } } } .uni-btn { margin: 5px; color: #393939; border:1px solid #ccc; font-size: 16px; font-weight: 200; background-color: #F9F9F9; // TODO 暂时处理边框隐藏一边的问题 overflow: visible; &::after{ border: none; } &:not([type]),&[type=default] { color: #999; &[loading] { background: none; &::before { margin-right:5px; } } &[disabled]{ color: mix($-color-white, #999, 60%); &, &[loading], &:active { color: mix($-color-white, #999, 60%); background-color: mix($-color-white,$-color-black , 98%); border-color: mix($-color-white, #999, 85%); } } &[plain] { color: #999; background: none; border-color: $uni-border-1; &:not([hover-class]):active { background: none; color: mix($-color-white, $-color-black, 80%); border-color: mix($-color-white, $-color-black, 90%); outline: none; } &[disabled]{ &, &[loading], &:active { background: none; color: mix($-color-white, #999, 60%); border-color: mix($-color-white, #999, 85%); } } } } &:not([hover-class]):active { color: mix($-color-white, $-color-black, 50%); } &[size=mini] { font-size: 16px; font-weight: 200; border-radius: 8px; } &.uni-btn-small { font-size: 14px; } &.uni-btn-mini { font-size: 12px; } &.uni-btn-radius { border-radius: 999px; } &[type=primary] { @include is-color($uni-primary); @include is-plain($uni-primary) } &[type=success] { @include is-color($uni-success); @include is-plain($uni-success) } &[type=error] { @include is-color($uni-error); @include is-plain($uni-error) } &[type=warning] { @include is-color($uni-warning); @include is-plain($uni-warning) } &[type=info] { @include is-color($uni-info); @include is-plain($uni-info) } } /* #endif */ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_text.scss ================================================ @mixin get-styles($k,$c) { @if $k == size or $k == weight{ font-#{$k}:#{$c} }@else{ #{$k}:#{$c} } } @each $key, $child in $uni-headings { /* #ifndef APP-NVUE */ .uni-#{$key} { @each $k, $c in $child { @include get-styles($k,$c) } } /* #endif */ /* #ifdef APP-NVUE */ .container .uni-#{$key} { @each $k, $c in $child { @include get-styles($k,$c) } } /* #endif */ } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/setting/_variables.scss ================================================ // @use "sass:math"; @import '../tools/functions.scss'; // 间距基础倍数 $uni-space-root: 2 !default; // 边框半径默认值 $uni-radius-root:5px !default; $uni-radius: () !default; // 边框半径断点 $uni-radius: map-deep-merge( ( 0: 0, // TODO 当前版本暂时不支持 sm 属性 // 'sm': math.div($uni-radius-root, 2), null: $uni-radius-root, 'lg': $uni-radius-root * 2, 'xl': $uni-radius-root * 6, 'pill': 9999px, 'circle': 50% ), $uni-radius ); // 字体家族 $body-font-family: 'Roboto', sans-serif !default; // 文本 $heading-font-family: $body-font-family !default; $uni-headings: () !default; $letterSpacing: -0.01562em; $uni-headings: map-deep-merge( ( 'h1': ( size: 32px, weight: 300, line-height: 50px, // letter-spacing:-0.01562em ), 'h2': ( size: 28px, weight: 300, line-height: 40px, // letter-spacing: -0.00833em ), 'h3': ( size: 24px, weight: 400, line-height: 32px, // letter-spacing: normal ), 'h4': ( size: 20px, weight: 400, line-height: 30px, // letter-spacing: 0.00735em ), 'h5': ( size: 16px, weight: 400, line-height: 24px, // letter-spacing: normal ), 'h6': ( size: 14px, weight: 500, line-height: 18px, // letter-spacing: 0.0125em ), 'subtitle': ( size: 12px, weight: 400, line-height: 20px, // letter-spacing: 0.00937em ), 'body': ( font-size: 14px, font-weight: 400, line-height: 22px, // letter-spacing: 0.03125em ), 'caption': ( 'size': 12px, 'weight': 400, 'line-height': 20px, // 'letter-spacing': 0.03333em, // 'text-transform': false ) ), $uni-headings ); // 主色 $uni-primary: #2979ff !default; $uni-primary-disable:lighten($uni-primary,20%) !default; $uni-primary-light: lighten($uni-primary,25%) !default; // 辅助色 // 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 $uni-success: #18bc37 !default; $uni-success-disable:lighten($uni-success,20%) !default; $uni-success-light: lighten($uni-success,25%) !default; $uni-warning: #f3a73f !default; $uni-warning-disable:lighten($uni-warning,20%) !default; $uni-warning-light: lighten($uni-warning,25%) !default; $uni-error: #e43d33 !default; $uni-error-disable:lighten($uni-error,20%) !default; $uni-error-light: lighten($uni-error,25%) !default; $uni-info: #8f939c !default; $uni-info-disable:lighten($uni-info,20%) !default; $uni-info-light: lighten($uni-info,25%) !default; // 中性色 // 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 $uni-main-color: #3a3a3a !default; // 主要文字 $uni-base-color: #6a6a6a !default; // 常规文字 $uni-secondary-color: #909399 !default; // 次要文字 $uni-extra-color: #c7c7c7 !default; // 辅助说明 // 边框颜色 $uni-border-1: #F0F0F0 !default; $uni-border-2: #EDEDED !default; $uni-border-3: #DCDCDC !default; $uni-border-4: #B9B9B9 !default; // 常规色 $uni-black: #000000 !default; $uni-white: #ffffff !default; $uni-transparent: rgba($color: #000000, $alpha: 0) !default; // 背景色 $uni-bg-color: #f7f7f7 !default; /* 水平间距 */ $uni-spacing-sm: 8px !default; $uni-spacing-base: 15px !default; $uni-spacing-lg: 30px !default; // 阴影 $uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default; $uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; $uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default; // 蒙版 $uni-mask: rgba($color: #000000, $alpha: 0.4) !default; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/styles/tools/functions.scss ================================================ // 合并 map @function map-deep-merge($parent-map, $child-map){ $result: $parent-map; @each $key, $child in $child-map { $parent-has-key: map-has-key($result, $key); $parent-value: map-get($result, $key); $parent-type: type-of($parent-value); $child-type: type-of($child); $parent-is-map: $parent-type == map; $child-is-map: $child-type == map; @if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){ $result: map-merge($result, ( $key: $child )); }@else { $result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) )); } } @return $result; }; ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/theme.scss ================================================ // 间距基础倍数 $uni-space-root: 2; // 边框半径默认值 $uni-radius-root:5px; // 主色 $uni-primary: #2979ff; // 辅助色 $uni-success: #4cd964; // 警告色 $uni-warning: #f0ad4e; // 错误色 $uni-error: #dd524d; // 描述色 $uni-info: #909399; // 中性色 $uni-main-color: #303133; $uni-base-color: #606266; $uni-secondary-color: #909399; $uni-extra-color: #C0C4CC; // 背景色 $uni-bg-color: #f5f5f5; // 边框颜色 $uni-border-1: #DCDFE6; $uni-border-2: #E4E7ED; $uni-border-3: #EBEEF5; $uni-border-4: #F2F6FC; // 常规色 $uni-black: #000000; $uni-white: #ffffff; $uni-transparent: rgba($color: #000000, $alpha: 0); ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-scss/variables.scss ================================================ @import './styles/setting/_variables.scss'; // 间距基础倍数 $uni-space-root: 2; // 边框半径默认值 $uni-radius-root:5px; // 主色 $uni-primary: #2979ff; $uni-primary-disable:mix(#fff,$uni-primary,50%); $uni-primary-light: mix(#fff,$uni-primary,80%); // 辅助色 // 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 $uni-success: #18bc37; $uni-success-disable:mix(#fff,$uni-success,50%); $uni-success-light: mix(#fff,$uni-success,80%); $uni-warning: #f3a73f; $uni-warning-disable:mix(#fff,$uni-warning,50%); $uni-warning-light: mix(#fff,$uni-warning,80%); $uni-error: #e43d33; $uni-error-disable:mix(#fff,$uni-error,50%); $uni-error-light: mix(#fff,$uni-error,80%); $uni-info: #8f939c; $uni-info-disable:mix(#fff,$uni-info,50%); $uni-info-light: mix(#fff,$uni-info,80%); // 中性色 // 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 $uni-main-color: #3a3a3a; // 主要文字 $uni-base-color: #6a6a6a; // 常规文字 $uni-secondary-color: #909399; // 次要文字 $uni-extra-color: #c7c7c7; // 辅助说明 // 边框颜色 $uni-border-1: #F0F0F0; $uni-border-2: #EDEDED; $uni-border-3: #DCDCDC; $uni-border-4: #B9B9B9; // 常规色 $uni-black: #000000; $uni-white: #ffffff; $uni-transparent: rgba($color: #000000, $alpha: 0); // 背景色 $uni-bg-color: #f7f7f7; /* 水平间距 */ $uni-spacing-sm: 8px; $uni-spacing-base: 15px; $uni-spacing-lg: 30px; // 阴影 $uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5); $uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2); $uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5); // 蒙版 $uni-mask: rgba($color: #000000, $alpha: 0.4); ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/changelog.md ================================================ ## 1.2.4(2023-05-09) - 修复 i18n 国际化不正确的 Bug ## 1.2.3(2022-05-24) - 新增 readonly 属性,组件只读 ## 1.2.2(2022-05-06) - 修复 vue3 input 事件不生效的bug ## 1.2.1(2022-05-06) - 修复 多余代码导致的bug ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-search-bar](https://uniapp.dcloud.io/component/uniui/uni-search-bar) ## 1.1.2(2021-08-30) - 修复 value 属性与 modelValue 属性不兼容的Bug ## 1.1.1(2021-08-24) - 新增 支持国际化 ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.9(2021-05-12) - 新增 项目示例地址 ## 1.0.8(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.7(2021-04-15) - uni-ui 新增 uni-search-bar 的 focus 事件 ## 1.0.6(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.5(2021-02-05) - 调整为uni_modules目录规范 - 新增 支持双向绑定 - 更改 input 事件的返回值,e={value:Number} --> e=value - 新增 支持图标插槽 - 新增 支持 clear、blur 事件 - 新增 支持 focus 属性 - 去掉组件背景色 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json ================================================ { "uni-search-bar.cancel": "cancel", "uni-search-bar.placeholder": "Search enter content" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js ================================================ import en from './en.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json ================================================ { "uni-search-bar.cancel": "取消", "uni-search-bar.placeholder": "请输入搜索内容" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json ================================================ { "uni-search-bar.cancel": "取消", "uni-search-bar.placeholder": "請輸入搜索內容" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/package.json ================================================ { "id": "uni-search-bar", "displayName": "uni-search-bar 搜索栏", "version": "1.2.4", "description": "搜索栏组件,通常用于搜索商品、文章等", "keywords": [ "uni-ui", "uniui", "搜索框", "搜索栏" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-search-bar/readme.md ================================================ ## SearchBar 搜索栏 > **组件名:uni-search-bar** > 代码块: `uSearchBar` 搜索栏组件 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-search-bar) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-section/changelog.md ================================================ ## 0.0.1(2022-07-22) - 初始化 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-section/components/uni-section/uni-section.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-section/package.json ================================================ { "id": "uni-section", "displayName": "uni-section 标题栏", "version": "0.0.1", "description": "标题栏组件", "keywords": [ "uni-ui", "uniui", "标题栏" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-section/readme.md ================================================ ## Section 标题栏 > **组件名:uni-section** > 代码块: `uSection` uni-section 组件主要用于文章、列表详情等标题展示 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-section) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-segmented-control/changelog.md ================================================ ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-segmented-control](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.5(2021-05-12) - 新增 项目示例地址 ## 1.0.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-segmented-control/package.json ================================================ { "id": "uni-segmented-control", "displayName": "uni-segmented-control 分段器", "version": "1.2.0", "description": "分段器由至少 2 个分段控件组成,用作不同视图的显示", "keywords": [ "uni-ui", "uniui", "分段器", "segement", "顶部选择" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-segmented-control/readme.md ================================================ ## SegmentedControl 分段器 > **组件名:uni-segmented-control** > 代码块: `uSegmentedControl` 用作不同视图的显示 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-steps/changelog.md ================================================ ## 1.1.1(2021-11-22) - 修复 vue3中某些scss变量无法找到的问题 ## 1.1.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-steps](https://uniapp.dcloud.io/component/uniui/uni-steps) ## 1.0.8(2021-05-12) - 新增 项目示例地址 ## 1.0.7(2021-05-06) - 修复 uni-steps 横向布局时,多行文字高度不合理的 bug ## 1.0.6(2021-04-21) - 优化 添加依赖 uni-icons, 导入后自动下载依赖 ## 1.0.5(2021-02-05) - 优化 组件引用关系,通过uni_modules引用组件 ## 1.0.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-steps/components/uni-steps/uni-steps.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-steps/package.json ================================================ { "id": "uni-steps", "displayName": "uni-steps 步骤条", "version": "1.1.1", "description": "步骤条组件,提供横向和纵向两种布局格式。", "keywords": [ "uni-ui", "uniui", "步骤条", "时间轴" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": [ "uni-scss", "uni-icons" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-steps/readme.md ================================================ ## Steps 步骤条 > **组件名:uni-steps** > 代码块: `uSteps` 步骤条,常用于显示进度 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-steps) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/changelog.md ================================================ ## 1.3.8(2023-04-13) - 修复`uni-swipe-action`和`uni-swipe-action-item`不同时使用导致 closeOther 方法报错的 bug ## 1.3.7(2022-06-06) - 修复 vue3 下使用组件不能正常运行的Bug ## 1.3.6(2022-05-31) - 修复 h5端点击click触发两次的Bug ## 1.3.5(2022-05-23) - 修复 isPC 找不到的Bug ## 1.3.4(2022-05-19) - 修复 在 nvue 下 disabled 失效的bug ## 1.3.3(2022-03-31) - 修复 按钮字体大小不能设置的bug ## 1.3.2(2022-03-16) - 修复 h5和app端下报el错误的bug ## 1.3.1(2022-03-07) - 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug ## 1.3.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swipe-action](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) ## 1.2.4(2021-08-20) - 优化 close-all 方法 ## 1.2.3(2021-08-20) - 新增 close-all 方法,关闭所有已打开的组件 ## 1.2.2(2021-08-17) - 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 - 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 - 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 ## 1.2.1(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) - 修复 跨页面修改组件数据 ,导致不能滑动的问题 ## 1.1.10(2021-06-17) - 修复 按钮点击执行两次的bug ## 1.1.9(2021-05-12) - 新增 项目示例地址 ## 1.1.8(2021-03-26) - 修复 微信小程序 nv_navigator is not defined 报错的bug ## 1.1.7(2021-02-05) - 调整为uni_modules目录规范 - 新增 左侧滑动 - 新增 插槽使用方式 - 新增 threshold 属性,可以控制滑动缺省值 - 优化 长列表滚动性能 - 修复 滚动页面时触发组件滑动的Bug ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js ================================================ let bindIngXMixins = {} // #ifdef APP-NVUE const BindingX = uni.requireNativePlugin('bindingx'); const dom = uni.requireNativePlugin('dom'); const animation = uni.requireNativePlugin('animation'); bindIngXMixins = { data() { return {} }, watch: { show(newVal) { if (this.autoClose) return if (this.stop) return this.stop = true if (newVal) { this.open(newVal) } else { this.close() } }, leftOptions() { this.getSelectorQuery() this.init() }, rightOptions(newVal) { this.init() } }, created() { this.swipeaction = this.getSwipeAction() if (this.swipeaction && Array.isArray(this.swipeaction.children)) { this.swipeaction.children.push(this) } }, mounted() { this.box = this.getEl(this.$refs['selector-box--hock']) this.selector = this.getEl(this.$refs['selector-content--hock']); this.leftButton = this.getEl(this.$refs['selector-left-button--hock']); this.rightButton = this.getEl(this.$refs['selector-right-button--hock']); this.init() }, // beforeDestroy() { // this.swipeaction.children.forEach((item, index) => { // if (item === this) { // this.swipeaction.children.splice(index, 1) // } // }) // }, methods: { init() { this.$nextTick(() => { this.x = 0 this.button = { show: false } setTimeout(() => { this.getSelectorQuery() }, 200) }) }, onClick(index, item, position) { this.$emit('click', { content: item, index, position }) }, touchstart(e) { // fix by mehaotian 禁止滑动 if (this.disabled) return // 每次只触发一次,避免多次监听造成闪烁 if (this.stop) return this.stop = true if (this.autoClose && this.swipeaction) { this.swipeaction.closeOther(this) } const leftWidth = this.button.left.width const rightWidth = this.button.right.width let expression = this.range(this.x, -rightWidth, leftWidth) let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0) let rightExpression = this.range(this.x + rightWidth, 0, rightWidth) this.eventpan = BindingX.bind({ anchor: this.box, eventType: 'pan', props: [{ element: this.selector, property: 'transform.translateX', expression }, { element: this.leftButton, property: 'transform.translateX', expression: leftExpression }, { element: this.rightButton, property: 'transform.translateX', expression: rightExpression }, ] }, (e) => { // nope if (e.state === 'end') { this.x = e.deltaX + this.x; this.isclick = true this.bindTiming(e.deltaX) } }); }, touchend(e) { if (this.isopen !== 'none' && !this.isclick) { this.open('none') } }, bindTiming(x) { const left = this.x const leftWidth = this.button.left.width const rightWidth = this.button.right.width const threshold = this.threshold if (!this.isopen || this.isopen === 'none') { if (left > threshold) { this.open('left') } else if (left < -threshold) { this.open('right') } else { this.open('none') } } else { if ((x > -leftWidth && x < 0) || x > rightWidth) { if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) { this.open('left') } else { this.open('none') } } else { if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) { this.open('right') } else { this.open('none') } } } }, /** * 移动范围 * @param {Object} num * @param {Object} mix * @param {Object} max */ range(num, mix, max) { return `min(max(x+${num}, ${mix}), ${max})` }, /** * 开启swipe */ open(type) { this.animation(type) }, /** * 关闭swipe */ close() { this.animation('none') }, /** * 开启关闭动画 * @param {Object} type */ animation(type) { const time = 300 const leftWidth = this.button.left.width const rightWidth = this.button.right.width if (this.eventpan && this.eventpan.token) { BindingX.unbind({ token: this.eventpan.token, eventType: 'pan' }) } switch (type) { case 'left': Promise.all([ this.move(this.selector, leftWidth), this.move(this.leftButton, 0), this.move(this.rightButton, rightWidth * 2) ]).then(() => { this.setEmit(leftWidth, type) }) break case 'right': Promise.all([ this.move(this.selector, -rightWidth), this.move(this.leftButton, -leftWidth * 2), this.move(this.rightButton, 0) ]).then(() => { this.setEmit(-rightWidth, type) }) break default: Promise.all([ this.move(this.selector, 0), this.move(this.leftButton, -leftWidth), this.move(this.rightButton, rightWidth) ]).then(() => { this.setEmit(0, type) }) } }, setEmit(x, type) { const leftWidth = this.button.left.width const rightWidth = this.button.right.width this.isopen = this.isopen || 'none' this.stop = false this.isclick = false // 只有状态不一致才会返回结果 if (this.isopen !== type && this.x !== x) { if (type === 'left' && leftWidth > 0) { this.$emit('change', 'left') } if (type === 'right' && rightWidth > 0) { this.$emit('change', 'right') } if (type === 'none') { this.$emit('change', 'none') } } this.x = x this.isopen = type }, move(ref, value) { return new Promise((resolve, reject) => { animation.transition(ref, { styles: { transform: `translateX(${value})`, }, duration: 150, //ms timingFunction: 'linear', needLayout: false, delay: 0 //ms }, function(res) { resolve(res) }) }) }, /** * 获取ref * @param {Object} el */ getEl(el) { return el.ref }, /** * 获取节点信息 */ getSelectorQuery() { Promise.all([ this.getDom('left'), this.getDom('right'), ]).then((data) => { let show = 'none' if (this.autoClose) { show = 'none' } else { show = this.show } if (show === 'none') { // this.close() } else { this.open(show) } }) }, getDom(str) { return new Promise((resolve, reject) => { dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => { if (data) { this.button[str] = data.size resolve(data) } else { reject() } }) }) } } } // #endif export default bindIngXMixins ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js ================================================ export function isPC() { var userAgentInfo = navigator.userAgent; var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; var flag = true; for (let v = 0; v < Agents.length - 1; v++) { if (userAgentInfo.indexOf(Agents[v]) > 0) { flag = false; break; } } return flag; } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js ================================================ export default { data() { return { x: 0, transition: false, width: 0, viewWidth: 0, swipeShow: 0 } }, watch: { show(newVal) { if (this.autoClose) return if (newVal && newVal !== 'none') { this.transition = true this.open(newVal) } else { this.close() } } }, created() { this.swipeaction = this.getSwipeAction() if (this.swipeaction && Array.isArray(this.swipeaction.children)) { this.swipeaction.children.push(this) } }, mounted() { this.isopen = false setTimeout(() => { this.getQuerySelect() }, 50) }, methods: { appTouchStart(e) { const { clientX } = e.changedTouches[0] this.clientX = clientX this.timestamp = new Date().getTime() }, appTouchEnd(e, index, item, position) { const { clientX } = e.changedTouches[0] // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 let diff = Math.abs(this.clientX - clientX) let time = (new Date().getTime()) - this.timestamp if (diff < 40 && time < 300) { this.$emit('click', { content: item, index, position }) } }, /** * 移动触发 * @param {Object} e */ onChange(e) { this.moveX = e.detail.x this.isclose = false }, touchstart(e) { this.transition = false this.isclose = true if (this.autoClose && this.swipeaction) { this.swipeaction.closeOther(this) } }, touchmove(e) {}, touchend(e) { // 0的位置什么都不执行 if (this.isclose && this.isopen === 'none') return if (this.isclose && this.isopen !== 'none') { this.transition = true this.close() } else { this.move(this.moveX + this.leftWidth) } }, /** * 移动 * @param {Object} moveX */ move(moveX) { // 打开关闭的处理逻辑不太一样 this.transition = true // 未打开状态 if (!this.isopen || this.isopen === 'none') { if (moveX > this.threshold) { this.open('left') } else if (moveX < -this.threshold) { this.open('right') } else { this.close() } } else { if (moveX < 0 && moveX < this.rightWidth) { const rightX = this.rightWidth + moveX if (rightX < this.threshold) { this.open('right') } else { this.close() } } else if (moveX > 0 && moveX < this.leftWidth) { const leftX = this.leftWidth - moveX if (leftX < this.threshold) { this.open('left') } else { this.close() } } } }, /** * 打开 */ open(type) { this.x = this.moveX this.animation(type) }, /** * 关闭 */ close() { this.x = this.moveX // TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化 this.$nextTick(() => { this.x = -this.leftWidth if (this.isopen !== 'none') { this.$emit('change', 'none') } this.isopen = 'none' }) }, /** * 执行结束动画 * @param {Object} type */ animation(type) { this.$nextTick(() => { if (type === 'left') { this.x = 0 } else { this.x = -this.rightWidth - this.leftWidth } if (this.isopen !== type) { this.$emit('change', type) } this.isopen = type }) }, getSlide(x) {}, getQuerySelect() { const query = uni.createSelectorQuery().in(this); query.selectAll('.movable-view--hock').boundingClientRect(data => { this.leftWidth = data[1].width this.rightWidth = data[2].width this.width = data[0].width this.viewWidth = this.width + this.rightWidth + this.leftWidth if (this.leftWidth === 0) { // TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点 this.x = -0.1 } else { this.x = -this.leftWidth } this.moveX = this.x this.$nextTick(() => { this.swipeShow = 1 }) if (!this.buttonWidth) { this.disabledView = true } if (this.autoClose) return if (this.show !== 'none') { this.transition = true this.open(this.shows) } }).exec(); } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js ================================================ let otherMixins = {} // #ifndef APP-PLUS|| MP-WEIXIN || H5 const MIN_DISTANCE = 10; otherMixins = { data() { // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug const elClass = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` return { uniShow: false, left: 0, buttonShow: 'none', ani: false, moveLeft: '', elClass } }, watch: { show(newVal) { if (this.autoClose) return this.openState(newVal) }, left() { this.moveLeft = `translateX(${this.left}px)` }, buttonShow(newVal) { if (this.autoClose) return this.openState(newVal) }, leftOptions() { this.init() }, rightOptions() { this.init() } }, mounted() { this.swipeaction = this.getSwipeAction() if (this.swipeaction && Array.isArray(this.swipeaction.children)) { this.swipeaction.children.push(this) } this.init() }, methods: { init() { clearTimeout(this.timer) this.timer = setTimeout(() => { this.getSelectorQuery() }, 100) // 移动距离 this.left = 0 this.x = 0 }, closeSwipe(e) { if (this.autoClose && this.swipeaction) { this.swipeaction.closeOther(this) } }, appTouchStart(e) { const { clientX } = e.changedTouches[0] this.clientX = clientX this.timestamp = new Date().getTime() }, appTouchEnd(e, index, item, position) { const { clientX } = e.changedTouches[0] // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 let diff = Math.abs(this.clientX - clientX) let time = (new Date().getTime()) - this.timestamp if (diff < 40 && time < 300) { this.$emit('click', { content: item, index, position }) } }, touchstart(e) { if (this.disabled) return this.ani = false this.x = this.left || 0 this.stopTouchStart(e) this.autoClose && this.closeSwipe() }, touchmove(e) { if (this.disabled) return // 是否可以滑动页面 this.stopTouchMove(e); if (this.direction !== 'horizontal') { return; } this.move(this.x + this.deltaX) return false }, touchend() { if (this.disabled) return this.moveDirection(this.left) }, /** * 设置移动距离 * @param {Object} value */ move(value) { value = value || 0 const leftWidth = this.leftWidth const rightWidth = this.rightWidth // 获取可滑动范围 this.left = this.range(value, -rightWidth, leftWidth); }, /** * 获取范围 * @param {Object} num * @param {Object} min * @param {Object} max */ range(num, min, max) { return Math.min(Math.max(num, min), max); }, /** * 移动方向判断 * @param {Object} left * @param {Object} value */ moveDirection(left) { const threshold = this.threshold const isopen = this.isopen || 'none' const leftWidth = this.leftWidth const rightWidth = this.rightWidth if (this.deltaX === 0) { this.openState('none') return } if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth + left < threshold)) { // right this.openState('right') } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && leftWidth - left < threshold)) { // left this.openState('left') } else { // default this.openState('none') } }, /** * 开启状态 * @param {Boolean} type */ openState(type) { const leftWidth = this.leftWidth const rightWidth = this.rightWidth let left = '' this.isopen = this.isopen ? this.isopen : 'none' switch (type) { case "left": left = leftWidth break case "right": left = -rightWidth break default: left = 0 } if (this.isopen !== type) { this.throttle = true this.$emit('change', type) } this.isopen = type // 添加动画类 this.ani = true this.$nextTick(() => { this.move(left) }) // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 }, close() { this.openState('none') }, getDirection(x, y) { if (x > y && x > MIN_DISTANCE) { return 'horizontal'; } if (y > x && y > MIN_DISTANCE) { return 'vertical'; } return ''; }, /** * 重置滑动状态 * @param {Object} event */ resetTouchStatus() { this.direction = ''; this.deltaX = 0; this.deltaY = 0; this.offsetX = 0; this.offsetY = 0; }, /** * 设置滑动开始位置 * @param {Object} event */ stopTouchStart(event) { this.resetTouchStatus(); const touch = event.touches[0]; this.startX = touch.clientX; this.startY = touch.clientY; }, /** * 滑动中,是否禁止打开 * @param {Object} event */ stopTouchMove(event) { const touch = event.touches[0]; this.deltaX = touch.clientX - this.startX; this.deltaY = touch.clientY - this.startY; this.offsetX = Math.abs(this.deltaX); this.offsetY = Math.abs(this.deltaY); this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); }, getSelectorQuery() { const views = uni.createSelectorQuery().in(this) views .selectAll('.' + this.elClass) .boundingClientRect(data => { if (data.length === 0) return let show = 'none' if (this.autoClose) { show = 'none' } else { show = this.show } this.leftWidth = data[0].width || 0 this.rightWidth = data[1].width || 0 this.buttonShow = show }) .exec() } } } // #endif export default otherMixins ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js ================================================ let mpMixins = {} let is_pc = null // #ifdef H5 import { isPC } from "./isPC" is_pc = isPC() // #endif // #ifdef APP-VUE|| MP-WEIXIN || H5 mpMixins = { data() { return { is_show: 'none' } }, watch: { show(newVal) { this.is_show = this.show } }, created() { this.swipeaction = this.getSwipeAction() if (this.swipeaction && Array.isArray(this.swipeaction.children)) { this.swipeaction.children.push(this) } }, mounted() { this.is_show = this.show }, methods: { // wxs 中调用 closeSwipe(e) { if (this.autoClose && this.swipeaction) { this.swipeaction.closeOther(this) } }, change(e) { this.$emit('change', e.open) if (this.is_show !== e.open) { this.is_show = e.open } }, appTouchStart(e) { if (is_pc) return const { clientX } = e.changedTouches[0] this.clientX = clientX this.timestamp = new Date().getTime() }, appTouchEnd(e, index, item, position) { if (is_pc) return const { clientX } = e.changedTouches[0] // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 let diff = Math.abs(this.clientX - clientX) let time = (new Date().getTime()) - this.timestamp if (diff < 40 && time < 300) { this.$emit('click', { content: item, index, position }) } }, onClickForPC(index, item, position) { if (!is_pc) return // #ifdef H5 this.$emit('click', { content: item, index, position }) // #endif } } } // #endif export default mpMixins ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js ================================================ const MIN_DISTANCE = 10; export default { showWatch(newVal, oldVal, ownerInstance, instance, self) { var state = self.state var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el if (!$el) return this.getDom(instance, ownerInstance, self) if (newVal && newVal !== 'none') { this.openState(newVal, instance, ownerInstance, self) return } if (state.left) { this.openState('none', instance, ownerInstance, self) } this.resetTouchStatus(instance, self) }, /** * 开始触摸操作 * @param {Object} e * @param {Object} ins */ touchstart(e, ownerInstance, self) { let instance = e.instance; let disabled = instance.getDataset().disabled let state = self.state; this.getDom(instance, ownerInstance, self) // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = this.getDisabledType(disabled) if (disabled) return // 开始触摸时移除动画类 instance.requestAnimationFrame(function() { instance.removeClass('ani'); ownerInstance.callMethod('closeSwipe'); }) // 记录上次的位置 state.x = state.left || 0 // 计算滑动开始位置 this.stopTouchStart(e, ownerInstance, self) }, /** * 开始滑动操作 * @param {Object} e * @param {Object} ownerInstance */ touchmove(e, ownerInstance, self) { let instance = e.instance; // 删除之后已经那不到实例了 if (!instance) return; let disabled = instance.getDataset().disabled let state = self.state // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = this.getDisabledType(disabled) if (disabled) return // 是否可以滑动页面 this.stopTouchMove(e, self); if (state.direction !== 'horizontal') { return; } if (e.preventDefault) { // 阻止页面滚动 e.preventDefault() } let x = state.x + state.deltaX this.move(x, instance, ownerInstance, self) }, /** * 结束触摸操作 * @param {Object} e * @param {Object} ownerInstance */ touchend(e, ownerInstance, self) { let instance = e.instance; let disabled = instance.getDataset().disabled let state = self.state // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = this.getDisabledType(disabled) if (disabled) return // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 this.moveDirection(state.left, instance, ownerInstance, self) }, /** * 设置移动距离 * @param {Object} value * @param {Object} instance * @param {Object} ownerInstance */ move(value, instance, ownerInstance, self) { value = value || 0 let state = self.state let leftWidth = state.leftWidth let rightWidth = state.rightWidth // 获取可滑动范围 state.left = this.range(value, -rightWidth, leftWidth); instance.requestAnimationFrame(function() { instance.setStyle({ transform: 'translateX(' + state.left + 'px)', '-webkit-transform': 'translateX(' + state.left + 'px)' }) }) }, /** * 获取元素信息 * @param {Object} instance * @param {Object} ownerInstance */ getDom(instance, ownerInstance, self) { var state = self.state var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el var leftDom = $el.querySelector('.button-group--left') var rightDom = $el.querySelector('.button-group--right') state.leftWidth = leftDom.offsetWidth || 0 state.rightWidth = rightDom.offsetWidth || 0 state.threshold = instance.getDataset().threshold }, getDisabledType(value) { return (typeof(value) === 'string' ? JSON.parse(value) : value) || false; }, /** * 获取范围 * @param {Object} num * @param {Object} min * @param {Object} max */ range(num, min, max) { return Math.min(Math.max(num, min), max); }, /** * 移动方向判断 * @param {Object} left * @param {Object} value * @param {Object} ownerInstance * @param {Object} ins */ moveDirection(left, ins, ownerInstance, self) { var state = self.state var threshold = state.threshold var position = state.position var isopen = state.isopen || 'none' var leftWidth = state.leftWidth var rightWidth = state.rightWidth if (state.deltaX === 0) { this.openState('none', ins, ownerInstance, self) return } if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth + left < threshold)) { // right this.openState('right', ins, ownerInstance, self) } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && leftWidth - left < threshold)) { // left this.openState('left', ins, ownerInstance, self) } else { // default this.openState('none', ins, ownerInstance, self) } }, /** * 开启状态 * @param {Boolean} type * @param {Object} ins * @param {Object} ownerInstance */ openState(type, ins, ownerInstance, self) { let state = self.state let leftWidth = state.leftWidth let rightWidth = state.rightWidth let left = '' state.isopen = state.isopen ? state.isopen : 'none' switch (type) { case "left": left = leftWidth break case "right": left = -rightWidth break default: left = 0 } // && !state.throttle if (state.isopen !== type) { state.throttle = true ownerInstance.callMethod('change', { open: type }) } state.isopen = type // 添加动画类 ins.requestAnimationFrame(() => { ins.addClass('ani'); this.move(left, ins, ownerInstance, self) }) }, getDirection(x, y) { if (x > y && x > MIN_DISTANCE) { return 'horizontal'; } if (y > x && y > MIN_DISTANCE) { return 'vertical'; } return ''; }, /** * 重置滑动状态 * @param {Object} event */ resetTouchStatus(instance, self) { let state = self.state; state.direction = ''; state.deltaX = 0; state.deltaY = 0; state.offsetX = 0; state.offsetY = 0; }, /** * 设置滑动开始位置 * @param {Object} event */ stopTouchStart(event, ownerInstance, self) { let instance = event.instance; let state = self.state this.resetTouchStatus(instance, self); var touch = event.touches[0]; state.startX = touch.clientX; state.startY = touch.clientY; }, /** * 滑动中,是否禁止打开 * @param {Object} event */ stopTouchMove(event, self) { let instance = event.instance; let state = self.state; let touch = event.touches[0]; state.deltaX = touch.clientX - state.startX; state.deltaY = touch.clientY - state.startY; state.offsetY = Math.abs(state.deltaY); state.offsetX = Math.abs(state.deltaX); state.direction = state.direction || this.getDirection(state.offsetX, state.offsetY); } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs ================================================ var MIN_DISTANCE = 10; /** * 判断当前是否为H5、app-vue */ var IS_HTML5 = false if (typeof window === 'object') IS_HTML5 = true /** * 监听页面内值的变化,主要用于动态开关swipe-action * @param {Object} newValue * @param {Object} oldValue * @param {Object} ownerInstance * @param {Object} instance */ function showWatch(newVal, oldVal, ownerInstance, instance) { var state = instance.getState() getDom(instance, ownerInstance) if (newVal && newVal !== 'none') { openState(newVal, instance, ownerInstance) return } if (state.left) { openState('none', instance, ownerInstance) } resetTouchStatus(instance) } /** * 开始触摸操作 * @param {Object} e * @param {Object} ins */ function touchstart(e, ownerInstance) { var instance = e.instance; var disabled = instance.getDataset().disabled var state = instance.getState(); getDom(instance, ownerInstance) // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; if (disabled) return // 开始触摸时移除动画类 instance.requestAnimationFrame(function() { instance.removeClass('ani'); ownerInstance.callMethod('closeSwipe'); }) // 记录上次的位置 state.x = state.left || 0 // 计算滑动开始位置 stopTouchStart(e, ownerInstance) } /** * 开始滑动操作 * @param {Object} e * @param {Object} ownerInstance */ function touchmove(e, ownerInstance) { var instance = e.instance; var disabled = instance.getDataset().disabled var state = instance.getState() // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; if (disabled) return // 是否可以滑动页面 stopTouchMove(e); if (state.direction !== 'horizontal') { return; } if (e.preventDefault) { // 阻止页面滚动 e.preventDefault() } move(state.x + state.deltaX, instance, ownerInstance) } /** * 结束触摸操作 * @param {Object} e * @param {Object} ownerInstance */ function touchend(e, ownerInstance) { var instance = e.instance; var disabled = instance.getDataset().disabled var state = instance.getState() // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; if (disabled) return // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 moveDirection(state.left, instance, ownerInstance) } /** * 设置移动距离 * @param {Object} value * @param {Object} instance * @param {Object} ownerInstance */ function move(value, instance, ownerInstance) { value = value || 0 var state = instance.getState() var leftWidth = state.leftWidth var rightWidth = state.rightWidth // 获取可滑动范围 state.left = range(value, -rightWidth, leftWidth); instance.requestAnimationFrame(function() { instance.setStyle({ transform: 'translateX(' + state.left + 'px)', '-webkit-transform': 'translateX(' + state.left + 'px)' }) }) } /** * 获取元素信息 * @param {Object} instance * @param {Object} ownerInstance */ function getDom(instance, ownerInstance) { var state = instance.getState() var leftDom = ownerInstance.selectComponent('.button-group--left') var rightDom = ownerInstance.selectComponent('.button-group--right') var leftStyles = { width: 0 } var rightStyles = { width: 0 } leftStyles = leftDom.getBoundingClientRect() rightStyles = rightDom.getBoundingClientRect() state.leftWidth = leftStyles.width || 0 state.rightWidth = rightStyles.width || 0 state.threshold = instance.getDataset().threshold } /** * 获取范围 * @param {Object} num * @param {Object} min * @param {Object} max */ function range(num, min, max) { return Math.min(Math.max(num, min), max); } /** * 移动方向判断 * @param {Object} left * @param {Object} value * @param {Object} ownerInstance * @param {Object} ins */ function moveDirection(left, ins, ownerInstance) { var state = ins.getState() var threshold = state.threshold var position = state.position var isopen = state.isopen || 'none' var leftWidth = state.leftWidth var rightWidth = state.rightWidth if (state.deltaX === 0) { openState('none', ins, ownerInstance) return } if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth + left < threshold)) { // right openState('right', ins, ownerInstance) } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && leftWidth - left < threshold)) { // left openState('left', ins, ownerInstance) } else { // default openState('none', ins, ownerInstance) } } /** * 开启状态 * @param {Boolean} type * @param {Object} ins * @param {Object} ownerInstance */ function openState(type, ins, ownerInstance) { var state = ins.getState() var leftWidth = state.leftWidth var rightWidth = state.rightWidth var left = '' state.isopen = state.isopen ? state.isopen : 'none' switch (type) { case "left": left = leftWidth break case "right": left = -rightWidth break default: left = 0 } // && !state.throttle if (state.isopen !== type) { state.throttle = true ownerInstance.callMethod('change', { open: type }) } state.isopen = type // 添加动画类 ins.requestAnimationFrame(function() { ins.addClass('ani'); move(left, ins, ownerInstance) }) // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 } function getDirection(x, y) { if (x > y && x > MIN_DISTANCE) { return 'horizontal'; } if (y > x && y > MIN_DISTANCE) { return 'vertical'; } return ''; } /** * 重置滑动状态 * @param {Object} event */ function resetTouchStatus(instance) { var state = instance.getState(); state.direction = ''; state.deltaX = 0; state.deltaY = 0; state.offsetX = 0; state.offsetY = 0; } /** * 设置滑动开始位置 * @param {Object} event */ function stopTouchStart(event) { var instance = event.instance; var state = instance.getState(); resetTouchStatus(instance); var touch = event.touches[0]; if (IS_HTML5 && isPC()) { touch = event; } state.startX = touch.clientX; state.startY = touch.clientY; } /** * 滑动中,是否禁止打开 * @param {Object} event */ function stopTouchMove(event) { var instance = event.instance; var state = instance.getState(); var touch = event.touches[0]; if (IS_HTML5 && isPC()) { touch = event; } state.deltaX = touch.clientX - state.startX; state.deltaY = touch.clientY - state.startY; state.offsetY = Math.abs(state.deltaY); state.offsetX = Math.abs(state.deltaX); state.direction = state.direction || getDirection(state.offsetX, state.offsetY); } function isPC() { var userAgentInfo = navigator.userAgent; var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; var flag = true; for (var v = 0; v < Agents.length - 1; v++) { if (userAgentInfo.indexOf(Agents[v]) > 0) { flag = false; break; } } return flag; } var movable = false function mousedown(e, ins) { if (!IS_HTML5) return if (!isPC()) return touchstart(e, ins) movable = true } function mousemove(e, ins) { if (!IS_HTML5) return if (!isPC()) return if (!movable) return touchmove(e, ins) } function mouseup(e, ins) { if (!IS_HTML5) return if (!isPC()) return touchend(e, ins) movable = false } function mouseleave(e, ins) { if (!IS_HTML5) return if (!isPC()) return movable = false } module.exports = { showWatch: showWatch, touchstart: touchstart, touchmove: touchmove, touchend: touchend, mousedown: mousedown, mousemove: mousemove, mouseup: mouseup, mouseleave: mouseleave } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/package.json ================================================ { "id": "uni-swipe-action", "displayName": "uni-swipe-action 滑动操作", "version": "1.3.8", "description": "SwipeAction 滑动操作操作组件", "keywords": [ "", "uni-ui", "uniui", "滑动删除", "侧滑删除" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "y", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swipe-action/readme.md ================================================ ## SwipeAction 滑动操作 > **组件名:uni-swipe-action** > 代码块: `uSwipeAction`、`uSwipeActionItem` 通过滑动触发选项的容器 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swiper-dot/changelog.md ================================================ ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swiper-dot](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.6(2021-05-12) - 新增 示例地址 - 修复 示例项目缺少组件的Bug ## 1.0.5(2021-02-05) - 调整为uni_modules目录规范 - 新增 clickItem 事件,支持指示点控制轮播 - 新增 支持 pc 可用 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swiper-dot/package.json ================================================ { "id": "uni-swiper-dot", "displayName": "uni-swiper-dot 轮播图指示点", "version": "1.2.0", "description": "自定义轮播图指示点组件", "keywords": [ "uni-ui", "uniui", "轮播图指示点", "dot", "swiper" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-swiper-dot/readme.md ================================================ ## SwiperDot 轮播图指示点 > **组件名:uni-swiper-dot** > 代码块: `uSwiperDot` 自定义轮播图指示点 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/changelog.md ================================================ ## 1.2.3(2023-03-28) - 修复 在vue3模式下可能会出现错误的问题 ## 1.2.2(2022-11-29) - 优化 主题样式 ## 1.2.1(2022-06-06) - 修复 微信小程序存在无使用组件的问题 ## 1.2.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table) ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-07-08) - 新增 uni-th 支持 date 日期筛选范围 ## 1.0.6(2021-07-05) - 新增 uni-th 支持 range 筛选范围 ## 1.0.5(2021-06-28) - 新增 uni-th 筛选功能 ## 1.0.4(2021-05-12) - 新增 示例地址 - 修复 示例项目缺少组件的Bug ## 1.0.3(2021-04-16) - 新增 sortable 属性,是否开启单列排序 - 优化 表格多选逻辑 ## 1.0.2(2021-03-22) - uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制 ## 1.0.1(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-table/uni-table.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-td/uni-td.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-th/filter-dropdown.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-th/uni-th.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-thead/uni-thead.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-tr/table-checkbox.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/components/uni-tr/uni-tr.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/en.json ================================================ { "filter-dropdown.reset": "Reset", "filter-dropdown.search": "Search", "filter-dropdown.submit": "Submit", "filter-dropdown.filter": "Filter", "filter-dropdown.gt": "Greater or equal to", "filter-dropdown.lt": "Less than or equal to", "filter-dropdown.date": "Date" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/es.json ================================================ { "filter-dropdown.reset": "Reiniciar", "filter-dropdown.search": "Búsqueda", "filter-dropdown.submit": "Entregar", "filter-dropdown.filter": "Filtrar", "filter-dropdown.gt": "Mayor o igual a", "filter-dropdown.lt": "Menos que o igual a", "filter-dropdown.date": "Fecha" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/fr.json ================================================ { "filter-dropdown.reset": "Réinitialiser", "filter-dropdown.search": "Chercher", "filter-dropdown.submit": "Soumettre", "filter-dropdown.filter": "Filtre", "filter-dropdown.gt": "Supérieur ou égal à", "filter-dropdown.lt": "Inférieur ou égal à", "filter-dropdown.date": "Date" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/index.js ================================================ import en from './en.json' import es from './es.json' import fr from './fr.json' import zhHans from './zh-Hans.json' import zhHant from './zh-Hant.json' export default { en, es, fr, 'zh-Hans': zhHans, 'zh-Hant': zhHant } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/zh-Hans.json ================================================ { "filter-dropdown.reset": "重置", "filter-dropdown.search": "搜索", "filter-dropdown.submit": "确定", "filter-dropdown.filter": "筛选", "filter-dropdown.gt": "大于等于", "filter-dropdown.lt": "小于等于", "filter-dropdown.date": "日期范围" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/i18n/zh-Hant.json ================================================ { "filter-dropdown.reset": "重置", "filter-dropdown.search": "搜索", "filter-dropdown.submit": "確定", "filter-dropdown.filter": "篩選", "filter-dropdown.gt": "大於等於", "filter-dropdown.lt": "小於等於", "filter-dropdown.date": "日期範圍" } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/package.json ================================================ { "id": "uni-table", "displayName": "uni-table 表格", "version": "1.2.3", "description": "表格组件,多用于展示多条结构类似的数据,如", "keywords": [ "uni-ui", "uniui", "table", "表格" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss","uni-datetime-picker"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "n" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "n", "QQ": "y" }, "快应用": { "华为": "n", "联盟": "n" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-table/readme.md ================================================ ## Table 表单 > 组件名:``uni-table``,代码块: `uTable`。 用于展示多条结构类似的数据 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tag/changelog.md ================================================ ## 2.1.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-tag](https://uniapp.dcloud.io/component/uniui/uni-tag) ## 2.0.0(2021-11-09) - 新增 提供组件设计资源,组件样式调整 - 移除 插槽 - 移除 type 属性的 royal 选项 ## 1.1.1(2021-08-11) - type 不是 default 时,size 为 small 字体大小显示不正确 ## 1.1.0(2021-07-30) - 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.0.7(2021-06-18) - 修复 uni-tag 在字节跳动小程序上 css 类名编译错误的 bug ## 1.0.6(2021-06-04) - 修复 未定义 sass 变量 "$uni-color-royal" 的bug ## 1.0.5(2021-05-10) - 修复 royal 类型无效的bug - 修复 uni-tag 宽度不自适应的bug - 新增 uni-tag 支持属性 custom-style 自定义样式 ## 1.0.4(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tag/components/uni-tag/uni-tag.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tag/package.json ================================================ { "id": "uni-tag", "displayName": "uni-tag 标签", "version": "2.1.0", "description": "Tag 组件,用于展示1个或多个文字标签,可点击切换选中、不选中的状态。", "keywords": [ "uni-ui", "uniui", "", "tag", "标签" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tag/readme.md ================================================ ## Tag 标签 > **组件名:uni-tag** > 代码块: `uTag` 用于展示1个或多个文字标签,可点击切换选中、不选中的状态 。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tag) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-title/changelog.md ================================================ ## 1.1.1(2022-05-19) - 修改组件描述 ## 1.1.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-title](https://uniapp.dcloud.io/component/uniui/uni-title) ## 1.0.2(2021-05-12) - 新增 示例地址 - 修复 示例项目缺少组件的Bug ## 1.0.1(2021-02-05) - 调整为uni_modules目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-title/components/uni-title/uni-title.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-title/package.json ================================================ { "id": "uni-title", "displayName": "uni-title 章节标题", "version": "1.1.1", "description": "章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题", "keywords": [ "uni-ui", "uniui", "标题", "章节", "章节标题", "" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-title/readme.md ================================================ ## Title 标题 > **组件名:uni-title** > 代码块: `uTitle` 章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题 。 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-title) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tooltip/changelog.md ================================================ ## 0.2.1(2022-05-09) - 修复 content 为空时仍然弹出的bug ## 0.2.0(2022-05-07) **注意:破坏性更新** - 更新 text 属性变更为 content - 更新 移除 width 属性 ## 0.1.1(2022-04-27) - 修复 组件根 text 嵌套组件 warning ## 0.1.0(2022-04-21) - 初始化 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tooltip/package.json ================================================ { "id": "uni-tooltip", "displayName": "uni-tooltip", "version": "0.2.1", "description": "Tooltip 提示文字", "keywords": [ "uni-tooltip", "uni-ui", "tooltip", "tip", "文字提示" ], "repository": "", "engines": { }, "dcloudext": { "category": [ "前端组件", "通用组件" ], "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无 ", "data": "无", "permissions": "无" }, "npmurl": "" }, "uni_modules": { "dependencies": [], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "Vue": { "vue2": "y", "vue3": "y" }, "App": { "app-vue": "y", "app-nvue": "u" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "u", "百度": "u", "字节跳动": "u", "QQ": "u" }, "快应用": { "华为": "u", "联盟": "u" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-tooltip/readme.md ================================================ ## Badge 数字角标 > **组件名:uni-tooltip** > 代码块: `uTooltip` 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tooltip) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-transition/changelog.md ================================================ ## 1.3.2(2023-05-04) - 修复 NVUE 平台报错的问题 ## 1.3.1(2021-11-23) - 修复 init 方法初始化问题 ## 1.3.0(2021-11-19) - 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition) ## 1.2.1(2021-09-27) - 修复 init 方法不生效的 Bug ## 1.2.0(2021-07-30) - 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) ## 1.1.1(2021-05-12) - 新增 示例地址 - 修复 示例项目缺少组件的 Bug ## 1.1.0(2021-04-22) - 新增 通过方法自定义动画 - 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 - 优化 动画触发逻辑,使动画更流畅 - 优化 支持单独的动画类型 - 优化 文档示例 ## 1.0.2(2021-02-05) - 调整为 uni_modules 目录规范 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-transition/components/uni-transition/createAnimation.js ================================================ // const defaultOption = { // duration: 300, // timingFunction: 'linear', // delay: 0, // transformOrigin: '50% 50% 0' // } // #ifdef APP-NVUE const nvueAnimation = uni.requireNativePlugin('animation') // #endif class MPAnimation { constructor(options, _this) { this.options = options // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 this.animation = uni.createAnimation({ ...options }) this.currentStepAnimates = {} this.next = 0 this.$ = _this } _nvuePushAnimates(type, args) { let aniObj = this.currentStepAnimates[this.next] let styles = {} if (!aniObj) { styles = { styles: {}, config: {} } } else { styles = aniObj } if (animateTypes1.includes(type)) { if (!styles.styles.transform) { styles.styles.transform = '' } let unit = '' if(type === 'rotate'){ unit = 'deg' } styles.styles.transform += `${type}(${args+unit}) ` } else { styles.styles[type] = `${args}` } this.currentStepAnimates[this.next] = styles } _animateRun(styles = {}, config = {}) { let ref = this.$.$refs['ani'].ref if (!ref) return return new Promise((resolve, reject) => { nvueAnimation.transition(ref, { styles, ...config }, res => { resolve() }) }) } _nvueNextAnimate(animates, step = 0, fn) { let obj = animates[step] if (obj) { let { styles, config } = obj this._animateRun(styles, config).then(() => { step += 1 this._nvueNextAnimate(animates, step, fn) }) } else { this.currentStepAnimates = {} typeof fn === 'function' && fn() this.isEnd = true } } step(config = {}) { // #ifndef APP-NVUE this.animation.step(config) // #endif // #ifdef APP-NVUE this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin this.next++ // #endif return this } run(fn) { // #ifndef APP-NVUE this.$.animationData = this.animation.export() this.$.timer = setTimeout(() => { typeof fn === 'function' && fn() }, this.$.durationTime) // #endif // #ifdef APP-NVUE this.isEnd = false let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref if(!ref) return this._nvueNextAnimate(this.currentStepAnimates, 0, fn) this.next = 0 // #endif } } const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', 'translateZ' ] const animateTypes2 = ['opacity', 'backgroundColor'] const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { MPAnimation.prototype[type] = function(...args) { // #ifndef APP-NVUE this.animation[type](...args) // #endif // #ifdef APP-NVUE this._nvuePushAnimates(type, args) // #endif return this } }) export function createAnimation(option, _this) { if(!_this) return clearTimeout(_this.timer) return new MPAnimation(option, _this) } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-transition/components/uni-transition/uni-transition.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-transition/package.json ================================================ { "id": "uni-transition", "displayName": "uni-transition 过渡动画", "version": "1.3.2", "description": "元素的简单过渡动画", "keywords": [ "uni-ui", "uniui", "动画", "过渡", "过渡动画" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": ["uni-scss"], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-transition/readme.md ================================================ ## Transition 过渡动画 > **组件名:uni-transition** > 代码块: `uTransition` 元素过渡动画 ### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition) #### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-ui/changelog.md ================================================ ## 1.4.27(2023-04-23) - uni-calendar 修复 某些情况 monthSwitch 未触发的Bug - uni-calendar 修复 某些情况切换月份错误的Bug - uni-data-picker 修复 更改 modelValue 报错的 bug - uni-data-picker 修复 v-for 未使用 key 值控制台 warning - uni-data-picker 修复代码合并时引发 value 属性为空时不渲染数据的问题 - uni-data-picker 修复 localdata 不支持动态更新的bug - uni-data-select 修复 微信小程序点击时会改变背景颜色的 bug - uni-data-select 修复 禁用时会显示清空按钮 - uni-data-select 优化 查询条件短期内多次变更只查询最后一次变更后的结果 - uni-data-select 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue - uni-datetime-picker 修复 日历 picker 修改年月后,自动选中当月1日 [详情](https://ask.dcloud.net.cn/question/165937) - uni-datetime-picker 修复 小程序端 低版本 ios NaN [详情](https://ask.dcloud.net.cn/question/162979) - uni-datetime-picker 修复 firefox 浏览器显示区域点击无法拉起日历弹框的Bug [详情](https://ask.dcloud.net.cn/question/163362) - uni-datetime-picker 优化 值为空依然选中当天问题 - uni-datetime-picker 优化 提供 default-value 属性支持配置选择器打开时默认显示的时间 - uni-datetime-picker 优化 非范围选择未选择日期时间,点击确认按钮选中当前日期时间 - uni-datetime-picker 优化 字节小程序日期时间范围选择,底部日期换行问题 - uni-datetime-picker 修复 2.2.18 引起范围选择配置 end 选择无效的Bug [详情](https://github.com/dcloudio/uni-ui/issues/686) - uni-datetime-picker 修复 移动端范围选择change事件触发异常的Bug [详情](https://github.com/dcloudio/uni-ui/issues/684) - uni-datetime-picker 优化 PC端输入日期格式错误时返回当前日期时间 - uni-datetime-picker 优化 PC端输入日期时间超出 start、end 限制的Bug - uni-datetime-picker 优化 移动端日期时间范围用法时间展示不完整问题 - uni-datetime-picker 修复 小程序端绑定 Date 类型报错的Bug [详情](https://github.com/dcloudio/uni-ui/issues/679) - uni-datetime-picker 修复 vue3 time-picker 无法显示绑定时分秒的Bug - uni-datetime-picker 修复 字节小程序报错的Bug - uni-datetime-picker 修复 某些情况切换月份错误的Bug - uni-easyinput 修复 vue3 下 keyboardheightchange 事件报错的bug - uni-easyinput 优化 trim 属性默认值 - uni-easyinput 新增 cursor-spacing 属性 - uni-fab 新增 pattern.icon 属性,可自定义图标 - uni-file-picker 修复 手动上传删除一个文件后不能再上传的bug - uni-forms 修复 required 参数无法动态绑定 - uni-list 优化 uni-list-chat 具名插槽`header` 非app端套一层元素,方便使用时通过外层元素定位实现样式修改 - uni-list uni-list-chat 新增 支持具名插槽`header` - uni-list 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props) - uni-nav-bar 修复 自定义状态栏高度闪动BUG - uni-nav-bar 修复 暗黑模式下边线颜色错误的bug - uni-popup 修复 uni-popup 重复打开时的 bug - uni-popup uni-popup-dialog 组件新增 inputType 属性 - uni-swipe-action 修复`uni-swipe-action`和`uni-swipe-action-item`不同时使用导致 closeOther 方法报错的 bug - uni-table 修复 在vue3模式下可能会出现错误的问题 ## 1.4.26(2023-01-31) - uni-badge 修复 运行/打包 控制台警告问题 - uni-calendar 修复 某些情况切换月份错误问题 - uni-data-select 修复 不关联服务空间报错的问题 - uni-data-select 新增 属性 `format` 可用于格式化显示选项内容 - uni-datetime-picker 修复 某些情况切换月份错误问题 - uni-easyinput 新增 keyboardheightchange 事件,可监听键盘高度变化 - uni-list 修复 无反馈效果呈现的bug ## 1.4.25(2023-01-11) - uni-file-picker 新增 sourceType 属性, 可以自定义图片和视频选择的来源 ## 1.4.24(2023-01-11) - uni-data-select 修复 当where变化时,数据不会自动更新的问题 - uni-datetime-picker 修复 多次加载组件造成内存占用的 bug - uni-datetime-picker 修复 vue3 下 i18n 国际化初始值不正确的 bug - uni-easyinput 修复 props 中背景颜色无默认值的bug - uni-list 修复 uni-list-chat 在vue3下跳转报错的bug - uni-list 修复 uni-list-chat avatar属性 值为本地路径时错误的问题 - uni-list 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题 - uni-list 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题 - uni-list 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug - uni-list 修复 uni-list-item 的 customStyle 属性 padding值在nvue(vue2)下无效的bug - uni-list uni-list-chat 新增 avatar 支持 fileId - uni-list uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html) - uni-list uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im:[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im) - uni-list uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor - uni-popup 修复 nvue 下 v-show 报错 ## 1.4.23(2022-10-25) - uni-datetime-picker 修复,支付宝小程序样式错乱,[详情](https://github.com/dcloudio/uni-app/issues/3861) - uni-nav-bar 修复 条件编译错误的bug - uni-nav-bar 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug ## 1.4.22(2022-09-19) - 优化 部分组件适配 uni-scss 主题色 - uni-badge 修复 当 text 超过 max-num 时,badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473) - uni-calendar 修复 表头年月切换,导致改变当前日期为选择月1号,且未触发change事件 - uni-data-select 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 - uni-data-select 修复 点击的位置不准确 - uni-data-select 新增 支持 disabled 属性 - uni-datetime-picker 修复,反向选择日期范围,日期显示异常,[详情](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false) - uni-datetime-picker 修复 close事件无效的 bug - uni-datetime-picker 修复 移动端 maskClick 无效的 bug,详见:[https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false](https://ask.dcloud.net.cn/question/140824?item_id=209458&rf=false) - uni-fab 修复 小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) - uni-fab 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) - uni-forms 优化 根据 rules 自动添加 required 的问题 - uni-forms 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) - uni-nav-bar 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug - uni-notice-bar 新增 属性 fontSize,可修改文字大小。 - uni-pagination 修复,未对主题色设置默认色,导致未引入 uni-scss 变量文件报错。 - uni-pagination 修复,未对移动端当前页文字做主题色适配。 - uni-pagination 修复 es 语言 i18n 错误 ## 1.4.21(2022-09-19) - 修复,安装时未导入 uni-data-select 和 uni-tooltip 的问题。 ## 1.4.20(2022-07-25) - uni-section 新增组件 - uni-forms 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug ## 1.4.19(2022-07-07) - uni-data-picker 优化 pc端图标位置不正确的问题 - uni-data-select 修复 pc端宽度异常的bug ## 1.4.18(2022-07-06) - uni-forms 【重要】组件逻辑重构,部分用法旧版本不兼容,请注意兼容问题 - uni-forms 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 - uni-forms 新增 更多表单示例 - uni-forms 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 - uni-forms 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 - uni-forms 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 - uni-forms 新增 子表单的 setRules 方法,配合自定义校验函数使用 - uni-forms 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 - uni-forms 修复 由 1.4.0 引发的 label 插槽不生效的bug - uni-forms 修复 子组件找不到 setValue 报错的bug - uni-forms 修复 uni-data-picker 在 uni-forms-item 中报错的bug - uni-forms 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug - uni-forms 修复 表单校验顺序无序问题 - uni-forms 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 - uni-forms 优化 动态表单校验方式,废弃拼接name的方式 - uni-breadcrumb 修复 微信小程序 separator 不显示问题 - uni-data-checkbox 优化 在 uni-forms 中的依赖注入方式 - uni-data-picker 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug - uni-data-picker 优化 显示样式 - uni-data-select 优化 显示样式 - uni-datetime-picker 修复 日历顶部年月及底部确认未国际化 bug - uni-datetime-picker 优化 组件样式,调整了组件图标大小、高度、颜色等,与uni-ui风格保持一致 - uni-easyinput 新增 在 uni-forms 1.4.0 中使用可以在 blur 时校验内容 - uni-easyinput 新增 clear 事件,点击右侧叉号图标触发 - uni-easyinput 新增 change 事件 ,仅在输入框失去焦点或用户按下回车时触发 - uni-easyinput 优化 组件样式,组件获取焦点时高亮显示,图标颜色调整等 - uni-easyinput 优化 clearable 显示策略 - uni-file-picker 修复 在uni-forms下样式不生效的bug - uni-nav-bar 修复 组件示例中插槽用法无法显示内容的bug - uni-swipe-action 修复 vue3 下使用组件不能正常运行的Bug - uni-swipe-action 修复 h5端点击click触发两次的Bug - uni-table 修复 微信小程序存在无使用组件的问题 ## 1.4.17(2022-06-30) - 支持 ios 安全区 ## 1.4.16(2022-06-06) - uni-breadcrumb 新增 支持 uni.scss 修改颜色 - uni-data-select 修复 localdata 赋值不生效的 bug - uni-data-select 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) - uni-data-select 修复 当 value 为 0 时选择不生效的 bug - uni-easyinput 修复 关闭图标某些情况下无法取消的bug - uni-fav 新增 stat 属性 ,是否开启uni统计功能 - uni-goods-nav 新增 stat属性,是否开启uni统计功能 - uni-group 新增 stat属性,是否开启uni统计功能 - uni-nav-bar 新增 stat 属性 ,可开启统计 title 上报 ,仅使用了title 属性且项目开启了uni统计生效 - uni-search-bar 新增 readonly 属性,组件只读 - uni-swipe-action 修复 isPC 找不到的Bug - uni-swipe-action 修复 在 nvue 下 disabled 失效的bug - uni-tooltip 修复 content 为空时仍然弹出的bug ## 1.4.15(2022-05-07) - uni-data-picker 修复 字节小程序 本地数据无法选择下一级的Bug - uni-data-select 新增 记住上次的选项(仅 collection 存在时有效) - uni-search-bar 修复 vue3 input 事件不生效的bug - uni-search-bar 修复 多余代码导致的bug - uni-tooltip 更新 text 属性变更为 content - uni-tooltip 更新 移除 width 属性 - uni-tooltip 修复 组件根 text 嵌套组件 warning ## 1.4.14(2022-04-18) - uni-datetime-picker 修复 Vue3 下动态赋值,单选类型未响应的 bug - uni-easyinput 修复 默认值不生效的bug ## 1.4.13(2022-04-02) - uni-calendar 修复 条件编译 nvue 不支持的 css 样式 - uni-calendar 修复 startDate、 endDate 属性失效的 bug - uni-data-picker 修复 nvue 不支持的 v-show 的 bug - uni-data-picker 修复 条件编译 nvue 不支持的 css 样式 - uni-datetime-picker 修复 Vue3 下动态赋值未响应的 bug - uni-easyinput 修复 value不能为0的bug - uni-popup 修复 弹出层内部无法滚动的bug - uni-popup 修复 小程序中高度错误的bug - uni-popup 修复 快速调用open出现问题的Bug - uni-rate 修复 条件判断 `NaN` 错误的 bug - uni-swipe-action 修复 按钮字体大小不能设置的bug - uni-swipe-action 修复 h5和app端下报el错误的bug - uni-swipe-action 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug ## 1.4.12(2022-02-19) - uni-collapse 修复 初始化的时候 ,open 属性失效的bug - uni-data-checkbox 修复 multiple 为 true 时,v-model 的值为 null 报错的 bug - uni-icons 优化 size 属性可以传入不带单位的字符串数值 - uni-icons 优化 size 支持其他单位 - uni-nav-bar 新增 left-width/right-width属性 ,可修改左右两侧的宽度 - uni-popup 修复 safeArea 属性不能设置为false的bug ## 1.4.11(2022-01-21) - uni-collapse 修复 微信小程序resize后组件收起的bug - uni-countdown 修复 在微信小程序中样式不生效的bug - uni-countdown 新增 update 方法 ,在动态更新时间后,刷新组件 - uni-load-more 新增 showText属性 ,是否显示文本 - uni-load-more 修复 nvue 平台下不显示文本的bug - uni-load-more 修复 微信小程序平台样式选择器报警告的问题 - uni-nav-bar 修复 在vue下,标题不垂直居中的bug - uni-nav-bar 修复 height 属性类型错误 - uni-nav-bar 新增 height 属性,可修改组件高度 - uni-nav-bar 新增 dark 属性可可开启暗黑模式 - uni-nav-bar 优化 标题字数过多显示省略号 - uni-nav-bar 优化 插槽,插入内容可完全覆盖 - uni-popup 修复 isMaskClick 失效的bug - uni-popup 新增 cancelText \ confirmText 属性 ,可自定义文本 - uni-popup 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 - uni-popup 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 ## 1.4.10(2022-01-17) - uni-card 修复 在vue页面下略缩图显示不正常的bug - uni-datetime-picker 修复 clear-icon 属性在小程序平台不生效的 bug - uni-datetime-picker 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug - uni-fab 更新 组件依赖 - - uni-icons 修复 nvue 有些图标不显示的bug,兼容老版本图标 - uni-icons 优化 示例可复制图标名称 - uni-nav-bar 修复 color 属性不生效的bug - uni-popup 修复 设置 safeArea 属性不生效的bug - uni-popup 优化 组件示例 - uni-popup 修复 vuedoc 文字错误 ## 1.4.9(2021-11-23) - uni-ui 修复 vue3中某些scss变量无法找到的问题 - uni-combox 优化 label、label-width 属性 - uni-data-picker 修复 由上个版本引发的map、v-model等属性不生效的bug - uni-file-picker 修复 参数为对象的情况下,url在某些情况显示错误的bug - uni-icons 优化 兼容旧组件 type 值 - uni-list 修复 在vue3中to属性在发行应用的时候报错的bug - uni-scss 修复 vue3中scss语法兼容问题 - uni-transition 修复 init 方法初始化问题 ## 1.4.8(2021-11-19) - uni-fab 修复 阴影颜色不正确的bug ## 1.4.7(2021-11-19) - uni-ui 新增 支持国际化 - uni-ui 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) - uni-ui 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-ui](https://uniapp.dcloud.io/component/uniui/uni-ui) - uni-badge 修改 size 属性默认值调整为 small - uni-badge 修改 type 属性,默认值调整为 error,info 替换 default - uni-badge 修复 在字节小程序上样式不生效的 bug - uni-calendar 修复 弹出层被 tabbar 遮盖 bug - uni-card 重构插槽的用法 ,header 替换为 title - uni-card 新增 actions 插槽 - uni-card 新增 cover 封面图属性和插槽 - uni-card 新增 padding 内容默认内边距离 - uni-card 新增 margin 卡片默认外边距离 - uni-card 新增 spacing 卡片默认内边距 - uni-card 新增 shadow 卡片阴影属性 - uni-card 取消 mode 属性,可使用组合插槽代替 - uni-card 取消 note 属性 ,使用actions插槽代替 - uni-collapse 优化 show-arrow 属性默认为true - uni-collapse 新增 show-arrow 属性,控制是否显示右侧箭头 - uni-countdown 新增 font-size 支持自定义字体大小 - uni-data-checkbox 修复 在uni-forms中 modelValue 中不存在当前字段,当前字段必填写也不参与校验的问题 - uni-data-checkbox 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 - uni-data-checkbox 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 - uni-dateformat 优化 默认时间不再是当前时间,而是显示'-'字符 - uni-datetime-picker 修复 hide-second 在移动端的 bug - uni-datetime-picker 修复 单选赋默认值时,赋值日期未高亮的 bug - uni-datetime-picker 修复 赋默认值时,移动端未正确显示时间的 bug - uni-datetime-picker 新增 hide-second 属性,支持只使用时分,隐藏秒 - uni-datetime-picker 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次 - uni-datetime-picker 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法 - uni-datetime-picker 优化 调整字号大小,美化日历界面 - uni-datetime-picker 优化 范围选择器在 pc 端过宽的问题 - uni-datetime-picker 新增 支持作为 uni-forms 子组件相关功能 - uni-datetime-picker 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug - uni-datetime-picker 修复 type 属性动态赋值无效的 bug - uni-datetime-picker 修复 ‘确认’按钮被 tabbar 遮盖 bug - uni-datetime-picker 修复 组件未赋值时范围选左、右日历相同的 bug - uni-datetime-picker 修复 范围选未正确显示当前值的 bug - uni-datetime-picker 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug - uni-easyinput 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug - uni-easyinput 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 - uni-file-picker 新增 参数中返回 fileID 字段 - uni-file-picker 修复 腾讯云传入fileID 不能回显的bug - uni-file-picker 修复 选择图片后,不能放大的问题 - uni-file-picker 修复 由于 0.2.11 版本引起的不能回显图片的Bug - uni-file-picker 新增 clearFiles(index) 方法,可以手动删除指定文件 - uni-file-picker 修复 v-model 值设为 null 报错的Bug - uni-file-picker 修复 return-type="object" 时,无法删除文件的Bug - uni-file-picker 修复 auto-upload 属性失效的Bug - uni-forms 修复 label 插槽不生效的bug - uni-forms 修复 没有添加校验规则的字段依然报错的Bug - uni-forms 修复 重置表单错误信息无法清除的问题 - uni-forms 修复 表单验证只生效一次的问题 - uni-icons 新增 更多图标 - uni-icons 优化 自定义图标使用方式 - uni-link 修复 在 nvue 下不显示的 bug - uni-pagination 修复 current 、value 属性未监听,导致高亮样式失效的 bug - uni-rate 优化 默认值修改为 0 颗星 - uni-search-bar 修复 value 属性与 modelValue 属性不兼容的Bug - uni-swipe-action 新增 close-all 方法,关闭所有已打开的组件 - uni-swipe-action 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 - uni-swipe-action 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 - uni-swipe-action 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 - uni-tag 新增 提供组件设计资源,组件样式调整 - uni-tag 移除 插槽 - uni-tag 移除 type 属性的 royal 选项 - uni-tag type 不是 default 时,size 为 small 字体大小显示不正确 ## 1.4.2(2021-08-20) - 新增 uni-ui 组件支持国际化 i18n - uni-collapse 优化 show-arrow 属性默认为true - uni-collapse 新增 show-arrow 属性,控制是否显示右侧箭头 - uni-data-checkbox 修复 单选 list 模式下 ,icon 为 left 时,选中图标不显示的问题 - uni-easyinput 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug - uni-file-picker 修复 由于 0.2.11 版本引起的不能回显图片的Bug - uni-file-picker 新增 clearFiles(index) 方法,可以手动删除指定文件 - uni-file-picker 修复 v-model 值设为 null 报错的Bug - uni-swipe-action 新增 close-all 方法,关闭所有已打开的组件 - uni-swipe-action 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 - uni-swipe-action 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 - uni-swipe-action 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 ## 1.4.0(2021-08-13) - uni-calendar 修复 弹出层被 tabbar 遮盖 bug - uni-data-checkbox 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 - uni-dateformat 调整 默认时间不再是当前时间,而是显示'-'字符 - uni-datetime-picker 新增 适配 vue3 - uni-datetime-picker 新增 支持作为 uni-forms 子组件相关功能 - uni-datetime-picker 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug - uni-datetime-picker 修复 type 属性动态赋值无效的 bug - uni-datetime-picker 修复 ‘确认’按钮被 tabbar 遮盖 bug - uni-datetime-picker 修复 组件未赋值时范围选左、右日历相同的 bug - uni-datetime-picker 修复 范围选未正确显示当前值的 bug - uni-datetime-picker 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug - uni-easyinput 修复 在 uni-forms 中重置表单,错误信息无法清除的问题 - uni-file-picker 修复 return-type="object" 时,无法删除文件的Bug - uni-file-picker 修复 auto-upload 属性失效的Bug - uni-forms 修复 没有添加校验规则的字段依然报错的Bug - uni-forms 修复 重置表单错误信息无法清除的问题 - uni-forms 优化 组件文档 - uni-forms 修复 表单验证只生效一次的问题 - uni-tag type 不是 default 时,size 为 small 字体大小显示不正确 ## 1.3.9(2021-08-02) - uni-datetime-picker 新增 return-type 属性支持返回 date 日期对象 - uni-file-picker 修复 fileExtname属性不指定值报错的Bug - uni-file-picker 修复 在某种场景下图片不回显的Bug - uni-link 支持自定义插槽 ## 1.3.8(2021-07-31) - uni-ui 组件兼容 vue3 - uni-collapse 修复 由1.2.0版本引起的 change 事件返回 undefined 的Bug - uni-collapse 优化 组件示例 - uni-collapse 新增 组件折叠动画 - uni-collapse 新增 value\v-model 属性 ,动态修改面板折叠状态 - uni-collapse 新增 title 插槽 ,可定义面板标题 - uni-collapse 新增 border 属性 ,显示隐藏面板内容分隔线 - uni-collapse 新增 title-border 属性 ,显示隐藏面板标题分隔线 - uni-collapse 修复 resize 方法失效的Bug - uni-collapse 修复 change 事件返回参数不正确的Bug - uni-collapse 优化 H5、App 平台自动更具内容更新高度,无需调用 reszie() 方法 - uni-data-checkbox 优化 在uni-forms组件,与label不对齐的问题 - uni-data-checkbox 修复 单选默认值为0不能选中的Bug - uni-easyinput 优化 errorMessage 属性支持 Boolean 类型 - uni-file-picker 修复 return-type为object下,返回值不正确的Bug - uni-file-picker 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 - uni-file-picker 优化 h5平台下上传文件导致页面卡死的问题 - uni-forms 修复 vue2 下条件编译导致destroyed生命周期失效的Bug - uni-forms 修复 1.2.1 引起的示例在小程序平台报错的Bug - uni-forms 修复 动态校验表单,默认值为空的情况下校验失效的Bug - uni-forms 修复 不指定name属性时,运行报错的Bug - uni-forms 优化 label默认宽度从65调整至70,使required为true且四字时不换行 - uni-forms 优化 组件示例,新增动态校验示例代码 - uni-forms 优化 组件文档,使用方式更清晰 - uni-list 修复 与其他组件嵌套使用时,点击失效的Bug - uni-swipe-action 修复 跨页面修改组件数据 ,导致不能滑动的问题 ## 1.3.7(2021-07-16) - uni-ui 兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) - uni-datetime-picker 修复 单选日期类型,初始赋值后不在当前日历的 bug - uni-datetime-picker 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效) - uni-datetime-picker 优化 移动端移除显示框的清空按钮,无实际用途 - uni-datetime-picker 修复 组件赋值为空,界面未更新的 bug - uni-datetime-picker 修复 start 和 end 不能动态赋值的 bug - uni-datetime-picker 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug ## 1.3.6(2021-07-09) - uni-data-checkbox 优化 删除无用日志 - uni-data-checkbox 修复 由 0.1.9 引起的非 nvue 端图标不显示的问题 - uni-data-checkbox 修复 nvue 黑框样式问题 - uni-datetime-picker 修复 范围选择不能动态赋值的 bug - uni-datetime-picker 修复 范围选择的初始时间在一个月内时,造成无法选择的bug - uni-datetime-picker 优化 弹出层在超出视窗边缘定位不准确的问题 - uni-datetime-picker 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug - uni-datetime-picker 优化 弹出层在超出视窗边缘被遮盖的问题 - uni-datetime-picker 新增 maskClick 事件 - uni-datetime-picker 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px - uni-datetime-picker 修复 范围选择时清空返回值不合理的bug,['', ''] -> [] - uni-datetime-picker 新增 日期时间显示框支持插槽 - uni-file-picker 修复 sourceType 缺少默认值导致 ios 无法选择文件 - uni-file-picker 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 - uni-table 新增 uni-th 支持 date 日期筛选范围 - uni-table 新增 uni-th 支持 range 筛选范围 - uni-table 新增 uni-th 筛选功能 ## 1.3.5(2021-07-02) - uni-card 优化 图文卡片无图片加载时,提供占位图标 - uni-card 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持) - uni-card 修复 thumbnail 不存在仍然占位的 bug - uni-data-checkbox 修复 selectedTextColor 属性不生效的Bug - uni-datetime-picker 优化 添加 uni-icons 依赖 - uni-easyinput 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug - uni-file-picker 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 - uni-file-picker 优化 文件上传后进度条消失时机 - uni-file-picker 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug - uni-forms 修复 pattern 属性在微信小程序平台无效的问题 ## 1.3.4(2021-06-25) - uni-badge 优化 示例项目 - uni-countdown 修复 uni-countdown 重复赋值跳两秒的 bug - uni-easyinput 修复 passwordIcon 属性拼写错误的 bug - uni-forms 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug - uni-forms 修复 只写setRules方法而导致校验不生效的Bug - uni-forms 修复 由上个办法引发的错误提示文字错位的Bug - uni-forms 修复 不设置 label 属性 ,无法设置label插槽的问题 - uni-forms 修复 不设置label属性,label-width属性不生效的bug - uni-forms 修复 setRules 方法与rules属性冲突的问题 - uni-link 新增 download 属性,H5平台下载文件名 - uni-popup 新增 mask-click 遮罩层点击事件 - uni-popup 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug - uni-tag 修复 uni-tag 在字节跳动小程序上 css 类名编译错误的 bug ## 1.3.3(2021-06-18) - uni-easyinput 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标 - uni-easyinput 修复 confirmType 属性不生效的问题 - uni-easyinput 修复 disabled 状态可清出内容的 bug - uni-file-picker 修复 删除文件时无法触发 v-model 的Bug - uni-popup 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug - uni-popup 修复 错误的 watch 字段 - uni-popup 修复 safeArea 属性不生效的问题 - uni-popup 修复 点击内容,再点击遮罩无法关闭的Bug ## 1.3.2(2021-06-04) - uni-data-checkbox 新增 map 属性,可以方便映射text/value属性 - uni-data-checkbox 修复 不关联服务空间的情况下组件报错的Bug - uni-data-picker 修复 上个版本引出的本地数据无法选择带有children的2级节点 - uni-forms 修复 动态删减数据导致报错的问题 - uni-forms 新增 modelValue 属性 ,value 即将废弃 - uni-forms 新增 uni-forms-item 可以设置单独的 rules - uni-forms 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 - uni-forms 优化 submit 事件重命名为 validate - uni-data-picker 修复 无法加载云端数据的问题 - uni-data-picker 修复 v-model无效问题 - uni-data-picker 修复 loaddata 为空数据组时加载时间过长问题 - uni-datetime-picker 修复 图标在小程序上不显示的 bug - uni-datetime-picker 优化 重命名引用组件,避免潜在组件命名冲突 - uni-datetime-picker 优化 代码目录扁平化 - uni-tag 修复 未定义 sass 变量 "$uni-color-royal" 的bug ## 1.3.1(2021-05-14) - uni-badge 新增 uni-badge 的 absolute 属性,支持定位 - uni-badge 新增 uni-badge 的 offset 属性,支持定位偏移 - uni-badge 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点 - uni-badge 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+ - uni-badge 优化 uni-badge 属性 custom-style, 支持以对象形式自定义样式 - uni-badge 修复 uni-badge 在 App 端,数字小于10时不是圆形的bug - uni-badge 修复 uni-badge 在父元素不是 flex 布局时,宽度缩小的bug - uni-badge 新增 uni-badge 属性 custom-style, 支持自定义样式 - uni-datetime-picker 修复 ios 下不识别 '-' 日期格式的 bug - uni-datetime-picker 优化 pc 下弹出层添加边框和阴影 - uni-datetime-picker 修复 在 admin 中获取弹出层定位错误的bug - uni-datetime-picker 修复 type 属性向下兼容,默认值从 date 变更为 datetime - uni-datetime-picker 支持日历形式的日期+时间的范围选择 - uni-steps 修复 uni-steps 横向布局时,多行文字高度不合理的 bug - uni-countdown 修复 uni-countdown 不能控制倒计时的 bug - uni-tag 修复 royal 类型无效的bug - uni-tag 修复 uni-tag 宽度不自适应的bug - uni-tag 新增 uni-tag 支持属性 custom-style 自定义样式 - uni-link 新增 href 属性支持 tel:|mailto: - uni-popup 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 - uni-popup 新增 type 属性的 left\right 值,支持左右弹出 - uni-popup 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 - uni-popup 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 - uni-popup 新增 safeArea 属性,是否适配底部安全区 - uni-popup 修复 App\h5\微信小程序底部安全区占位不对的Bug - uni-popup 修复 App 端弹出等待的Bug - uni-popup 优化 提升低配设备性能,优化动画卡顿问题 - uni-popup 优化 更简单的组件自定义方式 - uni-table 修复 示例项目缺少组件的Bug - uni-forms 修复 自定义检验器失效的问题 - uni-title 修复 示例项目缺少组件的Bug - uni-transition 修复 示例项目缺少组件的Bug - uni-swiper-dot 修复 示例项目缺少组件的Bug - uni-ui 新增 组件示例地址 ## 1.3.0(2021-04-23) - uni-combox 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-data-picker 修复 非树形数据有 where 属性查询报错的问题 - uni-fav 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-goods-nav 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-nav-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-notice-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-number-box 修复 uni-number-box 浮点数运算不精确的 bug - uni-number-box 修复 uni-number-box change 事件触发不正确的 bug - uni-number-box 新增 uni-number-box v-model 双向绑定 - uni-rate 修复 布局变化后 uni-rate 星星计算不准确的 bug - uni-rate 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 - uni-search-bar 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-steps 优化 添加依赖 uni-icons, 导入后自动下载依赖 - uni-transition 新增 通过方法自定义动画 - uni-transition 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 - uni-transition 优化 动画触发逻辑,使动画更流畅 - uni-transition 优化 支持单独的动画类型 - uni-transition 优化 文档示例 ## 1.2.13(2021-04-16) - uni-ui 新增 uni-data-picker 支持云端非树形表结构数据 - uni-ui 修复 uni-data-checkbox nvue 下无法选中的问题 - uni-ui 修复 uni-data-picker 根节点 parent_field 字段等于null时选择界面错乱问题 - uni-ui 修复 uni-file-picker 选择的文件非 file-extname 字段指定的扩展名报错的Bug - uni-ui 修复 uni-swipe-action 报错 nv_navigator is not defined 的bug - uni-ui 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug - uni-ui 优化 uni-file-picker file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 - uni-ui 优化 uni-pagination PC 和 移动端适配不同的 ui - uni-ui 更新 uni-file-picker 组件示例 - uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug - uni-ui 新增 uni-search-bar 的 focus 事件 - uni-ui 修复 uni-rate 属性 margin 值为 string 组件失效的 bug - uni-data-picker 修复 本地数据概率无法回显时问题 - uni-table 新增 sortable 属性,是否开启单列排序 - uni-table 优化 表格多选逻辑 ## 1.2.12(2021-03-23) - uni-ui 新增 uni-datetime-picker 的 hide-second 属性、border 属性; - uni-ui 修复 uni-datetime-picker 选择跟显示的日期不一样的 bug, - uni-ui 修复 uni-datetime-picker change事件触发2次的 bug - uni-ui 修复 uni-datetime-picker 分、秒 end 范围错误的 bug - uni-ui 新增 uni-tr selectable 属性,用于 type=selection 时,设置某行是否可由全选按钮控制 - uni-ui 新增 uni-data-checkbox 新增 disabled属性,支持nvue - uni-ui 优化 uni-data-checkbox 无选项时提示“暂无数据” - uni-ui 优化 uni-data-checkbox 默认颜色显示 - uni-ui 新增 uni-link href 属性支持 tel:|mailto: - uni-ui 新增 uni-table 示例demo - uni-ui 修复 uni-data-picker 微信小程序某些情况下无法选择的问题,事件无法触发的问题 - uni-ui 修复 uni-nav-bar easycom 下,找不到 uni-status-bar 的bug - uni-ui 修复 uni-easyinput 示例在 qq 小程序上的bug - uni-ui 修复 uni-forms 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug - uni-ui 调整 cli 项目 建议使用 easycom 方式引用组件,如使用按需引用,需手动维护组件内部引用 ## 1.2.11(2021-02-24) - 调整为uni_modules目录规范 - uni-data-picker 新增 数据驱动的picker选择器 - uni-file-picker 新增 文件选择上传 - uni-row 新增 栅格系统 - uni-data-checkbox 优化 支持 nvue - uni-forms 修复 偶发性获取表单值错误的Bug - uni-forms 修复 校验 uni-data-picker value 为 0 时,返回值错误的Bug - uni-forms 修复 uni-forms-item 组件隐藏时依然触发校验的bug - uni-forms 优化 实时校验 - uni-forms 优化 兼容nvue页面 - uni-easyinput 优化 兼容nvue页面 - uni-group 优化 兼容nvue页面 - uni-popup 优化 组件适配 PC - uni-fab 优化 适配 PC - uni-swiper-dot 优化 适配 PC - uni-rate 优化 适配 PC - uni-notice-bar 优化 适配 PC - uni-indexed-list 优化 适配 PC - uni-combox 优化 适配 PC - uni-transition 优化 适配 PC - uni-nav-bar 优化 适配 PC - uni-swipe-action 优化 适配 PC ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-ui/components/uni-ui/uni-ui.vue ================================================ ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-ui/package.json ================================================ { "id": "uni-ui", "displayName": "uni-ui", "version": "1.4.27", "description": "uni-ui 是基于uni-app的、全端兼容的、高性能UI框架", "keywords": [ "uni-ui", "uniui", "UI组件库", "ui框架", "ui库" ], "repository": "https://github.com/dcloudio/uni-ui", "engines": { "HBuilderX": "^3.2.10" }, "directories": { "example": "../../temps/example_temps" }, "dcloudext": { "sale": { "regular": { "price": "0.00" }, "sourcecode": { "price": "0.00" } }, "contact": { "qq": "" }, "declaration": { "ads": "无", "data": "无", "permissions": "无" }, "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", "type": "component-vue" }, "uni_modules": { "dependencies": [ "uni-badge", "uni-calendar", "uni-card", "uni-collapse", "uni-combox", "uni-countdown", "uni-data-checkbox", "uni-data-picker", "uni-data-select", "uni-dateformat", "uni-datetime-picker", "uni-drawer", "uni-easyinput", "uni-fab", "uni-fav", "uni-file-picker", "uni-forms", "uni-goods-nav", "uni-grid", "uni-group", "uni-icons", "uni-indexed-list", "uni-link", "uni-list", "uni-load-more", "uni-nav-bar", "uni-notice-bar", "uni-number-box", "uni-pagination", "uni-popup", "uni-rate", "uni-row", "uni-search-bar", "uni-section", "uni-segmented-control", "uni-steps", "uni-swipe-action", "uni-swiper-dot", "uni-table", "uni-tag", "uni-title", "uni-tooltip", "uni-transition" ], "encrypt": [], "platforms": { "cloud": { "tcb": "y", "aliyun": "y" }, "client": { "App": { "app-vue": "y", "app-nvue": "y" }, "H5-mobile": { "Safari": "y", "Android Browser": "y", "微信浏览器(Android)": "y", "QQ浏览器(Android)": "y" }, "H5-pc": { "Chrome": "y", "IE": "y", "Edge": "y", "Firefox": "y", "Safari": "y" }, "小程序": { "微信": "y", "阿里": "y", "百度": "y", "字节跳动": "y", "QQ": "y", "京东": "u" }, "快应用": { "华为": "u", "联盟": "u" }, "Vue": { "vue2": "y", "vue3": "y" } } } } } ================================================ FILE: talkieai-uniapp/src/uni_modules/uni-ui/readme.md ================================================ > 当前插件不包含示例页面 ,如需示例请在 HBuiderX 中新建 `hello uni-app > 扩展组件` 中查看 > > 代码示例地址 :[https://ext.dcloud.net.cn/plugin?id=4941](https://ext.dcloud.net.cn/plugin?id=4941) > > 组件演示地址:[https://hellouniapp.dcloud.net.cn](https://hellouniapp.dcloud.net.cn/pages/extUI/badge/badge) > > 组件文档地址:[https://uniapp.dcloud.io/component/uniui/uni-ui](https://uniapp.dcloud.io/component/uniui/uni-ui) # uni-ui 介绍 ## uni-ui产品特点 ### 1. 高性能 目前为止,在小程序和混合app领域,暂时还没有比 `uni-ui` 更高性能的框架。 - 自动差量更新数据 虽然uni-app支持小程序自定义组件,所有小程序的ui库都可以用。但小程序自定义组件的ui库都需要使用setData手动更新数据,在大数据量时、或高频更新数据时,很容易产生性能问题。 而 `uni-ui` 属于vue组件,uni-app引擎底层自动diff更新数据。当然其实插件市场里众多vue组件都具备这个特点。 - 优化逻辑层和视图层通讯折损 非H5,不管是小程序还是App,不管是app的webview渲染还是原生渲染,全都是逻辑层和视图层分离的。这里就有一个逻辑层和视图层通讯的折损问题。 比如在视图层拖动一个可跟手的组件,由于通讯的损耗,用js监听很难做到实时跟手。 这时就需要使用css动画以及平台底层提供的wxs、bindingx等技术。不过这些技术都比较复杂,所以 `uni-ui` 里做了封装,在需要跟手式操作的ui组件,比如swiperaction列表项左滑菜单,就在底层使用了这些技术,实现了高性能的交互体验 - 背景停止 很多ui组件是会一直动的,比如轮播图、跑马灯。即便这个窗体被新窗体挡住,它在背景层仍然在消耗着硬件资源。在Android的webview版本为chrome66以上,背景操作ui会引发很严重的性能问题,造成前台界面明显卡顿。 而 `uni-ui` 的组件,会自动判断自己的显示状态,在组件不再可见时,不会再消耗硬件资源。 ### 2. 全端 `uni-ui` 的组件都是多端自适应的,底层会抹平很多小程序平台的差异或bug。 比如导航栏navbar组件,会自动处理不同端的状态栏。 比如swiperaction组件,在app和微信小程序上会使用交互体验更好的wxs技术,但在不支持wxs的其他小程序端会使用js模拟类似效果。 `uni-ui` 还支持nvue原生渲染,[详见](https://github.com/dcloudio/uni-ui/tree/nvue-uni-ui) 未来 `uni-ui` 还会支持pc等大屏设备。 ### 3. 与uni统计自动集成实现免打点 uni统计是优秀的多端统计平台,见[tongji.dcloud.net.cn](https://tongji.dcloud.net.cn)。 除了一张报表看全端,它的另一个重要特点是免打点。 比如使用 `uni-ui` 的navbar标题栏、收藏、购物车等组件,均可实现自动打点,统计页面标题等各种行为数据。 当然你也可以关闭uni统计,这不是强制的。 ### 4. 主题扩展 `uni-ui` 支持[uni.scss](https://uniapp.dcloud.io/collocation/uni-scss),可以方便的切换App的风格。 ui是一种需求非常发散的产品,DCloud官方也无意用 `uni-ui` 压制第三方ui插件的空间,但官方有义务在性能和多端方面提供一个开源的标杆给大家。 我们欢迎更多优秀的ui组件出现,也欢迎更多人贡献 `uni-ui` 的主题风格,满足更多用户的需求。 ## 快速开始 uni-ui支持 HBuilderX直接新建项目模板、npm安装和单独导入个别组件等多种使用方式 ### 在HBuilderX 新建uni-app项目的模板中,选择uni-ui模板 ![HBuilderX内创建uni-ui项目](https://img.cdn.aliyun.dcloud.net.cn/uni-app/doc/create-uni-ui-project.jpg) 由于uni-app独特的[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)技术,可以免引用、注册,直接使用各种符合规则的vue组件。 在代码区键入`u`,拉出各种内置或uni-ui的组件列表,选择其中一个,即可使用该组件。 光标放在组件名称上,按F1,可以查阅组件的文档。 ![uni-ui代码块](https://img.cdn.aliyun.dcloud.net.cn/uni-app/doc/uni-ui-snippet.jpg) ### 通过 uni_modules 单独安装组件 如果你没有创建uni-ui项目模板,也可以在你的工程里,通过 uni_modules 单独安装需要的某个组件。下表为uni-ui的扩展组件清单,点击每个组件在详情页面可以导入组件到项目下,导入后直接使用即可,无需import和注册。 |组件名|组件说明| |---|---| |uni-badge|[数字角标](https://ext.dcloud.net.cn/plugin?name=uni-badge)| |uni-calendar|[日历](https://ext.dcloud.net.cn/plugin?name=uni-calendar)| |uni-card|[卡片](https://ext.dcloud.net.cn/plugin?name=uni-card)| |uni-collapse|[折叠面板](https://ext.dcloud.net.cn/plugin?name=uni-collapse)| |uni-combox|[组合框](https://ext.dcloud.net.cn/plugin?name=uni-combox)| |uni-countdown|[倒计时](https://ext.dcloud.net.cn/plugin?name=uni-countdown)| |uni-data-checkbox|[数据选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-checkbox)| |uni-data-picker|[数据驱动的picker选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-picker)| |uni-dateformat|[日期格式化](https://ext.dcloud.net.cn/plugin?name=uni-dateformat)| |uni-datetime-picker|[日期选择器](https://ext.dcloud.net.cn/plugin?name=uni-datetime-picker)| |uni-drawer|[抽屉](https://ext.dcloud.net.cn/plugin?name=uni-drawer)| |uni-easyinput|[增强输入框](https://ext.dcloud.net.cn/plugin?name=uni-easyinput)| |uni-fab|[悬浮按钮](https://ext.dcloud.net.cn/plugin?name=uni-fab)| |uni-fav|[收藏按钮](https://ext.dcloud.net.cn/plugin?name=uni-fav)| |uni-file-picker|[文件选择上传](https://ext.dcloud.net.cn/plugin?name=uni-file-picker)| |uni-forms|[表单](https://ext.dcloud.net.cn/plugin?name=uni-forms)| |uni-goods-nav|[商品导航](https://ext.dcloud.net.cn/plugin?name=uni-goods-nav)| |uni-grid|[宫格](https://ext.dcloud.net.cn/plugin?name=uni-grid)| |uni-group|[分组](https://ext.dcloud.net.cn/plugin?name=uni-group)| |uni-icons|[图标](https://ext.dcloud.net.cn/plugin?name=uni-icons)| |uni-indexed-list|[索引列表](https://ext.dcloud.net.cn/plugin?name=uni-indexed-list)| |uni-link|[超链接](https://ext.dcloud.net.cn/plugin?name=uni-link)| |uni-list|[列表](https://ext.dcloud.net.cn/plugin?name=uni-list)| |uni-load-more|[加载更多](https://ext.dcloud.net.cn/plugin?name=uni-load-more)| |uni-nav-bar|[自定义导航栏](https://ext.dcloud.net.cn/plugin?name=uni-nav-bar)| |uni-notice-bar|[通告栏](https://ext.dcloud.net.cn/plugin?name=uni-notice-bar)| |uni-number-box|[数字输入框](https://ext.dcloud.net.cn/plugin?name=uni-number-box)| |uni-pagination|[分页器](https://ext.dcloud.net.cn/plugin?name=uni-pagination)| |uni-popup|[弹出层](https://ext.dcloud.net.cn/plugin?name=uni-popup)| |uni-rate|[评分](https://ext.dcloud.net.cn/plugin?name=uni-rate)| |uni-row|[布局-行](https://ext.dcloud.net.cn/plugin?name=uni-row)| |uni-search-bar|[搜索栏](https://ext.dcloud.net.cn/plugin?name=uni-search-bar)| |uni-segmented-control|[分段器](https://ext.dcloud.net.cn/plugin?name=uni-segmented-control)| |uni-steps|[步骤条](https://ext.dcloud.net.cn/plugin?name=uni-steps)| |uni-swipe-action|[滑动操作](https://ext.dcloud.net.cn/plugin?name=uni-swipe-action)| |uni-swiper-dot|[轮播图指示点](https://ext.dcloud.net.cn/plugin?name=uni-swiper-dot)| |uni-table|[表格](https://ext.dcloud.net.cn/plugin?name=uni-table)| |uni-tag|[标签](https://ext.dcloud.net.cn/plugin?name=uni-tag)| |uni-title|[章节标题](https://ext.dcloud.net.cn/plugin?name=uni-title)| |uni-transition|[过渡动画](https://ext.dcloud.net.cn/plugin?name=uni-transition)| 使用 `uni_modules` 方式安装组件库,可以直接通过插件市场导入,通过右键菜单快速更新组件,不需要引用、注册,直接在页面中使用 `uni-ui` 组件。[点击安装 uni-ui 组件库](https://ext.dcloud.net.cn/plugin?id=55) **注意:下载最新的组件目前仅支持 uni_modules ,非 uni_modules 版本最高支持到组件的1.2.10版本** 如不能升级到 `uni_modules` 版本,可以使用 `uni_modules` 安装好对应组件,将组件拷贝到对应目录。 例如需更新 `uni-list`和`uni-badge` ,将 `uni_modules>uni-list>components`和`uni_modules>uni-badege>components`下所有目录拷贝到如下目录即可: **目录示例** ```json ┌─components 组件目录 │ ├─uni-list list 列表目录 │ │ └─uni-list.vue list 组件文件 │ ├─uni-list-item list-item 列表目录 │ │ └─uni-list-item.vue list 组件文件 │ ├─uni-badge badge 角标目录 │ │ └─uni-badge.vue badge 组件文件 │ └─ //.... 更多组件文件 ├─pages 业务页面文件存放的目录 │ ├─index │ │ └─index.vue index示例页面 ├─main.js Vue初始化入口文件 ├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期 ├─manifest.json 配置应用名称、appid、logo、版本等打包信息,详见 └─pages.json 配置页 ``` ### 通过 `uni_modules` 导入全部组件 如果想一次把所有uni-ui组件导入到项目中,只需要导入一个 `uni-ui` 组件即可 [点击去导入](https://ext.dcloud.net.cn/plugin?id=55)。 如果没有自动导入其他组件,可以在 uni-ui 组件目录上右键选择 `安装三方插件依赖` 即可。 ### npm安装 在 `vue-cli` 项目中可以使用 `npm` 安装 `uni-ui` 库 ,或者直接在 `HBuilderX` 项目中使用 `npm` 。 > **注意** > cli 项目默认是不编译 `node_modules` 下的组件的,导致条件编译等功能失效 ,导致组件异常 > 需要在根目录创建 `vue.config.js` 文件 ,增加 `@dcloudio/uni-ui` 包的编译即可正常 > ```javascript > // vue.config.js > module.exports = { > transpileDependencies:['@dcloudio/uni-ui'] > } > ``` **准备 sass** `vue-cli` 项目请先安装 sass 及 sass-loader,如在 HBuliderX 中使用,可跳过此步。 - 安装 sass ``` npm i sass -D 或 yarn add sass -D ``` - 安装 sass-loader ``` npm i sass-loader@10.1.1 -D 或 yarn add sass-loader@10.1.1 -D ``` > 如果 `node` 版本小于 16 ,sass-loader 请使用低于 @11.0.0 的版本,[sass-loader@11.0.0 不支持 vue@2.6.12 ](https://stackoverflow.com/questions/66082397/typeerror-this-getoptions-is-not-a-function) > 如果 `node` 版本大于 16 , `sass-loader` 建议使用 `v8.x` 版本 **安装 uni-ui** ``` npm i @dcloudio/uni-ui 或 yarn add @dcloudio/uni-ui ``` **配置easycom** 使用 `npm` 安装好 `uni-ui` 之后,需要配置 `easycom` 规则,让 `npm` 安装的组件支持 `easycom` 打开项目根目录下的 `pages.json` 并添加 `easycom` 节点: ```javascript // pages.json { "easycom": { "autoscan": true, "custom": { // uni-ui 规则如下配置 "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } }, // 其他内容 pages:[ // ... ] } ``` 在 ``template`` 中使用组件: ```html ``` **注意** - uni-ui 现在只推荐使用 `easycom` ,如自己引用组件,可能会出现组件找不到的问题 - 使用 npm 安装的组件,默认情况下 babel-loader 会忽略所有 node_modules 中的文件 ,导致条件编译失效,需要通过配置 `vue.config.js` 解决: ```javascript // 在根目录创建 vue.config.js 文件,并配置如下 module.exports = { transpileDependencies: ['@dcloudio/uni-ui'] } ``` - uni-ui 是uni-app内置组件的扩展。注意与web开发不同,uni-ui不包括基础组件,它是基础组件的补充。web开发中有的开发者习惯用一个ui库完成所有开发,但在uni-app体系中,推荐开发者首先使用性能更高的基础组件,然后按需引入必要的扩展组件。 - `uni-ui` 不支持使用 `Vue.use()` 的方式安装 ### 贡献代码 在使用 `uni-ui` 中,如遇到无法解决的问题,请提 [Issues](https://github.com/dcloudio/uni-ui/issues) 给我们,假如您有更好的点子或更好的实现方式,也欢迎给我们提交 [PR](https://github.com/dcloudio/uni-ui/pulls) ================================================ FILE: talkieai-uniapp/src/utils/bus.ts ================================================ export default class EventBus { constructor() { (this as any).events = {}; } emit(eventName: any, data: any) { if ((this as any).events[eventName]) { (this as any).events[eventName].forEach(function (fn: any) { fn(data); }); } } on(eventName: any, fn: any) { (this as any).events[eventName] = (this as any).events[eventName] || []; (this as any).events[eventName].push(fn); } off(eventName: any, fn: any) { if ((this as any).events[eventName]) { for (let i = 0; i < (this as any).events[eventName].length; i++) { if ((this as any).events[eventName][i] === fn) { (this as any).events[eventName].splice(i, 1); break; } } } } } ================================================ FILE: talkieai-uniapp/src/utils/utils.ts ================================================ import __config from "@/config/env"; export default { isWechat: () => { const ua = navigator.userAgent.toLowerCase(); return ua.indexOf("micromessenger") !== -1; }, removeDecimal: (num: number) => { return Math.floor(num); }, getVoiceFileUrl: (fileName: string) => { return `${__config.basePath}/voices/${fileName}`; }, }; ================================================ FILE: talkieai-uniapp/tsconfig.json ================================================ { "extends": "@vue/tsconfig/tsconfig.json", "compilerOptions": { "sourceMap": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] }, "lib": ["esnext", "dom"], "types": ["@dcloudio/types"] }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] } ================================================ FILE: talkieai-uniapp/vite.config.ts ================================================ import { defineConfig } from "vite"; import uni from "@dcloudio/vite-plugin-uni"; const target = "https://crm.shoxfashion.com/api/cms-dashboard/"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [uni()], server: { host: "0.0.0.0", proxy: { "/api/cms-dashboard": { target, rewrite: (path) => { console.log(path); return path.replace("/api/cms-dashboard", "/"); }, changeOrigin: true, secure: false, xfwd: false, }, }, }, });