diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index dd8b2435c..99859205e 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -43,6 +43,7 @@ "@vben/styles": "workspace:*", "@vben/types": "workspace:*", "@vben/utils": "workspace:*", + "@vueuse/components": "^14.0.0", "@vueuse/core": "catalog:", "@vueuse/integrations": "catalog:", "ant-design-vue": "catalog:", diff --git a/apps/web-antd/src/components/card-title/CardTitle.vue b/apps/web-antd/src/components/card-title/CardTitle.vue new file mode 100644 index 000000000..99f9d1131 --- /dev/null +++ b/apps/web-antd/src/components/card-title/CardTitle.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/apps/web-antd/src/components/card-title/index.ts b/apps/web-antd/src/components/card-title/index.ts new file mode 100644 index 000000000..4ebdb4494 --- /dev/null +++ b/apps/web-antd/src/components/card-title/index.ts @@ -0,0 +1 @@ +export { default as CardTitle } from './CardTitle.vue'; diff --git a/apps/web-antd/src/store/mall/kefu.ts b/apps/web-antd/src/store/mall/kefu.ts new file mode 100644 index 000000000..85f4023e7 --- /dev/null +++ b/apps/web-antd/src/store/mall/kefu.ts @@ -0,0 +1,102 @@ +import type { MallKefuConversationApi } from '#/api/mall/promotion/kefu/conversation'; +import type { MallKefuMessageApi } from '#/api/mall/promotion/kefu/message'; + +import { isEmpty } from '@vben/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +import * as KeFuConversationApi from '#/api/mall/promotion/kefu/conversation'; + +interface MallKefuInfoVO { + conversationList: MallKefuConversationApi.Conversation[]; // 会话列表 + conversationMessageList: Map; // 会话消息 +} + +export const useMallKefuStore = defineStore('mall-kefu', { + state: (): MallKefuInfoVO => ({ + conversationList: [], + conversationMessageList: new Map(), // key 会话,value 会话消息列表 + }), + getters: { + getConversationList(): MallKefuConversationApi.Conversation[] { + return this.conversationList; + }, + getConversationMessageList(): ( + conversationId: number, + ) => MallKefuMessageApi.Message[] | undefined { + return (conversationId: number) => + this.conversationMessageList.get(conversationId); + }, + }, + actions: { + // ======================= 会话消息相关 ======================= + /** 缓存历史消息 */ + saveMessageList( + conversationId: number, + messageList: MallKefuMessageApi.Message[], + ) { + this.conversationMessageList.set(conversationId, messageList); + }, + + // ======================= 会话相关 ======================= + /** 加载会话缓存列表 */ + async setConversationList() { + // TODO @jave:idea linter 告警,修复下; + // TODO @jave:不使用 KeFuConversationApi.,直接用 getConversationList + this.conversationList = await KeFuConversationApi.getConversationList(); + this.conversationSort(); + }, + /** 更新会话缓存已读 */ + async updateConversationStatus(conversationId: number) { + if (isEmpty(this.conversationList)) { + return; + } + const conversation = this.conversationList.find( + (item) => item.id === conversationId, + ); + conversation && (conversation.adminUnreadMessageCount = 0); + }, + /** 更新会话缓存 */ + async updateConversation(conversationId: number) { + if (isEmpty(this.conversationList)) { + return; + } + + const conversation = + await KeFuConversationApi.getConversation(conversationId); + this.deleteConversation(conversationId); + conversation && this.conversationList.push(conversation); + this.conversationSort(); + }, + /** 删除会话缓存 */ + deleteConversation(conversationId: number) { + const index = this.conversationList.findIndex( + (item) => item.id === conversationId, + ); + // 存在则删除 + if (index !== -1) { + this.conversationList.splice(index, 1); + } + }, + conversationSort() { + // 按置顶属性和最后消息时间排序 + this.conversationList.sort((a, b) => { + // 按照置顶排序,置顶的会在前面 + if (a.adminPinned !== b.adminPinned) { + return a.adminPinned ? -1 : 1; + } + // 按照最后消息时间排序,最近的会在前面 + return ( + (b.lastMessageTime as unknown as number) - + (a.lastMessageTime as unknown as number) + ); + }); + }, + }, +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useMallKefuStore, hot)); +} diff --git a/apps/web-antd/src/views/bpm/processInstance/detail/index.vue b/apps/web-antd/src/views/bpm/processInstance/detail/index.vue index d22cb7288..391fa1142 100644 --- a/apps/web-antd/src/views/bpm/processInstance/detail/index.vue +++ b/apps/web-antd/src/views/bpm/processInstance/detail/index.vue @@ -183,9 +183,7 @@ function setFieldPermission(field: string, permission: string) { } } -/** - * 操作成功后刷新 - */ +/** 操作成功后刷新 */ const refresh = () => { // 重新获取详情 getDetail(); diff --git a/apps/web-antd/src/views/iot/device/device/data.ts b/apps/web-antd/src/views/iot/device/device/data.ts index 424f0ef92..5edd1cc0d 100644 --- a/apps/web-antd/src/views/iot/device/device/data.ts +++ b/apps/web-antd/src/views/iot/device/device/data.ts @@ -239,7 +239,7 @@ export function useGridFormSchema(): VbenFormSchema[] { label: '设备状态', component: 'Select', componentProps: { - options: getDictOptions(DICT_TYPE.IOT_DEVICE_STATUS, 'number'), + options: getDictOptions(DICT_TYPE.IOT_DEVICE_STATE, 'number'), placeholder: '请选择设备状态', allowClear: true, }, @@ -295,12 +295,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { slots: { default: 'groups' }, }, { - field: 'status', + field: 'state', title: '设备状态', minWidth: 100, cellRender: { name: 'CellDict', - props: { type: DICT_TYPE.IOT_DEVICE_STATUS }, + props: { type: DICT_TYPE.IOT_DEVICE_STATE }, }, }, { diff --git a/apps/web-antd/src/views/iot/device/device/index.vue b/apps/web-antd/src/views/iot/device/device/index.vue index f83a7c14a..d06649d66 100644 --- a/apps/web-antd/src/views/iot/device/device/index.vue +++ b/apps/web-antd/src/views/iot/device/device/index.vue @@ -307,7 +307,7 @@ onMounted(async () => { style="width: 200px" > diff --git a/apps/web-antd/src/views/iot/device/device/modules/components/DeviceTableSelect.vue b/apps/web-antd/src/views/iot/device/device/modules/components/DeviceTableSelect.vue index efaad5976..8f8afc3e0 100644 --- a/apps/web-antd/src/views/iot/device/device/modules/components/DeviceTableSelect.vue +++ b/apps/web-antd/src/views/iot/device/device/modules/components/DeviceTableSelect.vue @@ -294,7 +294,7 @@ onMounted(async () => { style="width: 240px" > @@ -373,7 +373,7 @@ onMounted(async () => { diff --git a/apps/web-antd/src/views/iot/device/device/modules/detail/DeviceDetailsInfo.vue b/apps/web-antd/src/views/iot/device/device/modules/detail/DeviceDetailsInfo.vue index d4b6b06cb..5ba870c61 100644 --- a/apps/web-antd/src/views/iot/device/device/modules/detail/DeviceDetailsInfo.vue +++ b/apps/web-antd/src/views/iot/device/device/modules/detail/DeviceDetailsInfo.vue @@ -106,7 +106,7 @@ function handleAuthInfoDialogClose() { diff --git a/apps/web-antd/src/views/iot/device/group/data.ts b/apps/web-antd/src/views/iot/device/group/data.ts index 01b4cc3f9..ca6a10a88 100644 --- a/apps/web-antd/src/views/iot/device/group/data.ts +++ b/apps/web-antd/src/views/iot/device/group/data.ts @@ -7,7 +7,7 @@ import { getDictOptions } from '@vben/hooks'; import { z } from '#/adapter/form'; import { getRangePickerDefaultProps } from '#/utils'; -/** 新增/修改设备分组的表单 */ +/** 新增/修改的表单 */ export function useFormSchema(): VbenFormSchema[] { return [ { diff --git a/apps/web-antd/src/views/iot/device/group/modules/device-group-form.vue b/apps/web-antd/src/views/iot/device/group/modules/device-group-form.vue index 28168c0b4..26dfe29d0 100644 --- a/apps/web-antd/src/views/iot/device/group/modules/device-group-form.vue +++ b/apps/web-antd/src/views/iot/device/group/modules/device-group-form.vue @@ -42,6 +42,7 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false, }); +// TODO @haohao:参考别的 form;1)文件的命名可以简化;2)代码可以在简化下; const [Modal, modalApi] = useVbenModal({ async onConfirm() { const { valid } = await formApi.validate(); @@ -71,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({ async onOpenChange(isOpen: boolean) { if (!isOpen) { formData.value = undefined; - formApi.resetForm(); + await formApi.resetForm(); return; } diff --git a/apps/web-antd/src/views/iot/product/category/data.ts b/apps/web-antd/src/views/iot/product/category/data.ts index 4857bc7d2..da109b8bc 100644 --- a/apps/web-antd/src/views/iot/product/category/data.ts +++ b/apps/web-antd/src/views/iot/product/category/data.ts @@ -7,7 +7,7 @@ import { getDictOptions } from '@vben/hooks'; import { z } from '#/adapter/form'; import { getRangePickerDefaultProps } from '#/utils'; -/** 新增/修改产品分类的表单 */ +/** 新增/修改的表单 */ export function useFormSchema(): VbenFormSchema[] { return [ { diff --git a/apps/web-antd/src/views/iot/product/category/index.vue b/apps/web-antd/src/views/iot/product/category/index.vue index 476c5991e..3ccc4e830 100644 --- a/apps/web-antd/src/views/iot/product/category/index.vue +++ b/apps/web-antd/src/views/iot/product/category/index.vue @@ -105,8 +105,6 @@ const [Grid, gridApi] = useVbenVxeGrid({ ]" /> - -