ykt-wxapp/pages/message/common-phrases.vue
2026-01-23 15:51:26 +08:00

591 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="common-phrases-page">
<view class="header">
<view class="title">快捷回复</view>
<view class="edit-btn" @click="toggleEditMode">{{ isEditMode ? '完成' : '编辑' }}</view>
</view>
<view class="category-tabs">
<view
v-for="category in categories"
:key="category.id"
class="category-tab"
:class="{ active: currentCategory === category.id }"
@click="switchCategory(category.id)"
>
{{ category.name }}
</view>
<view class="add-category-btn" @click="showAddCategoryDialog">
<text class="plus-icon">+</text>
加分类
</view>
</view>
<scroll-view class="phrases-list" scroll-y>
<view
v-for="phrase in currentPhrases"
:key="phrase.id"
class="phrase-item"
@click="handlePhraseClick(phrase)"
>
<view class="phrase-content">{{ phrase.content }}</view>
<view v-if="isEditMode" class="phrase-actions">
<view class="action-btn edit" @click.stop="editPhrase(phrase)">编辑</view>
<view class="action-btn delete" @click.stop="deletePhrase(phrase)">删除</view>
</view>
</view>
<view v-if="currentPhrases.length === 0" class="empty-state">
<image src="/static/empty.svg" class="empty-icon" mode="aspectFit"></image>
<text class="empty-text">暂无常用语</text>
</view>
</scroll-view>
<view class="add-phrase-btn" @click="showAddPhraseDialog">
<text class="plus-icon-white">+</text>
添加快捷回复
</view>
<!-- 添加/编辑常用语弹窗 -->
<view v-if="showPhrasePopup" class="popup-mask" @click="closePopup">
<view class="popup-content" @click.stop>
<view class="popup-title">{{ editingPhrase ? '编辑' : '添加快捷回复' }}</view>
<textarea
v-model="phraseForm.content"
class="phrase-textarea"
placeholder="在此处输入文字"
maxlength="500"
></textarea>
<view class="popup-actions">
<button class="cancel-btn" @click="closePopup">取消</button>
<button class="confirm-btn" @click="savePhrase">{{ editingPhrase ? '保存' : '确认添加' }}</button>
</view>
</view>
</view>
<!-- 添加分类弹窗 -->
<view v-if="showCategoryPopup" class="popup-mask" @click="closeCategoryPopup">
<view class="popup-content category-popup" @click.stop>
<view class="popup-title">新建分类名</view>
<input
v-model="categoryForm.name"
class="category-input"
placeholder="请在此填写分类名最多6个字"
maxlength="6"
/>
<view class="popup-actions">
<button class="cancel-btn" @click="closeCategoryPopup">取消</button>
<button class="confirm-btn" @click="saveCategory">确认添加</button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import api from '@/utils/api';
// 编辑模式
const isEditMode = ref(false);
// 分类数据
const categories = ref([
{ id: 'visit', name: '文字随访' },
{ id: 'voice', name: '语音随访' },
{ id: 'common', name: '常用回复' },
]);
const currentCategory = ref('common');
// 常用语数据
const phrases = ref([]);
// 当前分类的常用语
const currentPhrases = computed(() => {
return phrases.value.filter(p => p.categoryId === currentCategory.value);
});
// 弹窗显示状态
const showPhrasePopup = ref(false);
const showCategoryPopup = ref(false);
// 表单数据
const phraseForm = ref({
content: ''
});
const categoryForm = ref({
name: ''
});
const editingPhrase = ref(null);
// 切换编辑模式
const toggleEditMode = () => {
isEditMode.value = !isEditMode.value;
};
// 切换分类
const switchCategory = (categoryId) => {
currentCategory.value = categoryId;
};
// 处理常用语点击
const handlePhraseClick = (phrase) => {
if (isEditMode.value) {
return;
}
// 返回到聊天页面并发送消息
const pages = getCurrentPages();
const prevPage = pages[pages.length - 2];
if (prevPage) {
prevPage.$vm.sendCommonPhrase(phrase.content);
}
uni.navigateBack();
};
// 显示添加常用语弹窗
const showAddPhraseDialog = () => {
editingPhrase.value = null;
phraseForm.value.content = '';
showPhrasePopup.value = true;
};
// 编辑常用语
const editPhrase = (phrase) => {
editingPhrase.value = phrase;
phraseForm.value.content = phrase.content;
showPhrasePopup.value = true;
};
// 保存常用语
const savePhrase = async () => {
if (!phraseForm.value.content.trim()) {
uni.showToast({
title: '请输入内容',
icon: 'none'
});
return;
}
try {
if (editingPhrase.value) {
// 更新
await api('saveCommonPhrase', {
id: editingPhrase.value.id,
categoryId: editingPhrase.value.categoryId,
content: phraseForm.value.content,
corpId: uni.getStorageSync('corpId')
});
const index = phrases.value.findIndex(p => p.id === editingPhrase.value.id);
if (index !== -1) {
phrases.value[index].content = phraseForm.value.content;
}
} else {
// 新增
const result = await api('saveCommonPhrase', {
categoryId: currentCategory.value,
content: phraseForm.value.content,
corpId: uni.getStorageSync('corpId')
});
if (result.code === 200 && result.data) {
phrases.value.push(result.data);
} else {
// 如果API失败使用本地ID
phrases.value.push({
id: Date.now(),
categoryId: currentCategory.value,
content: phraseForm.value.content
});
}
}
uni.showToast({
title: editingPhrase.value ? '保存成功' : '添加成功',
icon: 'success'
});
closePopup();
} catch (error) {
console.error('保存常用语失败:', error);
uni.showToast({
title: '操作失败',
icon: 'none'
});
}
};
// 删除常用语
const deletePhrase = (phrase) => {
uni.showModal({
title: '提示',
content: '确定删除该常用语吗?',
success: async (res) => {
if (res.confirm) {
try {
await api('deleteCommonPhrase', {
id: phrase.id,
corpId: uni.getStorageSync('corpId')
});
const index = phrases.value.findIndex(p => p.id === phrase.id);
if (index !== -1) {
phrases.value.splice(index, 1);
}
uni.showToast({
title: '删除成功',
icon: 'success'
});
} catch (error) {
console.error('删除常用语失败:', error);
uni.showToast({
title: '删除失败',
icon: 'none'
});
}
}
}
});
};
// 显示添加分类弹窗
const showAddCategoryDialog = () => {
categoryForm.value.name = '';
showCategoryPopup.value = true;
};
// 保存分类
const saveCategory = () => {
if (!categoryForm.value.name.trim()) {
uni.showToast({
title: '请输入分类名',
icon: 'none'
});
return;
}
categories.value.push({
id: `category_${Date.now()}`,
name: categoryForm.value.name
});
uni.showToast({
title: '添加成功',
icon: 'success'
});
closeCategoryPopup();
};
// 关闭弹窗
const closePopup = () => {
showPhrasePopup.value = false;
};
const closeCategoryPopup = () => {
showCategoryPopup.value = false;
};
// 加载常用语数据
const loadPhrases = async () => {
try {
const result = await api('getCommonPhrases', {
corpId: uni.getStorageSync('corpId')
});
if (result.code === 200 && result.data) {
phrases.value = result.data;
} else {
// 如果API返回失败使用模拟数据
phrases.value = getMockPhrases();
}
} catch (error) {
console.error('加载常用语失败:', error);
// 加载失败时使用模拟数据
phrases.value = getMockPhrases();
}
};
// 模拟数据
const getMockPhrases = () => {
return [
{
id: 1,
categoryId: 'common',
content: '想要买草药自己熬还是配方颗粒直接开水冲着喝线上购买全国包邮一二线城市一般1-2天送到其他城市可能慢一些。'
},
{
id: 2,
categoryId: 'common',
content: '以前得过什么病吗?做过什么检查吗?有检查结果吗?详细说一下。'
},
{
id: 3,
categoryId: 'common',
content: '把我之前给您开的处方,拍个照上传给我看一下,拍招清楚一点。'
},
{
id: 4,
categoryId: 'common',
content: '服药期间要慎炸身体,少吃油炸等辛辣食物,按时作息,精神放松。'
},
{
id: 5,
categoryId: 'common',
content: '煎药最好用砂锅避免用铁锅煎药前用冷水浸泡30分钟水量超过药材2-5厘米一般中药煎2次第1次煮沸后再煎20分钟就可以倒出来了再次加水煮沸后再煎30分钟后倒出将2次倒出的药混合在一起就可以按照医嘱用药了。'
},
{
id: 6,
categoryId: 'common',
content: '如果在线上买药,点击整体治疗方案,选择首选处方,划价取药,填写地址后去付款即可。'
},
{
id: 7,
categoryId: 'voice',
content: '想要买草药自己熬还是配方颗粒直接开水冲着喝?'
},
{
id: 8,
categoryId: 'visit',
content: '您好,我是您的健康管理师,请问有什么可以帮助您的吗?'
}
];
};
onMounted(() => {
loadPhrases();
});
</script>
<style scoped lang="scss">
.common-phrases-page {
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f5f5f5;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
background-color: #fff;
border-bottom: 1px solid #eee;
.title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.edit-btn {
font-size: 28rpx;
color: #ff6b35;
}
}
.category-tabs {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
background-color: #fff;
overflow-x: auto;
white-space: nowrap;
.category-tab {
padding: 12rpx 24rpx;
margin-right: 16rpx;
font-size: 28rpx;
color: #666;
background-color: #f5f5f5;
border-radius: 8rpx;
flex-shrink: 0;
&.active {
color: #ff6b35;
background-color: #fff5f2;
border: 1px solid #ff6b35;
}
}
.add-category-btn {
display: flex;
align-items: center;
padding: 12rpx 24rpx;
font-size: 28rpx;
color: #ff6b35;
background-color: #fff5f2;
border-radius: 8rpx;
flex-shrink: 0;
.plus-icon {
font-size: 32rpx;
margin-right: 4rpx;
font-weight: 600;
}
}
}
.phrases-list {
flex: 1;
padding: 24rpx 32rpx;
overflow-y: auto;
.phrase-item {
padding: 24rpx;
margin-bottom: 16rpx;
background-color: #fff;
border-radius: 12rpx;
.phrase-content {
font-size: 28rpx;
color: #333;
line-height: 1.6;
}
.phrase-actions {
display: flex;
justify-content: flex-end;
margin-top: 16rpx;
padding-top: 16rpx;
border-top: 1px solid #f5f5f5;
.action-btn {
padding: 8rpx 24rpx;
margin-left: 16rpx;
font-size: 24rpx;
border-radius: 8rpx;
&.edit {
color: #007aff;
background-color: #e6f2ff;
}
&.delete {
color: #ff3b30;
background-color: #ffe6e6;
}
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 24rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
}
.add-phrase-btn {
display: flex;
align-items: center;
justify-content: center;
margin: 24rpx 32rpx;
padding: 24rpx;
font-size: 28rpx;
color: #fff;
background-color: #ff6b35;
border-radius: 12rpx;
.plus-icon-white {
font-size: 36rpx;
margin-right: 8rpx;
font-weight: 600;
}
}
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.popup-content {
width: 600rpx;
padding: 32rpx;
background-color: #fff;
border-radius: 16rpx;
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 24rpx;
}
.phrase-textarea {
width: 100%;
min-height: 300rpx;
padding: 16rpx;
font-size: 28rpx;
color: #333;
background-color: #f5f5f5;
border-radius: 8rpx;
margin-bottom: 24rpx;
}
.category-input {
width: 100%;
padding: 16rpx;
font-size: 28rpx;
color: #333;
background-color: #f5f5f5;
border-radius: 8rpx;
margin-bottom: 24rpx;
}
.popup-actions {
display: flex;
justify-content: space-between;
button {
flex: 1;
padding: 20rpx;
font-size: 28rpx;
border-radius: 8rpx;
border: none;
&.cancel-btn {
margin-right: 16rpx;
color: #666;
background-color: #f5f5f5;
}
&.confirm-btn {
color: #fff;
background-color: #ff6b35;
}
}
}
}
</style>