diff --git a/web/.eslintrc.js b/web/.eslintrc.js index e2c18b9..5ec1698 100644 --- a/web/.eslintrc.js +++ b/web/.eslintrc.js @@ -47,5 +47,7 @@ module.exports = { '@typescript-eslint/ban-types': 'off', 'vue/multi-word-component-names': 'off', 'vue/comment-directive': 'off', + 'no-async-promise-executor': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', }, }; diff --git a/web/package-lock.json b/web/package-lock.json index a8b6ff5..c563151 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -12,13 +12,14 @@ "echarts": "^5.4.2", "less-loader": "^11.1.3", "lodash": "^4.17.21", - "open-im-sdk-wasm": "^0.1.1", + "open-im-sdk-wasm": "^3.1.0", "pinia": "^2.1.4", "pinia-plugin-persistedstate": "^3.1.0", "qs": "^6.11.2", "tdesign-icons-vue-next": "^0.1.11", "tdesign-vue-next": "^1.3.10", "tvision-color": "^1.6.0", + "uuid": "^9.0.0", "vite-svg-loader": "^4.0.0", "vue": "^3.3.4", "vue-router": "^4.2.2" @@ -2864,9 +2865,9 @@ } }, "node_modules/open-im-sdk-wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/open-im-sdk-wasm/-/open-im-sdk-wasm-0.1.1.tgz", - "integrity": "sha512-I12XLvP2YwaPPWUCTaOCkqoRuqEx3A1aMvS2f9ouzCMtiFa6FRDLT7koVIri2+XtM8MmEB1XZAxwt3jYBb22gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/open-im-sdk-wasm/-/open-im-sdk-wasm-3.1.0.tgz", + "integrity": "sha512-Q1VpRCfV6vUrpZKzL/G351zsLeRxByV6FecKWB4blHzJEhXVH47DxbHjXduOs4MU0IcYYRqXiQseiVLsYHUKkQ==", "dependencies": { "@jlongster/sql.js": "^1.6.7", "absurd-sql": "^0.0.53", diff --git a/web/package.json b/web/package.json index 1b7d104..67454ee 100644 --- a/web/package.json +++ b/web/package.json @@ -14,13 +14,14 @@ "echarts": "^5.4.2", "less-loader": "^11.1.3", "lodash": "^4.17.21", - "open-im-sdk-wasm": "^0.1.1", + "open-im-sdk-wasm": "^3.1.0", "pinia": "^2.1.4", "pinia-plugin-persistedstate": "^3.1.0", "qs": "^6.11.2", "tdesign-icons-vue-next": "^0.1.11", "tdesign-vue-next": "^1.3.10", "tvision-color": "^1.6.0", + "uuid": "^9.0.0", "vite-svg-loader": "^4.0.0", "vue": "^3.3.4", "vue-router": "^4.2.2" diff --git a/web/src/api/request/communityModel.ts b/web/src/api/request/communityModel.ts index 2b3329d..5614768 100644 --- a/web/src/api/request/communityModel.ts +++ b/web/src/api/request/communityModel.ts @@ -2,6 +2,7 @@ export interface CommunityInfo { avatar: string; name: string; email: string; + description: string; } export type CreateCommunityParam = CommunityInfo; diff --git a/web/src/api/request/openimModel.ts b/web/src/api/request/openimModel.ts index bd143e2..87812bc 100644 --- a/web/src/api/request/openimModel.ts +++ b/web/src/api/request/openimModel.ts @@ -1,3 +1,3 @@ -import { LoginParam } from '@/utils/open-im-sdk-wasm/types/params'; +import { InitAndLoginConfig } from '@/utils/open-im-sdk-wasm/types/params'; -export type IMLoginParam = LoginParam; +export type IMLoginParam = InitAndLoginConfig; diff --git a/web/src/store/index.ts b/web/src/store/index.ts index 7f62c9a..7e14ee2 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -8,5 +8,7 @@ export { store }; export * from './menu'; export * from './user'; +export * from './openim_session'; +export * from './openim_message'; export default store; diff --git a/web/src/store/menu/index.ts b/web/src/store/menu/index.ts index 31b6002..9f6185a 100644 --- a/web/src/store/menu/index.ts +++ b/web/src/store/menu/index.ts @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'; import usePermissionStore from '@/store'; import type { MenuRoute } from '@/types/interface' import type { MenuInfoResponse } from '@/api/response/menuModel'; +import store from "../index"; const MockMenuInfo: MenuInfoResponse[] = [ { @@ -36,7 +37,7 @@ const MockMenuInfo: MenuInfoResponse[] = [ } ] -export const useMenuStore = defineStore('menu', { +const useStore = defineStore('menu', { state: () => ({ menu_routes: MockMenuInfo, }), @@ -46,4 +47,8 @@ export const useMenuStore = defineStore('menu', { // TODO: fetch menu info } } -}); \ No newline at end of file +}); + +export default function useMenuStore() { + return useStore(store); +} diff --git a/web/src/store/openim_message/index.ts b/web/src/store/openim_message/index.ts new file mode 100644 index 0000000..a441bce --- /dev/null +++ b/web/src/store/openim_message/index.ts @@ -0,0 +1,75 @@ +import { OpenIM } from '@/api/openim'; +import { MessageItem } from "open-im-sdk-wasm/lib/types/entity"; +import { GetAdvancedHistoryMsgParams } from "open-im-sdk-wasm/lib/types/params"; +import { defineStore } from "pinia"; +import store from "../index"; + +interface StateType { + historyMessageList: MessageItem[]; + hasMore: boolean; +} + +interface IAdvancedMessageResponse { + lastMinSeq: number; + isEnd: boolean; + messageList: MessageItem[]; +} + +type GetHistoryMessageListFromReqResp = { + messageIDList: string[]; + lastMinSeq: number; +}; + +const useStore = defineStore("message", { + state: (): StateType => ({ + historyMessageList: [], + hasMore: true, + }), + getters: { + storeHistoryMessageList: (state) => state.historyMessageList, + storeHistoryMessageHasMore: (state) => state.hasMore, + }, + actions: { + async getHistoryMessageListFromReq( + params: GetAdvancedHistoryMsgParams + ): Promise { + const isFirstPage = + params.startClientMsgID === "" || params.lastMinSeq === 0; + try { + const { data } = await OpenIM.getAdvancedHistoryMessageList(params); + this.historyMessageList = [ + ...data.messageList, + ...(isFirstPage ? [] : this.historyMessageList), + ]; + this.hasMore = !data.isEnd && data.messageList.length === 20; + return { + messageIDList: data.messageList.map( + (message: MessageItem) => message.clientMsgID + ), + lastMinSeq: data.lastMinSeq, + }; + } catch (error) { + console.log("Get history message failed", error); + this.hasMore = false; + return { + messageIDList: [], + lastMinSeq: 0, + }; + } + }, + pushNewMessage(message: MessageItem) { + this.historyMessageList.push(message); + }, + clearHistoryMessage() { + this.historyMessageList = []; + }, + resetHistoryMessageList() { + this.historyMessageList = []; + this.hasMore = true; + }, + }, +}); + +export default function useMessageStore() { + return useStore(store); +} diff --git a/web/src/store/openim_session/index.ts b/web/src/store/openim_session/index.ts new file mode 100644 index 0000000..9b909b6 --- /dev/null +++ b/web/src/store/openim_session/index.ts @@ -0,0 +1,63 @@ +import { OpenIM } from '@/api/openim'; +import { + ConversationItem, +} from "@/utils/open-im-sdk-wasm/types/entity"; + +import { defineStore } from "pinia"; +import store from "../index"; + +interface StateType { + conversationList: ConversationItem[]; + currentConversation: ConversationItem; + unReadCount: number; +} + +const useStore = defineStore("session", { + state: (): StateType => ({ + conversationList: [], + currentConversation: {} as ConversationItem, + unReadCount: 0, + }), + getters: { + storeConversationList: (state) => state.conversationList, + storeCurrentConversation: (state) => state.currentConversation, + storeUnReadCount: (state) => state.unReadCount, + }, + actions: { + async getConversationListFromReq(): Promise { + try { + const { data } = await OpenIM.getAllConversationList(); + this.conversationList = data; + console.log('getConversationListFromReq', data); + return true; + } catch (error) { + return false; + } + }, + async getUnReadCountFromReq() { + const { data } = await OpenIM.getTotalUnreadMsgCount(); + this.unReadCount = data; + }, + updateUnReadCount(data: number) { + this.unReadCount = data; + }, + updateCurrentConversation(item: ConversationItem) { + this.currentConversation = { ...item }; + }, + updateConversationList(list: ConversationItem[]) { + this.conversationList = [...list]; + }, + delConversationByCID(conversationID: string) { + const idx = this.conversationList.findIndex( + (cve) => cve.conversationID === conversationID + ); + if (idx !== -1) { + this.conversationList.splice(idx, 1); + } + }, + }, +}); + +export default function useSessionStore() { + return useStore(store); +} diff --git a/web/src/store/user/index.ts b/web/src/store/user/index.ts index 53ab7c5..050e69b 100644 --- a/web/src/store/user/index.ts +++ b/web/src/store/user/index.ts @@ -7,6 +7,7 @@ import type { GetCommunityInfoResponse } from '@/api/response/communityModel'; import { defineStore } from 'pinia'; import { getMyCommunityInfo } from '@/api/index/community'; import { getMyInfo } from '@/api/index/user'; +import store from "../index"; const InitUserInfo: GetUserInfoResponse = { uuid: '', @@ -28,14 +29,23 @@ const InitCommunityInfo: GetCommunityInfoResponse = { }; const InitToken: TokenResponse = { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3V1aWQiOiI5NmUwNGQ5Mi1mZTkwLTRmMGEtYWU4MC1hZTg4YWRmNWY2ZGIiLCJjb21tdW5pdHlfdXVpZCI6ImNmZjkxMjEwLWE5MGEtNGNjYi04MmI3LWI1MzU2M2E3NzkxOSIsImlzcyI6Im9wZW5rZiIsImV4cCI6MTY5MTMxOTM3OCwibmJmIjoxNjkxMjMyOTc4fQ.PaCXrxjP_WyJSY8yishU5jCouRZ1cgpVrh5OSMVBnh0', + token: '', expire_time_seconds: 0, }; -export const useUserStore = defineStore('user', { +const InitKFToken: TokenResponse = { + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3V1aWQiOiI1NTUyNDhhMWQxNDA5YTdhYmI1ODMwZmRhZDVkIiwiY29tbXVuaXR5X3V1aWQiOiI4NjNiOTExMjdmNGYwODhlZDQ2MjZjMzU3MTYyIiwiaXNzIjoib3BlbmtmIiwiZXhwIjoxNjkxNzI5MzgwLCJuYmYiOjE2OTE2NDI5ODB9.JomPDoVlrRYHWr03yIWqHbPaIQfuXdACEkhw4Ccwm0c', + expire_time_seconds: 0, +}; +const InitIMToken: TokenResponse = { + token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiI1NTUyNDhhMWQxNDA5YTdhYmI1ODMwZmRhZDVkIiwiUGxhdGZvcm1JRCI6NSwiZXhwIjoxNjk5NDE4OTgwLCJuYmYiOjE2OTE2NDI2ODAsImlhdCI6MTY5MTY0Mjk4MH0.btYIWiNc21o7sowLddv-6k9qEKQdiWlTFnQraX3eAYE', + expire_time_seconds: 0, +}; + +const useStore = defineStore('user', { state: () => ({ - kf_token: { ...InitToken }, - im_token: { ...InitToken }, + kf_token: { ...InitKFToken }, + im_token: { ...InitIMToken }, userInfo: { ...InitUserInfo }, communityInfo: { ...InitCommunityInfo }, }), @@ -50,7 +60,7 @@ export const useUserStore = defineStore('user', { const [userInfo, communityInfo] = await Promise.all([ getMyInfo() .then(res => { - console.log('getMyInfo success', res); + // console.log('getMyInfo success', res); return res; }) .catch(res => { @@ -59,7 +69,7 @@ export const useUserStore = defineStore('user', { }), // if error, set to default value getMyCommunityInfo() .then(res => { - console.log('getMyCommunityInfo success', res); + // console.log('getMyCommunityInfo success', res); return res; }) .catch(res => { @@ -78,3 +88,8 @@ export const useUserStore = defineStore('user', { }, }, }); + +export default function useUserStore() { + return useStore(store); + } + \ No newline at end of file diff --git a/web/src/style/reset-tdesign.less b/web/src/style/reset-tdesign.less index 2a608c3..b8ee0c7 100644 --- a/web/src/style/reset-tdesign.less +++ b/web/src/style/reset-tdesign.less @@ -1,8 +1,22 @@ // reset for openkf +.t-textarea__inner { + border-radius: var(--td-radius-large); + height: var(--td-comp-size-xxxxxl); + padding: var(--td-comp-paddingTB-s) var(--td-comp-paddingLR-l); + resize: none; +} + .t-menu__logo { min-height: var(--td-comp-size-xxxxxl); } +.kf-session-input { + .t-input { + border-radius: var(--td-radius-large); + width: 180px; + } +} + .kf-config-input { .t-input { border-radius: var(--td-radius-large); @@ -10,6 +24,31 @@ } } +.kf-session-list-card { + margin-bottom: var(--td-comp-margin-m); + + .t-card { + border-radius: var(--td-radius-large); + background-color: var(--td-font-white-1); + } + + .t-card__header { + padding: var(--td-comp-paddingTB-xs) var(--td-comp-paddingLR-l); + padding-top: var(--td-comp-paddingTB-m); + + .t-card__title { + font-weight: 500; + } + } + + .t-card__body { + padding: var(--td-comp-paddingTB-xs) var(--td-comp-paddingLR-l); + padding-bottom: var(--td-comp-paddingTB-m); + font-size: var(--td-font-size-body-small); + color: var(--td-text-color-secondary); + } +} + .kf-config-list-card { margin-bottom: var(--td-comp-margin-m); diff --git a/web/src/utils/common/im.ts b/web/src/utils/common/im.ts new file mode 100644 index 0000000..f33ff83 --- /dev/null +++ b/web/src/utils/common/im.ts @@ -0,0 +1,37 @@ +import { + ConversationItem, +} from "@/utils/open-im-sdk-wasm/types/entity"; + +export const conversationSort = (conversationList: ConversationItem[]) => { + const arr: string[] = []; + const filterArr = conversationList.filter( + (c) => !arr.includes(c.conversationID) && arr.push(c.conversationID) + ); + filterArr.sort((a, b) => { + if (a.isPinned === b.isPinned) { + if (!(a.latestMsgSendTime && b.latestMsgSendTime)) { + return 0; + } + const aCompare = + a.draftTextTime! > a.latestMsgSendTime! + ? a.draftTextTime! + : a.latestMsgSendTime!; + const bCompare = + b.draftTextTime! > b.latestMsgSendTime! + ? b.draftTextTime! + : b.latestMsgSendTime!; + if (aCompare > bCompare) { + return -1; + } else if (aCompare < bCompare) { + return 1; + } else { + return 0; + } + } else if (a.isPinned && !b.isPinned) { + return -1; + } else { + return 1; + } + }); + return filterArr; +}; \ No newline at end of file diff --git a/web/src/utils/common/time.ts b/web/src/utils/common/time.ts index 438d5e8..f9c3794 100644 --- a/web/src/utils/common/time.ts +++ b/web/src/utils/common/time.ts @@ -43,4 +43,18 @@ export const getNowDiffDays = (date: string): number => { const diffTime = Math.abs(new Date(date).getTime() - new Date().getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays; +} + +// Return a time from date +// if date is today, return time +// if date is yesterday, return yesterday +// if date is before yesterday, return date +export const getTimeFromDate = (timestamp: number | string): string => { + const dat = new Date(timestamp); + const diffDays = getNowDiffDays(dat.toString()); + if (diffDays === 0) { + return dat.getHours() + ':' + dat.getMinutes(); + } else { + return dat.getDate() + '/' + (dat.getMonth() + 1) + '/' + dat.getFullYear(); + } } \ No newline at end of file diff --git a/web/src/utils/request/index.ts b/web/src/utils/request/index.ts index 33c6aee..0852f5a 100644 --- a/web/src/utils/request/index.ts +++ b/web/src/utils/request/index.ts @@ -7,7 +7,7 @@ import { ContentTypeEnum } from '@/constants'; import { VAxios } from './axios'; import type { AxiosTransform, CreateAxiosOptions } from './transform'; import { formatRequestDate, joinTimestamp, setObjToUrlParams } from './utils'; -import { useUserStore } from '@/store'; +import useUserStore from '@/store/user'; // Default API url const host = import.meta.env.VITE_API_URL; diff --git a/web/src/views/config/components/BotTable.vue b/web/src/views/config/components/BotTable.vue index 59198e8..3dd84e1 100644 --- a/web/src/views/config/components/BotTable.vue +++ b/web/src/views/config/components/BotTable.vue @@ -197,7 +197,7 @@ const COLUMNS: PrimaryTableCol[] = [ >