diff --git a/package.json b/package.json index b450bf69..c8dab2b8 100644 --- a/package.json +++ b/package.json @@ -6,17 +6,17 @@ "private": false, "scripts": { "i": "pnpm install", - "dev": "vite", + "dev": "vite --mode env.local", "dev-server": "vite --mode dev", "ts:check": "vue-tsc --noEmit", "build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build", - "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev", + "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev", "build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test", "build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage", "build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod", "serve:dev": "vite preview --mode dev", "serve:prod": "vite preview --mode prod", - "preview": "pnpm build:local-dev && vite preview", + "preview": "pnpm build:local && vite preview", "clean": "npx rimraf node_modules", "clean:cache": "npx rimraf node_modules/.cache", "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", @@ -29,6 +29,7 @@ "@form-create/designer": "^3.1.3", "@form-create/element-ui": "^3.1.24", "@iconify/iconify": "^3.1.1", + "@microsoft/fetch-event-source": "^2.0.1", "@videojs-player/vue": "^1.0.0", "@vueuse/core": "^10.9.0", "@wangeditor/editor": "^5.1.23", @@ -51,6 +52,7 @@ "highlight.js": "^11.9.0", "jsencrypt": "^3.3.2", "lodash-es": "^4.17.21", + "marked": "^12.0.2", "min-dash": "^4.1.1", "mitt": "^3.0.1", "nprogress": "^0.2.0", diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts new file mode 100644 index 00000000..683646ea --- /dev/null +++ b/src/api/ai/chat/conversation/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +// AI 聊天会话 VO +export interface ChatConversationVO { + id: number // ID 编号 + userId: number // 用户编号 + title: string // 会话标题 + pinned: boolean // 是否置顶 + roleId: number // 角色编号 + modelId: number // 模型编号 + model: string // 模型标志 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 + updateTime: number // 更新时间 + // 额外字段 + modelName?: string // 模型名字 + roleAvatar?: string // 角色头像 + modelMaxTokens?: string // 模型的单条回复的最大 Token 数量 + modelMaxContexts?: string // 模型的上下文的最大 Message 数量 +} + +// AI 聊天会话 API +export const ChatConversationApi = { + // 获得【我的】聊天会话 + getChatConversationMy: async (id: number) => { + return await request.get({ url: `/ai/chat/conversation/get-my?id=${id}` }) + }, + + // 新增【我的】聊天会话 + createChatConversationMy: async (data?: ChatConversationVO) => { + return await request.post({ url: `/ai/chat/conversation/create-my`, data }) + }, + + // 更新【我的】聊天会话 + updateChatConversationMy: async (data: ChatConversationVO) => { + return await request.put({ url: `/ai/chat/conversation/update-my`, data }) + }, + + // 删除【我的】聊天会话 + deleteChatConversationMy: async (id: number) => { + return await request.delete({ url: `/ai/chat/conversation/delete-my?id=${id}` }) + }, + + // 删除【我的】所有对话,置顶除外 + deleteMyAllExceptPinned: async () => { + return await request.delete({ url: `/ai/chat/conversation/delete-my-all-except-pinned` }) + }, + + // 获得【我的】聊天会话列表 + getChatConversationMyList: async () => { + return await request.get({ url: `/ai/chat/conversation/my-list` }) + } +} diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts new file mode 100644 index 00000000..05b4d804 --- /dev/null +++ b/src/api/ai/chat/message/index.ts @@ -0,0 +1,67 @@ +import request from '@/config/axios' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { getAccessToken } from '@/utils/auth' +import { config } from '@/config/axios/config' + +// 聊天VO +export interface ChatMessageVO { + id: number // 编号 + conversationId: number // 会话编号 + type: string // 消息类型 + userId: string // 用户编号 + roleId: string // 角色编号 + model: number // 模型标志 + modelId: number // 模型编号 + content: string // 聊天内容 + tokens: number // 消耗 Token 数量 + createTime: Date // 创建时间 +} + +export interface ChatMessageSendVO { + conversationId: string // 会话编号 + content: number // 聊天内容 +} + +// AI chat 聊天 +export const ChatMessageApi = { + // 消息列表 + messageList: async (conversationId: number | null) => { + return await request.get({ + url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 发送 send stream 消息 + // TODO axios 可以么? https://apifox.com/apiskills/how-to-create-axios-stream/ + sendStream: async ( + conversationId: number, + content: string, + ctrl, + onMessage, + onError, + onClose + ) => { + const token = getAccessToken() + return fetchEventSource(`${config.base_url}/ai/chat/message/send-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + + // 发送 send 消息 + delete: async (id: string) => { + return await request.delete({ url: `/ai/chat/message/delete?id=${id}` }) + } +} diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts index 47e415e4..ed94836e 100644 --- a/src/api/ai/model/apiKey/index.ts +++ b/src/api/ai/model/apiKey/index.ts @@ -12,27 +12,32 @@ export interface ApiKeyVO { // AI API 密钥 API export const ApiKeyApi = { - // 查询AI API 密钥分页 + // 查询 API 密钥分页 getApiKeyPage: async (params: any) => { return await request.get({ url: `/ai/api-key/page`, params }) }, - // 查询AI API 密钥详情 + // 获得 API 密钥列表 + getApiKeySimpleList: async () => { + return await request.get({ url: `/ai/api-key/simple-list` }) + }, + + // 查询 API 密钥详情 getApiKey: async (id: number) => { return await request.get({ url: `/ai/api-key/get?id=` + id }) }, - // 新增AI API 密钥 + // 新增 API 密钥 createApiKey: async (data: ApiKeyVO) => { return await request.post({ url: `/ai/api-key/create`, data }) }, - // 修改AI API 密钥 + // 修改 API 密钥 updateApiKey: async (data: ApiKeyVO) => { return await request.put({ url: `/ai/api-key/update`, data }) }, - // 删除AI API 密钥 + // 删除 API 密钥 deleteApiKey: async (id: number) => { return await request.delete({ url: `/ai/api-key/delete?id=` + id }) } diff --git a/src/api/ai/model/chatModel/index.ts b/src/api/ai/model/chatModel/index.ts new file mode 100644 index 00000000..c2ef4c8d --- /dev/null +++ b/src/api/ai/model/chatModel/index.ts @@ -0,0 +1,53 @@ +import request from '@/config/axios' + +// AI 聊天模型 VO +export interface ChatModelVO { + id: number // 编号 + keyId: number // API 秘钥编号 + name: string // 模型名字 + model: string // 模型标识 + platform: string // 模型平台 + sort: number // 排序 + status: number // 状态 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 +} + +// AI 聊天模型 API +export const ChatModelApi = { + // 查询聊天模型分页 + getChatModelPage: async (params: any) => { + return await request.get({ url: `/ai/chat-model/page`, params }) + }, + + // 获得聊天模型列表 + getChatModelSimpleList: async (status?: number) => { + return await request.get({ + url: `/ai/chat-model/simple-list`, + params: { + status + } + }) + }, + + // 查询聊天模型详情 + getChatModel: async (id: number) => { + return await request.get({ url: `/ai/chat-model/get?id=` + id }) + }, + + // 新增聊天模型 + createChatModel: async (data: ChatModelVO) => { + return await request.post({ url: `/ai/chat-model/create`, data }) + }, + + // 修改聊天模型 + updateChatModel: async (data: ChatModelVO) => { + return await request.put({ url: `/ai/chat-model/update`, data }) + }, + + // 删除聊天模型 + deleteChatModel: async (id: number) => { + return await request.delete({ url: `/ai/chat-model/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/chatRole/index.ts b/src/api/ai/model/chatRole/index.ts new file mode 100644 index 00000000..a9fce13c --- /dev/null +++ b/src/api/ai/model/chatRole/index.ts @@ -0,0 +1,80 @@ +import request from '@/config/axios' + +// AI 聊天角色 VO +export interface ChatRoleVO { + id: number // 角色编号 + modelId: number // 模型编号 + name: string // 角色名称 + avatar: string // 角色头像 + category: string // 角色类别 + sort: number // 角色排序 + description: string // 角色描述 + systemMessage: string // 角色设定 + welcomeMessage: string // 角色设定 + publicStatus: boolean // 是否公开 + status: number // 状态 +} + +// AI 聊天角色 分页请求 vo +export interface ChatRolePageReqVO { + name?: string // 角色名称 + category?: string // 角色类别 + publicStatus: boolean // 是否公开 + pageNo: number // 是否公开 + pageSize: number // 是否公开 +} + +// AI 聊天角色 API +export const ChatRoleApi = { + // 查询聊天角色分页 + getChatRolePage: async (params: any) => { + return await request.get({ url: `/ai/chat-role/page`, params }) + }, + + // 查询聊天角色详情 + getChatRole: async (id: number) => { + return await request.get({ url: `/ai/chat-role/get?id=` + id }) + }, + + // 新增聊天角色 + createChatRole: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create`, data }) + }, + + // 修改聊天角色 + updateChatRole: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update`, data }) + }, + + // 删除聊天角色 + deleteChatRole: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete?id=` + id }) + }, + + // ======= chat 聊天 + + // 获取 my role + getMyPage: async (params: ChatRolePageReqVO) => { + return await request.get({ url: `/ai/chat-role/my-page`, params}) + }, + + // 获取角色分类 + getCategoryList: async () => { + return await request.get({ url: `/ai/chat-role/category-list`}) + }, + + // 创建角色 + createMy: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create-my`, data}) + }, + + // 更新角色 + updateMy: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update-my`, data}) + }, + + // 删除角色 my + deleteMy: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete-my?id=` + id }) + }, +} diff --git a/src/assets/ai/copy-style2.svg b/src/assets/ai/copy-style2.svg new file mode 100644 index 00000000..2d56a87f --- /dev/null +++ b/src/assets/ai/copy-style2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/copy.svg b/src/assets/ai/copy.svg new file mode 100644 index 00000000..f51f8d81 --- /dev/null +++ b/src/assets/ai/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/ai/delete.svg b/src/assets/ai/delete.svg new file mode 100644 index 00000000..d2ee18ed --- /dev/null +++ b/src/assets/ai/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/MarkdownView/index.vue b/src/components/MarkdownView/index.vue new file mode 100644 index 00000000..6ecb2ea5 --- /dev/null +++ b/src/components/MarkdownView/index.vue @@ -0,0 +1,213 @@ + + + + + + + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue index 345670ae..1715d73b 100644 --- a/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue +++ b/src/components/bpmnProcessDesigner/package/penal/flow-condition/FlowCondition.vue @@ -79,13 +79,13 @@ const resetFlowCondition = () => { bpmnElement.value = bpmnInstances().bpmnElement bpmnElementSource.value = bpmnElement.value.source bpmnElementSourceRef.value = bpmnElement.value.businessObject.sourceRef + // 初始化默认type为default + flowConditionForm.value = { type: 'default' } if ( bpmnElementSourceRef.value && bpmnElementSourceRef.value.default && - bpmnElementSourceRef.value.default.id === bpmnElement.value.id && - flowConditionForm.value.type == 'default' + bpmnElementSourceRef.value.default.id === bpmnElement.value.id ) { - // 默认 flowConditionForm.value = { type: 'default' } } else if (!bpmnElement.value.businessObject.conditionExpression) { // 普通 diff --git a/src/utils/download.ts b/src/utils/download.ts index ab200149..fe24ee27 100644 --- a/src/utils/download.ts +++ b/src/utils/download.ts @@ -29,7 +29,7 @@ const download = { html: (data: Blob, fileName: string) => { download0(data, fileName, 'text/html') }, - // 下载 Markdown 方法 + // 下载 MarkdownView 方法 markdown: (data: Blob, fileName: string) => { download0(data, fileName, 'text/markdown') } diff --git a/src/views/Login/SocialLogin.vue b/src/views/Login/SocialLogin.vue index 1eb9293c..eff59ba6 100644 --- a/src/views/Login/SocialLogin.vue +++ b/src/views/Login/SocialLogin.vue @@ -64,7 +64,7 @@ - + { // 情况一,未开启:则直接登录 - if (loginData.captchaEnable) { + if (!loginData.captchaEnable) { await handleLogin({}) } else { // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录 diff --git a/src/views/ai/chat/components/ChatConversationUpdateForm.vue b/src/views/ai/chat/components/ChatConversationUpdateForm.vue new file mode 100644 index 00000000..fe08ffb2 --- /dev/null +++ b/src/views/ai/chat/components/ChatConversationUpdateForm.vue @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + 确 定 + 取 消 + + + + diff --git a/src/views/ai/chat/components/Header.vue b/src/views/ai/chat/components/Header.vue new file mode 100644 index 00000000..7d859c2b --- /dev/null +++ b/src/views/ai/chat/components/Header.vue @@ -0,0 +1,50 @@ + + + + + {{ title }} + + + + + + + + + + diff --git a/src/views/ai/chat/components/MessageList.vue b/src/views/ai/chat/components/MessageList.vue new file mode 100644 index 00000000..0f77f8ec --- /dev/null +++ b/src/views/ai/chat/components/MessageList.vue @@ -0,0 +1,156 @@ + + + + + + + + + + + + {{formatDate(item.createTime)}} + + + + {{item.content}} + + + + + + 复制 + + + + 删除 + + + + + + + + + + + + {{formatDate(item.createTime)}} + + + + {{item.content}} + + + + + + 复制 + + + + 删除 + + + + + + + + + + + diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue index ec49b774..0da53f1e 100644 --- a/src/views/ai/chat/index.vue +++ b/src/views/ai/chat/index.vue @@ -4,8 +4,8 @@ - - + + 新建对话 @@ -17,45 +17,51 @@ @keyup="searchConversation" > - + - - - - 置顶 - - - - - - {{ conversation.title }} - - - - - - - - - + + + + + {{conversationKey}} - + + + + + {{ conversation.title }} + + + + + + + + + + + + + - - + + 角色仓库 - - - 清空未置顶对话 + + + 清空未置顶对话 @@ -64,73 +70,579 @@ - 标题...... + {{ useConversation?.title }} - 3.5-turbo-0125 - - + + + + - + - + + + + - 对话列表 - - + + + + + + + + + + + + + + {{ formatDate(item.createTime) }} + + + + + + + + + + 复制 + + + + 删除 + + + + + + + + + + + + {{ formatDate(item.createTime) }} + + + {{ item.content }} + + + + + + 复制 + + + + 删除 + + + + + + + + + + + + + + + + + + {{ conversationInProgress ? '进行中' : '发送' }} + + + 停止 + + + + + + diff --git a/src/views/ai/chat/role/RoleCategoryList.vue b/src/views/ai/chat/role/RoleCategoryList.vue new file mode 100644 index 00000000..549a4416 --- /dev/null +++ b/src/views/ai/chat/role/RoleCategoryList.vue @@ -0,0 +1,46 @@ + + + + {{ category }} + {{ category }} + + + + + diff --git a/src/views/ai/chat/role/RoleList.vue b/src/views/ai/chat/role/RoleList.vue new file mode 100644 index 00000000..834eb3cb --- /dev/null +++ b/src/views/ai/chat/role/RoleList.vue @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + 编辑 + + + + 删除 + + + + + + + + + + + + {{ role.name }} + {{ role.description }} + + + + 使用 + + + + + + + + + + diff --git a/src/views/ai/chat/role/index.vue b/src/views/ai/chat/role/index.vue new file mode 100644 index 00000000..924a6784 --- /dev/null +++ b/src/views/ai/chat/role/index.vue @@ -0,0 +1,219 @@ + + + + + + + + + + + + + 添加角色 + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue index 2f9ba580..a8fc0128 100644 --- a/src/views/ai/model/apiKey/ApiKeyForm.vue +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -7,13 +7,7 @@ label-width="120px" v-loading="formLoading" > - - - - - - - + - - + + + + + + + + diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue index fcafc463..6daf6a7d 100644 --- a/src/views/ai/model/apiKey/index.vue +++ b/src/views/ai/model/apiKey/index.vue @@ -60,15 +60,14 @@ - - - - + - + + + diff --git a/src/views/ai/model/chatModel/ChatModelForm.vue b/src/views/ai/model/chatModel/ChatModelForm.vue new file mode 100644 index 00000000..cac3f772 --- /dev/null +++ b/src/views/ai/model/chatModel/ChatModelForm.vue @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + + + + + + 确 定 + 取 消 + + + + diff --git a/src/views/ai/model/chatModel/index.vue b/src/views/ai/model/chatModel/index.vue new file mode 100644 index 00000000..c5506746 --- /dev/null +++ b/src/views/ai/model/chatModel/index.vue @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + 搜索 + 重置 + + 新增 + + + + + + + + + + + + + + + + + + {{ apiKeyList.find((item) => item.id === scope.row.keyId)?.name }} + + + + + + + + + + + + + + + 编辑 + + + 删除 + + + + + + + + + + + + + diff --git a/src/views/ai/model/chatRole/ChatRoleForm.vue b/src/views/ai/model/chatRole/ChatRoleForm.vue new file mode 100644 index 00000000..a3fbb891 --- /dev/null +++ b/src/views/ai/model/chatRole/ChatRoleForm.vue @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ dict.label }} + + + + + + + + + + {{ dict.label }} + + + + + + 确 定 + 取 消 + + + + diff --git a/src/views/ai/model/chatRole/index.vue b/src/views/ai/model/chatRole/index.vue new file mode 100644 index 00000000..e870a556 --- /dev/null +++ b/src/views/ai/model/chatRole/index.vue @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + 搜索 + 重置 + + 新增 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 编辑 + + + 删除 + + + + + + + + + + + + + diff --git a/src/views/bpm/oa/leave/index.vue b/src/views/bpm/oa/leave/index.vue index bd41104a..2cb53247 100644 --- a/src/views/bpm/oa/leave/index.vue +++ b/src/views/bpm/oa/leave/index.vue @@ -83,7 +83,7 @@ - + - @@ -64,13 +63,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -173,7 +172,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastMonthCount !== 0 ? ((s.currentMonthCount / s.lastMonthCount) * 100).toFixed(2) : 'NULL' + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { @@ -181,7 +182,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastYearCount !== 0 ? ((s.currentMonthCount / s.lastYearCount) * 100).toFixed(2) : 'NULL' + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -197,8 +200,8 @@ const tableData = reactive([ { title: '当月合同数量统计(个)' }, { title: '上月合同数量统计(个)' }, { title: '去年当月合同数量统计(个)' }, - { title: '同比增长率(%)' }, - { title: '环比增长率(%)' } + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } ]) // 定义 convertListData 方法,数据行列转置,展示每月数据 @@ -214,9 +217,13 @@ const convertListData = () => { tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount tableData[3]['prop' + index] = - item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' tableData[4]['prop' + index] = - item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue index f97b612c..dd52d9fb 100644 --- a/src/views/crm/statistics/performance/components/ContractPricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ContractPricePerformance.vue @@ -1,5 +1,4 @@ - @@ -64,13 +63,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -173,7 +172,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastMonthCount !== 0 ? ((s.currentMonthCount / s.lastMonthCount) * 100).toFixed(2) : 'NULL' + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[2]['data']) { @@ -181,7 +182,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastYearCount !== 0 ? ((s.currentMonthCount / s.lastYearCount) * 100).toFixed(2) : 'NULL' + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -197,8 +200,8 @@ const tableData = reactive([ { title: '当月合同金额统计(元)' }, { title: '上月合同金额统计(元)' }, { title: '去年当月合同金额统计(元)' }, - { title: '同比增长率(%)' }, - { title: '环比增长率(%)' } + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } ]) // 定义 init 方法 @@ -214,9 +217,13 @@ const convertListData = () => { tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount tableData[3]['prop' + index] = - item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' tableData[4]['prop' + index] = - item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue index 14f59909..169f074b 100644 --- a/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue +++ b/src/views/crm/statistics/performance/components/ReceivablePricePerformance.vue @@ -17,7 +17,6 @@ :prop="item.prop" align="center" > - {{ scope.row[item.prop] }} @@ -64,13 +63,13 @@ const echartsOption = reactive({ data: [] }, { - name: '同比增长率(%)', + name: '环比增长率(%)', type: 'line', yAxisIndex: 1, data: [] }, { - name: '环比增长率(%)', + name: '同比增长率(%)', type: 'line', yAxisIndex: 1, data: [] @@ -121,7 +120,6 @@ const echartsOption = reactive({ type: 'value', name: '', axisTick: { - // TODO @scholar:IDEA 爆红的处理 alignWithLabel: true, lineStyle: { width: 0 @@ -174,7 +172,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastMonthCount ) echartsOption.series[3]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastMonthCount !== 0 ? ((s.currentMonthCount / s.lastMonthCount) * 100).toFixed(2) : 'NULL' + s.lastMonthCount !== 0 + ? (((s.currentMonthCount - s.lastMonthCount) / s.lastMonthCount) * 100).toFixed(2) + : 'NULL' ) } if (echartsOption.series && echartsOption.series[2] && echartsOption.series[1]['data']) { @@ -182,7 +182,9 @@ const loadData = async () => { (s: StatisticsPerformanceRespVO) => s.lastYearCount ) echartsOption.series[4]['data'] = performanceList.map((s: StatisticsPerformanceRespVO) => - s.lastYearCount !== 0 ? ((s.currentMonthCount / s.lastYearCount) * 100).toFixed(2) : 'NULL' + s.lastYearCount !== 0 + ? (((s.currentMonthCount - s.lastYearCount) / s.lastYearCount) * 100).toFixed(2) + : 'NULL' ) } @@ -193,14 +195,13 @@ const loadData = async () => { } // 初始化数据 -// TODO @scholar:加个 as any[],避免 idea 爆红 -const columnsData = reactive([] as any[]) +const columnsData = reactive([]) const tableData = reactive([ { title: '当月回款金额统计(元)' }, { title: '上月回款金额统计(元)' }, { title: '去年当月回款金额统计(元)' }, - { title: '同比增长率(%)' }, - { title: '环比增长率(%)' } + { title: '环比增长率(%)' }, + { title: '同比增长率(%)' } ]) // 定义 init 方法 @@ -215,11 +216,14 @@ const convertListData = () => { tableData[0]['prop' + index] = item.currentMonthCount tableData[1]['prop' + index] = item.lastMonthCount tableData[2]['prop' + index] = item.lastYearCount - // TODO @scholar:百分比,使用 erpCalculatePercentage 直接计算;如果是 0,则返回 0,统一就好哈; tableData[3]['prop' + index] = - item.lastMonthCount !== 0 ? (item.currentMonthCount / item.lastMonthCount).toFixed(2) : 'NULL' + item.lastMonthCount !== 0 + ? (((item.currentMonthCount - item.lastMonthCount) / item.lastMonthCount) * 100).toFixed(2) + : 'NULL' tableData[4]['prop' + index] = - item.lastYearCount !== 0 ? (item.currentMonthCount / item.lastYearCount).toFixed(2) : 'NULL' + item.lastYearCount !== 0 + ? (((item.currentMonthCount - item.lastYearCount) / item.lastYearCount) * 100).toFixed(2) + : 'NULL' }) } diff --git a/src/views/crm/statistics/performance/index.vue b/src/views/crm/statistics/performance/index.vue index 4a443c55..822afec9 100644 --- a/src/views/crm/statistics/performance/index.vue +++ b/src/views/crm/statistics/performance/index.vue @@ -73,7 +73,7 @@ import * as DeptApi from '@/api/system/dept' import * as UserApi from '@/api/system/user' import { useUserStore } from '@/store/modules/user' -import { beginOfDay, formatDate } from '@/utils/formatTime' +import { beginOfDay, endOfDay, formatDate } from '@/utils/formatTime' import { defaultProps, handleTree } from '@/utils/tree' import ContractCountPerformance from './components/ContractCountPerformance.vue' import ContractPricePerformance from './components/ContractPricePerformance.vue' @@ -85,8 +85,8 @@ const queryParams = reactive({ deptId: useUserStore().getUser.deptId, userId: undefined, times: [ - // 默认显示当年的数据 - formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))) + formatDate(beginOfDay(new Date(new Date().getFullYear(), 0, 1))), + formatDate(endOfDay(new Date(new Date().getFullYear(), 11, 31))) ] }) @@ -100,31 +100,20 @@ const userListByDeptId = computed(() => : [] ) -// TODO @scholar:改成尾注释,保证 vue 内容短一点;变量名小写 // 活跃标签 const activeTab = ref('ContractCountPerformance') -// 1.员工合同数量统计 -const ContractCountPerformanceRef = ref() -// 2.员工合同金额统计 -const ContractPricePerformanceRef = ref() -// 3.员工回款金额统计 -const ReceivablePricePerformanceRef = ref() +const ContractCountPerformanceRef = ref() // 员工合同数量统计 +const ContractPricePerformanceRef = ref() // 员工合同金额统计 +const ReceivablePricePerformanceRef = ref() // 员工回款金额统计 /** 搜索按钮操作 */ const handleQuery = () => { // 从 queryParams.times[0] 中获取到了年份 const selectYear = parseInt(queryParams.times[0]) + queryParams.times[0] = formatDate(beginOfDay(new Date(selectYear, 0, 1))) + queryParams.times[1] = formatDate(endOfDay(new Date(selectYear, 11, 31))) - // 创建一个新的 Date 对象,设置为指定的年份的第一天 - const fullDate = new Date(selectYear, 0, 1, 0, 0, 0) - - // 将完整的日期时间格式化为需要的字符串形式,比如 2004-01-01 00:00:00 - // TODO @scholar:看看,是不是可以使用 year 哈 - queryParams.times[0] = `${fullDate.getFullYear()}-${String(fullDate.getMonth() + 1).padStart( - 2, - '0' - )}-${String(fullDate.getDate()).padStart(2, '0')} ${String(fullDate.getHours()).padStart(2, '0')}:${String(fullDate.getMinutes()).padStart(2, '0')}:${String(fullDate.getSeconds()).padStart(2, '0')}` - + // 执行查询 switch (activeTab.value) { case 'ContractCountPerformance': ContractCountPerformanceRef.value?.loadData?.() diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue index e99d7f8a..4757e5c0 100644 --- a/src/views/system/dept/index.vue +++ b/src/views/system/dept/index.vue @@ -8,7 +8,7 @@ :inline="true" label-width="68px" > - +