diff --git a/src/views/mall/product/spu/components/SkuList.vue b/src/views/mall/product/spu/components/SkuList.vue index 2befe640..9bbd38e4 100644 --- a/src/views/mall/product/spu/components/SkuList.vue +++ b/src/views/mall/product/spu/components/SkuList.vue @@ -292,6 +292,7 @@ import { createImageViewer } from '@/components/ImageViewer' import { RuleConfig } from '@/views/mall/product/spu/components/index' import { PropertyAndValues } from './index' import { ElTable } from 'element-plus' +import { isEmpty } from '@/utils/is' defineOptions({ name: 'SkuList' }) const message = useMessage() // 消息弹窗 @@ -340,11 +341,22 @@ const imagePreview = (imgUrl: string) => { /** 批量添加 */ const batchAdd = () => { + validateProperty() formData.value!.skus!.forEach((item) => { copyValueToTarget(item, skuList.value[0]) }) } - +/** 校验商品属性属性值 */ +const validateProperty = () => { + // 校验商品属性属性值是否为空,有一个为空都不给过 + const warningInfo = '存在属性属性值为空,请先检查完善属性值后重试!!!' + for (const item of props.propertyList) { + if (!item.values || isEmpty(item.values)) { + message.warning(warningInfo) + throw new Error(warningInfo) + } + } +} /** 删除 sku */ const deleteSku = (row) => { const index = formData.value!.skus!.findIndex( @@ -358,6 +370,7 @@ const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表 * 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。 */ const validateSku = () => { + validateProperty() let warningInfo = '请检查商品各行相关属性配置,' let validate = true // 默认通过 for (const sku of formData.value!.skus!) { @@ -421,7 +434,7 @@ watch( const generateTableData = (propertyList: any[]) => { // 构建数据结构 const propertyValues = propertyList.map((item) => - item.values.map((v) => ({ + item.values.map((v: any) => ({ propertyId: item.id, propertyName: item.name, valueId: v.id, @@ -464,15 +477,14 @@ const generateTableData = (propertyList: any[]) => { */ const validateData = (propertyList: any[]) => { const skuPropertyIds: number[] = [] - formData.value!.skus!.forEach( - (sku) => - sku.properties - ?.map((property) => property.propertyId) - ?.forEach((propertyId) => { - if (skuPropertyIds.indexOf(propertyId!) === -1) { - skuPropertyIds.push(propertyId!) - } - }) + formData.value!.skus!.forEach((sku) => + sku.properties + ?.map((property) => property.propertyId) + ?.forEach((propertyId) => { + if (skuPropertyIds.indexOf(propertyId!) === -1) { + skuPropertyIds.push(propertyId!) + } + }) ) const propertyIds = propertyList.map((item) => item.id) return skuPropertyIds.length === propertyIds.length @@ -543,7 +555,7 @@ watch( return } // 添加新属性没有属性值也不做处理 - if (propertyList.some((item) => item.values!.length === 0)) { + if (propertyList.some((item) => !item.values || isEmpty(item.values))) { return } // 生成 table 数据,即 sku 列表 diff --git a/src/views/mall/product/spu/form/ProductAttributes.vue b/src/views/mall/product/spu/form/ProductAttributes.vue index 28962f47..ffe7397d 100644 --- a/src/views/mall/product/spu/form/ProductAttributes.vue +++ b/src/views/mall/product/spu/form/ProductAttributes.vue @@ -3,7 +3,7 @@
属性名: - + {{ item.name }}
@@ -12,8 +12,8 @@ {{ value.name }} @@ -44,7 +44,6 @@ diff --git a/src/views/mall/promotion/kefu/components/KeFuChatBox.vue b/src/views/mall/promotion/kefu/components/KeFuChatBox.vue index 872de7ab..ff577325 100644 --- a/src/views/mall/promotion/kefu/components/KeFuChatBox.vue +++ b/src/views/mall/promotion/kefu/components/KeFuChatBox.vue @@ -4,9 +4,16 @@
{{ keFuConversation.userNickname }}
- +
+ 加载更多 +
+
-
+
+
+ 有新消息 + +
@@ -101,23 +116,47 @@ const messageTool = useMessage() const message = ref('') // 消息 const messageList = ref([]) // 消息列表 const keFuConversation = ref({} as KeFuConversationRespVO) // 用户会话 -// 获得消息 TODO puhui999: 先不考虑下拉加载历史消息 +const showNewMessageTip = ref(false) // 显示有新消息提示 +const queryParams = reactive({ + pageNo: 1, + conversationId: 0 +}) +const total = ref(0) // 消息总条数 +// 获得消息 const getMessageList = async (conversation: KeFuConversationRespVO) => { keFuConversation.value = conversation - const { list } = await KeFuMessageApi.getKeFuMessagePage({ - pageNo: 1, - conversationId: conversation.id - }) - messageList.value = list.reverse() - // TODO puhui999: 首次加载时滚动到最新消息,如果加载的是历史消息则不滚动 + queryParams.conversationId = conversation.id + const messageTotal = messageList.value.length + if (total.value > 0 && messageTotal > 0 && messageTotal === total.value) { + return + } + const res = await KeFuMessageApi.getKeFuMessagePage(queryParams) + total.value = res.total + for (const item of res.list) { + if (messageList.value.some((val) => val.id === item.id)) { + continue + } + messageList.value.push(item) + } await scrollToBottom() } +const getMessageList0 = computed(() => { + messageList.value.sort((a: any, b: any) => a.createTime - b.createTime) + return messageList.value +}) + // 刷新消息列表 -const refreshMessageList = () => { +const refreshMessageList = async () => { if (!keFuConversation.value) { return } - getMessageList(keFuConversation.value) + + queryParams.pageNo = 1 + await getMessageList(keFuConversation.value) + if (loadHistory.value) { + // 有下角显示有新消息提示 + showNewMessageTip.value = true + } } defineExpose({ getMessageList, refreshMessageList }) // 是否显示聊天区域 @@ -140,7 +179,7 @@ const handleSendPicture = async (picUrl: string) => { const handleSendMessage = async () => { // 1. 校验消息是否为空 if (isEmpty(unref(message.value))) { - messageTool.warning('请输入消息后再发送哦!') + messageTool.notifyWarning('请输入消息后再发送哦!') return } // 2. 组织发送消息 @@ -167,12 +206,41 @@ const innerRef = ref() const scrollbarRef = ref>() // 滚动到底部 const scrollToBottom = async () => { - // 1. 滚动到最新消息 + // 1. 首次加载时滚动到最新消息,如果加载的是历史消息则不滚动 + if (loadHistory.value) { + return + } + // 2.1 滚动到最新消息,关闭新消息提示 await nextTick() scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight) - // 2. 消息已读 + showNewMessageTip.value = false + // 2.2 消息已读 await KeFuMessageApi.updateKeFuMessageReadStatus(keFuConversation.value.id) } +// 查看新消息 +const handleToNewMessage = async () => { + loadHistory.value = false + await scrollToBottom() +} + +const loadingMore = ref(false) // 滚动到顶部加载更多 +const loadHistory = ref(false) // 加载历史消息 +const handleScroll = async ({ scrollTop }) => { + const messageTotal = messageList.value.length + if (total.value > 0 && messageTotal > 0 && messageTotal === total.value) { + return + } + // 距顶 20 加载下一页数据 + loadingMore.value = scrollTop < 20 +} +const handleOldMessage = async () => { + loadHistory.value = true + // 加载消息列表 + queryParams.pageNo += 1 + await getMessageList(keFuConversation.value) + loadingMore.value = false + // TODO puhui999: 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置 +} /** * 是否显示时间 * @param {*} item - 数据 @@ -196,6 +264,32 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => { } &-content { + position: relative; + + .loadingMore { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 50px; + background-color: #eee; + color: #666; + text-align: center; + line-height: 50px; + transform: translateY(-100%); + transition: transform 0.3s ease-in-out; + } + + .newMessageTip { + position: absolute; + bottom: 35px; + right: 35px; + background-color: #fff; + padding: 10px; + border-radius: 30px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */ + } + .ss-row-left { justify-content: flex-start; diff --git a/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue b/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue index 947a7ad6..a9ec9444 100644 --- a/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue +++ b/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue @@ -74,7 +74,7 @@