| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- <template>
- <view ref="targetDiv" id="target_input_box" class="input-box" :class="{ spread: globalStore.spread }">
- <view class="input-container hover-scale">
- <textarea
- v-if="globalStore.spread"
- v-model="message"
- class="input-container-input"
- placeholder="请输入你想咨询的问题..."
- :maxlength="maxLength"
- @input="handleInputFilter"
- @compositionstart="handleCompositionStart"
- @compositionend="handleCompositionEnd"
- @linechange="linechange"
- />
- <input
- v-else
- v-model="message"
- class="input-container-input"
- placeholder="请输入你想咨询的问题..."
- :maxlength="maxLength"
- @input="handleInputFilter"
- @compositionstart="handleCompositionStart"
- @compositionend="handleCompositionEnd"
- />
- <view class="input-buttons">
- <view class="input-buttons-limit">{{ message.length }}/200</view>
- <view class="input-buttons-right">
- <ss-recording @set-message="quickSend"></ss-recording>
- <view class="buttons-separate"></view>
- <button class="send-button" @tap="clickSend">
- <view v-if="globalStore.chatLoading" class="send-button-stop"></view>
- <uni-icons v-else type="arrow-up" size="24" color="#ffffff"></uni-icons>
- </button>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- import { useGlobalStore } from '@/stores/global';
- import SsRecording from './SsRecording.vue';
- import { RequestApi } from '@/api/requestApi';
- import { MessageApi } from '../api/message';
- export default {
- components: {
- SsRecording
- },
- data() {
- return {
- globalStore: useGlobalStore(),
- message: '',
- conversationId: '', // 消息ID,
- isComposing: false, // 中文输入法状态标识
- maxLength: 200,
- // 过滤规则配置
- filterRules: {
- regExp: /[^a-zA-Z0-9\u4e00-\u9fa5\,\,\.\。\!\!\%]/, // 允许:中文/英文/数字
- forbiddenWords: ['微信', 'vx'] // 黑名单关键词
- }
- };
- },
- computed: {
- /**
- * 发送消息标识
- */
- sendMessageFlag() {
- const msg = this.message.trim();
- return msg == '' || this.globalStore.chatLoading;
- }
- },
- mounted() {
- this.globalStore.setInputLine(3);
- },
- methods: {
- // 处理中文输入法状态
- handleCompositionStart() {
- this.isComposing = true;
- },
- handleCompositionEnd(e) {
- this.isComposing = false;
- this.handleInputFilter(e); // 输入法结束后触发过滤
- }, // 核心过滤方法
- handleInputFilter(e) {
- if (this.isComposing) return; // 输入法中间状态不处理
- let value = e.detail.value;
- // 步骤1:基础字符过滤
- // value = value.replace(this.filterRules.regExp, '');
- // 步骤2:长度截断
- if (value.length > this.maxLength) {
- value = value.slice(0, this.maxLength);
- uni.showToast({
- title: `最多输入${this.maxLength}个字符`,
- icon: 'none'
- });
- }
- // 步骤3:同步数据(解决iOS双向绑定延迟问题)
- this.message = value;
- },
- clickSend() {
- if (this.globalStore.chatLoading) {
- MessageApi.stop();
- this.globalStore.setChatLoading(false);
- setTimeout(() => {
- MessageApi.stop();
- }, 500);
- } else {
- this.sendMessage();
- }
- },
- sendMessage() {
- const msg = this.message.trim();
- if (this.sendMessageFlag) {
- return;
- }
- if (msg == '') {
- uni.showToast({
- title: '请输入你想咨询的问题',
- icon: 'none'
- });
- return;
- }
- this.$emit('newMessage');
- // 停止消息
- MessageApi.stop();
- // 设置会话主体
- this.globalStore.setSpread(true);
- this.globalStore.setMenuSwitch(false);
- // 设置为消息回复中
- this.globalStore.setChatLoading(true);
- // 发送消息内容
- this.globalStore.pushChatList({
- type: 'send_message',
- content: msg
- });
- this.$emit('changeChat');
- // 清除对话内容
- this.message = '';
- // 初始化单次对话内容
- this.globalStore.pushChatList({
- chatId: '',
- source: [],
- avatar: this.getStaticImageUrl('/images/robot-avatar.png'),
- type: 'text',
- title: msg,
- content: ''
- });
- MessageApi.sendMessage(msg, this.conversationId, (result) => {
- switch (result.type) {
- case 'id':
- this.conversationId = result.value;
- this.globalStore.replyChatList('', result.id);
- break;
- case 'text':
- this.globalStore.replyChatList(result.value);
- break;
- case 'source':
- case 'messageCompleted':
- this.globalStore.replyChatListSource(result.value);
- break;
- default:
- MessageApi.stop();
- this.globalStore.setChatLoading(false);
- }
- this.$emit('changeChat');
- });
- },
- quickSend(msg) {
- this.message = msg;
- this.sendMessage();
- },
- editMessage(msg) {
- this.message = msg;
- },
- loadService(categoryId, title, avatar) {
- if (this.globalStore.chatLoading) {
- return;
- }
- this.globalStore.setChatLoading(true);
- this.globalStore.setSpread(true);
- this.$emit('newMessage');
- let replyContent = {
- type: 'category',
- source: [],
- avatar: this.getImageUrl(avatar),
- title: title,
- content: categoryId
- };
- let [last] = this.globalStore.chatList.slice(-1);
- if (last) {
- if (typeof last.type === 'category') {
- if (last.content != categoryId) {
- this.globalStore.pushChatList(replyContent);
- }
- } else {
- this.globalStore.pushChatList(replyContent);
- }
- } else {
- this.globalStore.pushChatList(replyContent);
- }
- this.$nextTick(() => {
- this.globalStore.setMenuSwitch(false);
- this.globalStore.setChatLoading(false);
- });
- }
- }
- };
- </script>
- <style scoped lang="scss">
- @keyframes rotation {
- 0% {
- transform: translate(-50%, -50%) rotate(0deg);
- }
- 100% {
- transform: translate(-50%, -50%) rotate(360deg);
- }
- }
- .input-box {
- padding-bottom: 20rpx;
- .input-container {
- margin: 0 auto;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 18rpx 24rpx;
- border-radius: 24rpx;
- position: relative;
- overflow: hidden;
- box-shadow: 0 8rpx 14rpx 0 rgba(0, 0, 0, 0.08);
- &::before,
- &:before {
- content: '';
- position: absolute;
- width: 110%;
- padding-top: 110%;
- top: 50%;
- left: 50%;
- z-index: -2;
- transform: translate(-50%, -50%);
- background: linear-gradient(45deg, #ffa37c 30%, #ff7bb0 40%, #ad8afe 60%, #73b9ff 70%);
- animation: rotation 10s infinite linear;
- }
- &::after,
- &:after {
- content: '';
- position: absolute;
- top: 2px;
- left: 2px;
- bottom: 2px;
- right: 2px;
- z-index: -1;
- border-radius: 22rpx;
- background: linear-gradient(#f6faff, #e8f1fe);
- }
- .input-container-input {
- width: calc(100% - 192rpx);
- border: 0;
- background-color: transparent;
- font-size: 32rpx;
- max-height: 100rpx;
- &::-webkit-input-placeholder {
- /* Chrome/Safari/Opera */
- color: #aeaeb0;
- }
- &::-moz-placeholder {
- /* Firefox */
- color: #aeaeb0;
- }
- &:-ms-input-placeholder {
- /* IE 10+ */
- color: #aeaeb0;
- }
- &:-moz-placeholder {
- /* Firefox 18- */
- color: #aeaeb0;
- }
- }
- .input-buttons {
- display: flex;
- align-items: center;
- font-size: 38rpx;
- .input-buttons-limit {
- font-size: 28rpx;
- color: #7e8aa2;
- display: none;
- //transform: translateY(32rpx);
- }
- .input-buttons-right {
- display: flex;
- align-items: center;
- .buttons-separate {
- background-color: #d5d6d8;
- height: 32rpx;
- width: 1px;
- margin: 0 26rpx;
- }
- .send-button {
- background-color: #2942d4;
- border-radius: 32rpx;
- width: 66rpx;
- height: 66rpx;
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 0;
- .send-button-stop {
- display: inline-block;
- width: 32rpx;
- height: 32rpx;
- background-color: #ffffff;
- border-radius: 6rpx;
- }
- }
- }
- }
- }
- &.position-1 {
- // position: absolute;
- // z-index: 200;
- // width: calc(100% - 64rpx);
- }
- &.bottom {
- bottom: 0 !important;
- }
- &.spread {
- padding-bottom: 20rpx;
- .input-container {
- display: block;
- padding: 24rpx 32rpx;
- .input-container-input {
- width: 100%;
- }
- .input-buttons {
- .input-buttons-limit {
- display: block;
- }
- justify-content: space-between;
- }
- }
- }
- }
- </style>
|