From 2103173b9d14477748128f72541be14a37bd3b20 Mon Sep 17 00:00:00 2001 From: ouhaolan Date: Fri, 17 Apr 2026 09:55:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/ydoyun/aiAssistantReport/index.ts | 247 ++ src/api/ydoyun/aiModulePrompt/index.ts | 36 + src/api/ydoyun/aichat/index.ts | 140 + src/api/ydoyun/difyKbManage/index.ts | 65 + src/api/ydoyun/weather/index.ts | 12 + .../AiAssistant/AiAssistantMark.vue | 68 + .../AiAssistant/AiAssistantProvider.vue | 654 +++++ .../AiAssistant/AiImageChatDialog.vue | 1387 +++++++++ .../AiAssistant/ReportDocumentModal.vue | 2571 +++++++++++++++++ src/components/AiAssistant/index.vue | 115 + src/components/AiAssistant/useAiAssistant.ts | 61 + src/hooks/web/useAiModulePromptEditor.ts | 15 + src/views/ydoyun/aichat/index.vue | 396 +++ src/views/ydoyun/difykb/index.vue | 2270 +++++++++++++++ .../lijun/reportpage6/aiModuleConfig.ts | 14 + .../components/AiPromptEditDialog.vue | 168 ++ src/views/ydoyun/report/salesdaily/index.vue | 2251 +++++++++++++++ src/views/ydoyun/tagconfig/EditSqlModal.vue | 450 +++ .../ydoyun/tagconfig/报表库标签表建表.sql | 45 + src/views/ydoyun/tagconfig/表结构.sql | 168 ++ src/views/ydoyun/日报表 - 副本1.HTML | 297 ++ 21 files changed, 11430 insertions(+) create mode 100644 src/api/ydoyun/aiAssistantReport/index.ts create mode 100644 src/api/ydoyun/aiModulePrompt/index.ts create mode 100644 src/api/ydoyun/aichat/index.ts create mode 100644 src/api/ydoyun/difyKbManage/index.ts create mode 100644 src/api/ydoyun/weather/index.ts create mode 100644 src/components/AiAssistant/AiAssistantMark.vue create mode 100644 src/components/AiAssistant/AiAssistantProvider.vue create mode 100644 src/components/AiAssistant/AiImageChatDialog.vue create mode 100644 src/components/AiAssistant/ReportDocumentModal.vue create mode 100644 src/components/AiAssistant/index.vue create mode 100644 src/components/AiAssistant/useAiAssistant.ts create mode 100644 src/hooks/web/useAiModulePromptEditor.ts create mode 100644 src/views/ydoyun/aichat/index.vue create mode 100644 src/views/ydoyun/difykb/index.vue create mode 100644 src/views/ydoyun/report/lijun/reportpage6/aiModuleConfig.ts create mode 100644 src/views/ydoyun/report/lijun/reportpage6/components/AiPromptEditDialog.vue create mode 100644 src/views/ydoyun/report/salesdaily/index.vue create mode 100644 src/views/ydoyun/tagconfig/EditSqlModal.vue create mode 100644 src/views/ydoyun/tagconfig/报表库标签表建表.sql create mode 100644 src/views/ydoyun/tagconfig/表结构.sql create mode 100644 src/views/ydoyun/日报表 - 副本1.HTML diff --git a/src/api/ydoyun/aiAssistantReport/index.ts b/src/api/ydoyun/aiAssistantReport/index.ts new file mode 100644 index 0000000..141f475 --- /dev/null +++ b/src/api/ydoyun/aiAssistantReport/index.ts @@ -0,0 +1,247 @@ +import request from '@/config/axios' + +/** 保存报告单据请求 */ +export interface AiAssistantReportSaveReq { + /** 传入则更新;不传则后端按 报告人+模块编码+报告日期 唯一 upsert */ + id?: number + moduleName: string + moduleCode?: string + reporter: string + reporterId: number + reportTime: string + reportContent?: string + screenshotUrl?: string +} + +/** 历史报告项 */ +export interface AiAssistantReportItem { + id: number + moduleName: string + moduleCode?: string + reporter: string + reporterId: number + reportTime: string + reportContent?: string + screenshotUrl?: string + createTime: string +} + +/** 每日汇报详表行(与主表一对多) */ +export interface AiAssistantReportDetailItem { + id?: number + reportId?: number + /** 诊断项目 */ + diagnosisItem?: string + /** 指标异常 */ + value1?: string + /** 归因分析 */ + value2?: string + /** 改善对策 */ + value3?: string + sortOrder?: number +} + +/** 详表批量保存(全量替换) */ +export interface AiAssistantReportDetailBatchSaveReq { + reportId: number + details: Array<{ + diagnosisItem?: string + value1?: string + value2?: string + value3?: string + }> +} + +/** 分页查询参数(与后端 PageParam 一致:pageSize 为 -1 时不分页、一次查全量) */ +export interface AiAssistantReportPageParams { + reporterId: number + moduleCode?: string + reportTimeStart?: string + reportTimeEnd?: string + pageNo?: number + /** 每页条数;传 -1 表示不分页(PAGE_SIZE_NONE) */ + pageSize?: number +} + +/** 分页结果 */ +export interface AiAssistantReportPageResult { + list: AiAssistantReportItem[] + total: number +} + +/** 按日期聚合报告数量(轨迹图) */ +export interface ReportCountByDateItem { + reportTime: string + count: number +} + +/** Dify 知识库(数据集)元信息,由后端持久化 datasetId 等 */ +export interface DifyKbInfoVO { + datasetId?: string + /** 聚合同步文档 ID,与控制台路径 .../documents/{documentId} 一致 */ + documentId?: string + datasetName?: string + knowledgeBaseUrl?: string + lastSyncTime?: string + created?: boolean +} + +/** 单次同步接口返回 */ +export interface DifyKbSyncResultVO { + datasetId?: string + documentId?: string + datasetName?: string + knowledgeBaseUrl?: string + syncRecordId?: number + message?: string +} + +/** 同步历史一行 */ +export interface DifyKbSyncLogItem { + id: number + moduleCode?: string + moduleName?: string + reportId?: number + datasetId?: string + syncStatus?: string + syncMessage?: string + creator?: string + createTime: string +} + +export const AiAssistantReportApi = { + /** 保存报告单据 */ + save: (data: AiAssistantReportSaveReq) => + request.post({ + url: '/ydoyun/ai-assistant-report/save', + data + }), + + /** 查询当前人当前模块的历史报告 */ + getMyList: (params: { reporterId: number; moduleCode?: string }) => + request.get({ + url: '/ydoyun/ai-assistant-report/my-list', + params + }), + + /** 分页查询历史报告(按最新记录排序) */ + getMyReportPage: (params: AiAssistantReportPageParams) => + request.get({ + url: '/ydoyun/ai-assistant-report/my-page', + params + }), + + /** 按部门+统计日一次查询全员汇报(替代 N 次 my-page) */ + listDeptDailyReports: (params: { + deptId: number + reportDate: string + moduleCode?: string + }) => + request.get({ + url: '/ydoyun/ai-assistant-report/dept-daily-list', + params + }), + + /** 查询有报告的日期列表(用于日历点状图,不提供补写) */ + getMyReportDates: (params: { + reporterId: number + moduleCode?: string + startDate: string + endDate: string + }) => + request.get({ + url: '/ydoyun/ai-assistant-report/my-report-dates', + params + }), + + /** 按日期聚合报告提交数量(用于轨迹图) */ + getReportCountByDate: (params: { + reporterId: number + moduleCode?: string + startDate: string + endDate: string + }) => + request.get({ + url: '/ydoyun/ai-assistant-report/report-count-by-date', + params + }), + + /** 按日期查询报告列表(日历点击查看详情) */ + getListByDate: (params: { + reporterId: number + moduleCode?: string + reportDate: string + }) => + request.get({ + url: '/ydoyun/ai-assistant-report/list-by-date', + params + }), + + /** 删除本人「报告日期为当天」的一条记录(后端校验) */ + deleteMyReportToday: (id: number) => + request.delete({ url: '/ydoyun/ai-assistant-report/delete?id=' + id }), + + /** 按每日汇报主键查询详表 */ + getDetailList: (reportId: number) => + request.get({ + url: '/ydoyun/ai-assistant-report/detail/list', + params: { reportId } + }), + + /** 管理端:按主键查详表(报表知识库/日志填报;需 ydoyun:dify-kb:query,可查他人汇报) */ + getDetailListForManage: (reportId: number) => + request.get({ + url: '/ydoyun/ai-assistant-report/detail/list-for-manage', + params: { reportId } + }), + + /** 批量保存详表(与主表分步调用;同步 await 主保存后再调) */ + saveDetailBatch: (data: AiAssistantReportDetailBatchSaveReq) => + request.post({ + url: '/ydoyun/ai-assistant-report/detail/save-batch', + data + }), + + /** Dify 智能解析报告正文 → AI经营诊断详表行(不落库) */ + difyParseDiagnosis: (data: { text: string }) => + request.post({ + url: '/ydoyun/ai-assistant-report/dify-parse-diagnosis', + data + }), + + /** + * 将当前租户、当前页面(moduleCode)下的知识库与 Dify 同步。 + * 后端约定:每个租户 × 每个页面(模块)对应一个 Dify 知识库/数据集, + * 内容包含本租户在该页面下全部店长的历史汇报(由服务端聚合写入)。 + * 先落库当前汇报再调用;返回知识库 id 与可访问链接。 + */ + syncDifyKnowledge: (data: { + reportId: number + moduleCode?: string + moduleName?: string + /** 当前路由对应后台菜单 id,与 meta.menuId 一致;用于 Dify 知识库命名 */ + menuId?: number + }) => + request.post({ + url: '/ydoyun/ai-assistant-report/dify-knowledge/sync', + data + }), + + /** 当前租户 + 当前模块 已绑定的 Dify 知识库元数据 */ + getDifyKbInfo: (params: { moduleCode?: string }) => + request.get({ + url: '/ydoyun/ai-assistant-report/dify-knowledge/info', + params + }), + + /** 同步记录分页(当前租户、当前模块) */ + getDifyKbSyncLogs: (params: { + moduleCode?: string + pageNo?: number + pageSize?: number + }) => + request.get<{ list: DifyKbSyncLogItem[]; total: number }>({ + url: '/ydoyun/ai-assistant-report/dify-knowledge/sync-logs', + params + }) +} diff --git a/src/api/ydoyun/aiModulePrompt/index.ts b/src/api/ydoyun/aiModulePrompt/index.ts new file mode 100644 index 0000000..d44609d --- /dev/null +++ b/src/api/ydoyun/aiModulePrompt/index.ts @@ -0,0 +1,36 @@ +import request from '@/config/axios' + +export interface AiModulePromptSaveReq { + id?: number + componentName: string + moduleName: string + moduleKey: string + prompt?: string +} + +export interface AiModulePromptResp { + id: number + componentName: string + moduleName: string + moduleKey: string + prompt?: string + createTime?: string + updateTime?: string +} + +export const AiModulePromptApi = { + save: (data: AiModulePromptSaveReq) => + request.post({ url: '/ydoyun/ai-module-prompt/save', data }), + + listByComponent: (componentName: string) => + request.get({ + url: '/ydoyun/ai-module-prompt/list-by-component', + params: { componentName } + }), + + getPromptMapByComponent: (componentName: string) => + request.get>({ + url: '/ydoyun/ai-module-prompt/map-by-component', + params: { componentName } + }) +} diff --git a/src/api/ydoyun/aichat/index.ts b/src/api/ydoyun/aichat/index.ts new file mode 100644 index 0000000..ef842ac --- /dev/null +++ b/src/api/ydoyun/aichat/index.ts @@ -0,0 +1,140 @@ +import { getAccessToken, getTenantId, getVisitTenantId } from '@/utils/auth' + +const BASE_URL = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + +export type AiChatStreamDonePayload = { aborted?: boolean } + +/** + * AI 对话流式请求(直接上传图片文件) + * 使用 FormData: query + file(可选) + task_desc(可选) + conversation_id(可选,多轮对话) + */ +export async function aiChatStream( + query: string, + file: File | null | undefined, + onChunk: (text: string) => void, + onError?: (err: string) => void, + onDone?: (payload?: AiChatStreamDonePayload) => void, + options?: { + taskDesc?: string + conversationId?: string + onConversationId?: (id: string) => void + /** 传入后可调用 AbortController.abort() 中止 fetch 与读流 */ + signal?: AbortSignal + } +): Promise { + const token = getAccessToken() + const formData = new FormData() + formData.append('query', query) + if (file) { + formData.append('file', file) + } + if (options?.taskDesc) { + formData.append('task_desc', options.taskDesc) + } + if (options?.conversationId) { + formData.append('conversation_id', options.conversationId) + } + + const headers: Record = { + ...(token ? { Authorization: 'Bearer ' + token } : {}) + } + const tenantId = getTenantId() + if (tenantId) { + headers['tenant-id'] = String(tenantId) + } + const visitTenantId = getVisitTenantId() + if (visitTenantId) { + headers['visit-tenant-id'] = String(visitTenantId) + } + + const signal = options?.signal + let aborted = false + let finished = false + + const finish = () => { + if (finished) return + finished = true + onDone?.({ aborted }) + } + + const processBlock = (block: string) => { + if (!block.startsWith('data: ')) return + const data = block.slice(6).trim() + if (!data || data === '[DONE]') return + try { + const obj = JSON.parse(data) + if (obj.error) { + onError?.(obj.error) + return + } + if (obj.conversation_id) { + options?.onConversationId?.(obj.conversation_id) + } + if (obj.text) { + onChunk(obj.text) + } + } catch { + // 忽略解析错误 + } + } + + try { + const res = await fetch(BASE_URL + '/ydoyun/ai-chat/stream', { + method: 'POST', + headers, + body: formData, + signal + }) + + if (!res.ok) { + const err = await res.text() + onError?.(err || '请求失败') + return + } + + const reader = res.body?.getReader() + if (!reader) { + onError?.('无法读取响应流') + return + } + + const decoder = new TextDecoder() + let buffer = '' + + try { + while (true) { + if (signal?.aborted) { + aborted = true + await reader.cancel().catch(() => {}) + break + } + const { done, value } = await reader.read() + buffer += value ? decoder.decode(value, { stream: true }) : '' + const lines = buffer.split('\n\n') + buffer = lines.pop() || '' + for (const block of lines) { + processBlock(block) + } + if (done) { + if (buffer.trim().startsWith('data: ')) { + processBlock(buffer.trim()) + } + break + } + } + } finally { + if (signal?.aborted) aborted = true + finish() + } + } catch (e: unknown) { + const name = (e as Error)?.name + if (name === 'AbortError' || signal?.aborted) { + aborted = true + finish() + } else { + const msg = e instanceof Error ? e.message : '请求异常' + onError?.(msg || '请求异常') + if (!finished) finish() + } + } +} diff --git a/src/api/ydoyun/difyKbManage/index.ts b/src/api/ydoyun/difyKbManage/index.ts new file mode 100644 index 0000000..aff4112 --- /dev/null +++ b/src/api/ydoyun/difyKbManage/index.ts @@ -0,0 +1,65 @@ +import request from '@/config/axios' + +/** 当前租户已绑定的 Dify 知识库 */ +export interface DifyKbManageItem { + id: number + moduleCode?: string + moduleName?: string + datasetId?: string + datasetName?: string + aggregateDocumentId?: string + existsOnDify?: boolean +} + +export interface DifyKbDocument { + id: string + name?: string + createdAt?: number + indexingStatus?: string + displayStatus?: string +} + +export interface DifyKbModuleOption { + moduleCode: string + moduleName?: string +} + +export interface DifyKbDocumentContent { + content?: string + truncated?: boolean + segmentPieces?: number +} + +export const DifyKbManageApi = { + /** 列表(仅当前租户;可选 moduleCode 精确、moduleName 模糊) */ + listBindings: async (params?: { moduleCode?: string; moduleName?: string }) => { + return await request.get({ url: '/ydoyun/dify-kb-manage/list', params }) + }, + + /** 模块筛选项(去重) */ + getModuleOptions: async () => { + return await request.get({ url: '/ydoyun/dify-kb-manage/module-options' }) + }, + + getDocumentPage: async (params: { datasetId: string; pageNo: number; pageSize: number }) => { + return await request.get({ url: '/ydoyun/dify-kb-manage/document/page', params }) + }, + + getDocumentContent: async (params: { datasetId: string; documentId: string }) => { + return await request.get({ url: '/ydoyun/dify-kb-manage/document/content', params }) + }, + + deleteDataset: async (datasetId: string) => { + return await request.delete({ + url: '/ydoyun/dify-kb-manage/dataset/delete', + params: { datasetId } + }) + }, + + deleteDocument: async (datasetId: string, documentId: string) => { + return await request.delete({ + url: '/ydoyun/dify-kb-manage/document/delete', + params: { datasetId, documentId } + }) + } +} diff --git a/src/api/ydoyun/weather/index.ts b/src/api/ydoyun/weather/index.ts new file mode 100644 index 0000000..74d95c5 --- /dev/null +++ b/src/api/ydoyun/weather/index.ts @@ -0,0 +1,12 @@ +import request from '@/config/axios' + +/** + * 后端代理高德天气,返回实况 + 近 3 日预报聚合。 + * @param location 城市名称(如「长沙市」)或 6 位行政区划编码 adcode + */ +export const getWeatherAggregate = (location: string) => { + return request.get({ + url: '/ydoyun/weather/wttr', + params: { location } + }) +} diff --git a/src/components/AiAssistant/AiAssistantMark.vue b/src/components/AiAssistant/AiAssistantMark.vue new file mode 100644 index 0000000..309551a --- /dev/null +++ b/src/components/AiAssistant/AiAssistantMark.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/components/AiAssistant/AiAssistantProvider.vue b/src/components/AiAssistant/AiAssistantProvider.vue new file mode 100644 index 0000000..6369bc9 --- /dev/null +++ b/src/components/AiAssistant/AiAssistantProvider.vue @@ -0,0 +1,654 @@ + + + + + diff --git a/src/components/AiAssistant/AiImageChatDialog.vue b/src/components/AiAssistant/AiImageChatDialog.vue new file mode 100644 index 0000000..b1aa622 --- /dev/null +++ b/src/components/AiAssistant/AiImageChatDialog.vue @@ -0,0 +1,1387 @@ + + + + + + diff --git a/src/components/AiAssistant/ReportDocumentModal.vue b/src/components/AiAssistant/ReportDocumentModal.vue new file mode 100644 index 0000000..6489dfa --- /dev/null +++ b/src/components/AiAssistant/ReportDocumentModal.vue @@ -0,0 +1,2571 @@ + + + + + + + + diff --git a/src/components/AiAssistant/index.vue b/src/components/AiAssistant/index.vue new file mode 100644 index 0000000..8c948a4 --- /dev/null +++ b/src/components/AiAssistant/index.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/components/AiAssistant/useAiAssistant.ts b/src/components/AiAssistant/useAiAssistant.ts new file mode 100644 index 0000000..a8bcc6b --- /dev/null +++ b/src/components/AiAssistant/useAiAssistant.ts @@ -0,0 +1,61 @@ +import type { InjectionKey } from 'vue' + +export interface ChatMessage { + role: 'user' | 'assistant' + text: string + picPreview?: string + /** 待确认:来自卡片 AI 分析,显示取消/分析按钮,点击分析后才发送 */ + pending?: boolean +} + +export interface OpenWithScreenshotOptions { + /** 模块名称,用于拼接 taskDesc:【模块名】提示词 */ + moduleName?: string + /** 模块编码,如 SupplierPerformance:main */ + moduleCode?: string + /** 模块 AI 提示词,发送时与 moduleName 拼接为 taskDesc 传给 Dify */ + prompt?: string + /** 待确认卡片上的说明文案(默认「分析其中的数据」) */ + pendingUserText?: string +} + +export interface AiAssistantApi { + openWithScreenshot: (target: string | HTMLElement, options?: OpenWithScreenshotOptions) => Promise + /** 设置页面查询中状态,为 true 时悬浮框禁用并显示加载动画 */ + setPageLoading: (loading: boolean) => void + /** 设置当前页面的 AI 任务提示词,点击悬浮按钮时代入;离开页面时传 null 清除 */ + setPagePrompt: (prompt: string | null) => void + /** 设置当前页面模块名称,用于报告单据;离开页面时传 null 清除 */ + setPageModuleName: (moduleName: string | null) => void + /** 设置当前页面模块编码,用于报告单据;离开页面时传 null 清除 */ + setPageModuleCode: (moduleCode: string | null) => void + /** 设置当前页面截图目标(选择器或元素),保存报告时自动截取该区域 Y 轴全部内容;离开页面时传 null */ + setScreenshotTarget: (target: string | HTMLElement | null) => void + /** + * 是否显示全局「AI 决策助手」悬浮入口;默认 false,仅允许展示的页面在 onMounted 设为 true、onUnmounted 设回 false + */ + setAiAssistantFabEnabled: (enabled: boolean) => void +} + +/** 报告单据上下文:模块名、模块编码、截图文件、截图目标、截图方法(供单据弹窗使用) */ +export interface AiAssistantReportContext { + moduleName: Ref + moduleCode: Ref + screenshotFile: Ref + /** 保存时自动截图当前组件页面,返回截图 File,无目标时返回 null */ + captureScreenshot: () => Promise +} +export const AI_ASSISTANT_REPORT_CONTEXT_KEY: InjectionKey = Symbol( + 'AiAssistantReportContext' +) as InjectionKey + +export const AI_ASSISTANT_KEY: InjectionKey = Symbol('AiAssistant') as InjectionKey +export const AI_CHAT_MESSAGES_KEY: InjectionKey> = Symbol('AiChatMessages') as InjectionKey> + +export function useAiAssistant(): AiAssistantApi { + const api = inject(AI_ASSISTANT_KEY) + if (!api) { + throw new Error('useAiAssistant must be used within AiAssistantProvider') + } + return api +} diff --git a/src/hooks/web/useAiModulePromptEditor.ts b/src/hooks/web/useAiModulePromptEditor.ts new file mode 100644 index 0000000..b08f878 --- /dev/null +++ b/src/hooks/web/useAiModulePromptEditor.ts @@ -0,0 +1,15 @@ +import { computed } from 'vue' +import { useUserStore } from '@/store/modules/user' + +/** + * 模块 AI 提示词编辑入口:仅 admin / super_admin 可见(与系统 hasRole 中超管逻辑一致) + */ +export function useAiModulePromptEditor() { + const userStore = useUserStore() + const canEditAiModulePrompt = computed(() => { + const roles = userStore.getRoles ?? [] + if (!Array.isArray(roles)) return false + return roles.some((r) => r === 'admin' || r === 'super_admin') + }) + return { canEditAiModulePrompt } +} diff --git a/src/views/ydoyun/aichat/index.vue b/src/views/ydoyun/aichat/index.vue new file mode 100644 index 0000000..73ce8d9 --- /dev/null +++ b/src/views/ydoyun/aichat/index.vue @@ -0,0 +1,396 @@ + + + + + diff --git a/src/views/ydoyun/difykb/index.vue b/src/views/ydoyun/difykb/index.vue new file mode 100644 index 0000000..0a5b945 --- /dev/null +++ b/src/views/ydoyun/difykb/index.vue @@ -0,0 +1,2270 @@ + + + + + diff --git a/src/views/ydoyun/report/lijun/reportpage6/aiModuleConfig.ts b/src/views/ydoyun/report/lijun/reportpage6/aiModuleConfig.ts new file mode 100644 index 0000000..bf20554 --- /dev/null +++ b/src/views/ydoyun/report/lijun/reportpage6/aiModuleConfig.ts @@ -0,0 +1,14 @@ +/** + * ProductDashboard AI 可分析模块配置 + * 定义任务时需列举这些模块供 AI 分析 + */ +export const PRODUCT_DASHBOARD_COMPONENT = 'ProductDashboard' + +export const PRODUCT_DASHBOARD_MODULES = [ + { key: 'kpi', name: 'KPI 指标卡片', moduleKey: `${PRODUCT_DASHBOARD_COMPONENT}:kpi` }, + { key: 'supplier', name: '供应商表现 (Top 10)', moduleKey: `${PRODUCT_DASHBOARD_COMPONENT}:supplier` }, + { key: 'pie', name: '中类销售排名 Top 5', moduleKey: `${PRODUCT_DASHBOARD_COMPONENT}:pie` }, + { key: 'productList', name: '商品明细列表', moduleKey: `${PRODUCT_DASHBOARD_COMPONENT}:productList` } +] as const + +export type ProductDashboardModuleKey = (typeof PRODUCT_DASHBOARD_MODULES)[number]['key'] diff --git a/src/views/ydoyun/report/lijun/reportpage6/components/AiPromptEditDialog.vue b/src/views/ydoyun/report/lijun/reportpage6/components/AiPromptEditDialog.vue new file mode 100644 index 0000000..34a2848 --- /dev/null +++ b/src/views/ydoyun/report/lijun/reportpage6/components/AiPromptEditDialog.vue @@ -0,0 +1,168 @@ + + + + + + + diff --git a/src/views/ydoyun/report/salesdaily/index.vue b/src/views/ydoyun/report/salesdaily/index.vue new file mode 100644 index 0000000..a18c8fc --- /dev/null +++ b/src/views/ydoyun/report/salesdaily/index.vue @@ -0,0 +1,2251 @@ + + + + + + diff --git a/src/views/ydoyun/tagconfig/EditSqlModal.vue b/src/views/ydoyun/tagconfig/EditSqlModal.vue new file mode 100644 index 0000000..ad4ef1b --- /dev/null +++ b/src/views/ydoyun/tagconfig/EditSqlModal.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/src/views/ydoyun/tagconfig/报表库标签表建表.sql b/src/views/ydoyun/tagconfig/报表库标签表建表.sql new file mode 100644 index 0000000..5c54b83 --- /dev/null +++ b/src/views/ydoyun/tagconfig/报表库标签表建表.sql @@ -0,0 +1,45 @@ +-- ========== 报表库标签表建表脚本(SQL Server 语法)========== +-- 同步时由 initTagTables 执行,也可手动执行。color 用于保存标签颜色。 +-- ============================================================ + +-- 删除旧表(可选,会清空数据) +-- DROP TABLE IF EXISTS ydoyun_tag_product; +-- DROP TABLE IF EXISTS ydoyun_tag_supplier; +-- DROP TABLE IF EXISTS ydoyun_tag_store; +-- DROP TABLE IF EXISTS ydoyun_tag_member; + +-- 创建产品标签表 +CREATE TABLE ydoyun_tag_product ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + code NVARCHAR(64) NOT NULL, + name NVARCHAR(128) NOT NULL, + color NVARCHAR(32) NULL, + create_time DATETIME NOT NULL DEFAULT GETDATE() +); + +-- 创建供货商标签表 +CREATE TABLE ydoyun_tag_supplier ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + code NVARCHAR(64) NOT NULL, + name NVARCHAR(128) NOT NULL, + color NVARCHAR(32) NULL, + create_time DATETIME NOT NULL DEFAULT GETDATE() +); + +-- 创建门店标签表 +CREATE TABLE ydoyun_tag_store ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + code NVARCHAR(64) NOT NULL, + name NVARCHAR(128) NOT NULL, + color NVARCHAR(32) NULL, + create_time DATETIME NOT NULL DEFAULT GETDATE() +); + +-- 创建会员标签表 +CREATE TABLE ydoyun_tag_member ( + id BIGINT IDENTITY(1,1) PRIMARY KEY, + code NVARCHAR(64) NOT NULL, + name NVARCHAR(128) NOT NULL, + color NVARCHAR(32) NULL, + create_time DATETIME NOT NULL DEFAULT GETDATE() +); diff --git a/src/views/ydoyun/tagconfig/表结构.sql b/src/views/ydoyun/tagconfig/表结构.sql new file mode 100644 index 0000000..da5ddc6 --- /dev/null +++ b/src/views/ydoyun/tagconfig/表结构.sql @@ -0,0 +1,168 @@ +-- ========== 变更说明 ========== +-- ydoyun_tag: 【新增】sql_script_resolved;【删除】无 +-- ydoyun_tag_config: 【新增】auto_sync、product_category_ids、sync_warehouse_ids;【删除】无 +-- ydoyun_tag_sync_history: 同步主表,记录每次同步任务;【删除】无 +-- ydoyun_tag_sync_detail: 【新增】同步详情表,记录每条标签的执行情况(同步了哪些标签、执行了哪些脚本、成功与否) +-- ============================= + +-- ---------------------------- +-- 1. 标准标签表 +-- ---------------------------- +create table ydoyun_tag +( + id bigint auto_increment comment '主键ID' + primary key, + name varchar(50) not null comment '标签名称', + type varchar(20) not null comment '标签类型(product/store/supplier/member)', + expression varchar(200) null comment '标签表达式', + color varchar(20) null comment '标签颜色', + sql_script text null comment 'SQL脚本(含${参数}占位符)', + sql_script_resolved text null comment '已拼接参数的SQL,执行时直接使用', -- 【新增】 + use_params tinyint(1) default 0 not null comment '是否代入参数(0否 1是)', + params varchar(255) null comment '参数列表(逗号分隔)', + param_values text null comment '参数值 JSON,如 {"参数1":"28","参数2":"90"}', + creator varchar(64) default '' null comment '创建者', + create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', + updater varchar(64) default '' null comment '更新者', + update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + deleted bit default b'0' not null comment '是否删除', + tenant_id bigint default 0 not null comment '租户编号', + constraint uk_tenant_name_type unique (tenant_id, name, type) +) comment '标准标签表'; + +create index idx_tenant_id on ydoyun_tag (tenant_id); +create index idx_type on ydoyun_tag (type); + +-- ---------------------------- +-- 2. 标签同步配置表 +-- ---------------------------- +create table ydoyun_tag_config +( + id bigint auto_increment comment '主键ID' + primary key, + database_id bigint not null comment '同步数据源ID(ydoyun_report_database.id)', + cron_expression varchar(100) null comment '定时任务表达式(CRON)', + auto_sync tinyint(1) default 0 not null comment '是否开启自动同步(0否 1是)', -- 【新增】 + product_category_ids varchar(500) null comment '剔除大类(多选,逗号分隔)', -- 【新增】 + sync_warehouse_ids varchar(500) null comment '剔除仓库(多选,逗号分隔)', -- 【新增】 + last_sync_time datetime null comment '上次同步时间', + next_sync_time datetime null comment '下次预计同步时间', + creator varchar(64) default '' null comment '创建者', + create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', + updater varchar(64) default '' null comment '更新者', + update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + deleted bit default b'0' not null comment '是否删除', + tenant_id bigint default 0 not null comment '租户编号' +) comment '标签同步配置表'; + +create index idx_database_id on ydoyun_tag_config (database_id); +create index idx_tenant_id on ydoyun_tag_config (tenant_id); + +-- ---------------------------- +-- 3. 标签同步记录表(主表) +-- ---------------------------- +create table ydoyun_tag_sync_history +( + id bigint auto_increment comment '主键ID' + primary key, + tag_config_id bigint not null comment '标签同步配置ID(ydoyun_tag_config.id)', + sync_type varchar(20) default 'manual' not null comment '同步类型(manual手动/cron定时)', + sync_time datetime default CURRENT_TIMESTAMP not null comment '同步时间', + sync_status varchar(20) not null comment '同步状态(success成功/fail失败/partial部分成功)', + total_count int default 0 null comment '本次同步标签总数', + success_count int default 0 null comment '成功数', + fail_count int default 0 null comment '失败数', + record_count bigint default 0 null comment '同步记录总数', + error_message varchar(500) null comment '汇总错误信息(如有)', + creator varchar(64) default '' null comment '创建者', + create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', + updater varchar(64) default '' null comment '更新者', + update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + deleted bit default b'0' not null comment '是否删除', + tenant_id bigint default 0 not null comment '租户编号' +) comment '标签同步记录表(主表)'; + +create index idx_sync_time on ydoyun_tag_sync_history (sync_time); +create index idx_tag_config_id on ydoyun_tag_sync_history (tag_config_id); +create index idx_tenant_id on ydoyun_tag_sync_history (tenant_id); + +-- ---------------------------- +-- 4. 标签同步详情表(每条标签的执行明细) +-- ---------------------------- +create table ydoyun_tag_sync_detail +( + id bigint auto_increment comment '主键ID' + primary key, + sync_history_id bigint not null comment '同步记录ID(ydoyun_tag_sync_history.id)', + tag_id bigint not null comment '标签ID(ydoyun_tag.id)', + tag_name varchar(50) null comment '标签名称', + tag_type varchar(20) null comment '标签类型', + sql_executed text null comment '实际执行的SQL脚本', + exec_status varchar(20) not null comment '执行状态(success/fail)', + record_count bigint default 0 null comment '影响记录数', + error_message varchar(1000) null comment '错误信息(失败时)', + exec_order int default 0 null comment '执行顺序', + creator varchar(64) default '' null comment '创建者', + create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', + updater varchar(64) default '' null comment '更新者', + update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + deleted bit default b'0' not null comment '是否删除', + tenant_id bigint default 0 not null comment '租户编号' +) comment '标签同步详情表(每条标签的执行明细)'; + +create index idx_sync_history_id on ydoyun_tag_sync_detail (sync_history_id); +create index idx_tag_id on ydoyun_tag_sync_detail (tag_id); +create index idx_tenant_id on ydoyun_tag_sync_detail (tenant_id); + + +-- ========== 已有表时的增量修改 SQL ========== + +-- ydoyun_tag 【新增】sql_script_resolved +ALTER TABLE ydoyun_tag ADD COLUMN sql_script_resolved text null comment '已拼接参数的SQL,执行时直接使用' AFTER sql_script; + +-- ydoyun_tag 【新增】param_values +ALTER TABLE ydoyun_tag ADD COLUMN param_values text null comment '参数值 JSON,如 {"参数1":"28","参数2":"90"}' AFTER params; + +-- ydoyun_tag_config 【新增】auto_sync、product_category_ids、sync_warehouse_ids +ALTER TABLE ydoyun_tag_config ADD COLUMN auto_sync tinyint(1) default 0 not null comment '是否开启自动同步(0否 1是)' AFTER cron_expression; +ALTER TABLE ydoyun_tag_config ADD COLUMN product_category_ids varchar(500) null comment '剔除大类(多选,逗号分隔)' AFTER auto_sync; +ALTER TABLE ydoyun_tag_config ADD COLUMN sync_warehouse_ids varchar(500) null comment '剔除仓库(多选,逗号分隔)' AFTER auto_sync; + +-- ydoyun_tag_sync_history 【新增】total_count、success_count、fail_count(若原表无则添加) +ALTER TABLE ydoyun_tag_sync_history ADD COLUMN total_count int default 0 null comment '本次同步标签总数' AFTER sync_status; +ALTER TABLE ydoyun_tag_sync_history ADD COLUMN success_count int default 0 null comment '成功数' AFTER total_count; +ALTER TABLE ydoyun_tag_sync_history ADD COLUMN fail_count int default 0 null comment '失败数' AFTER success_count; + +-- ydoyun_tag_sync_detail 【新增】整张表(若不存在则执行下方建表) +-- 若已有 ydoyun_tag_sync_history,需先执行上方的 ALTER,再执行下方建表 +CREATE TABLE IF NOT EXISTS ydoyun_tag_sync_detail +( + id bigint auto_increment comment '主键ID' primary key, + sync_history_id bigint not null comment '同步记录ID(ydoyun_tag_sync_history.id)', + tag_id bigint not null comment '标签ID(ydoyun_tag.id)', + tag_name varchar(50) null comment '标签名称', + tag_type varchar(20) null comment '标签类型', + sql_executed text null comment '实际执行的SQL脚本', + exec_status varchar(20) not null comment '执行状态(success/fail)', + record_count bigint default 0 null comment '影响记录数', + error_message varchar(1000) null comment '错误信息(失败时)', + exec_order int default 0 null comment '执行顺序', + creator varchar(64) default '' null comment '创建者', + create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间', + updater varchar(64) default '' null comment '更新者', + update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', + deleted bit default b'0' not null comment '是否删除', + tenant_id bigint default 0 not null comment '租户编号' +) comment '标签同步详情表(每条标签的执行明细)'; +CREATE INDEX idx_sync_history_id ON ydoyun_tag_sync_detail (sync_history_id); +CREATE INDEX idx_tag_id ON ydoyun_tag_sync_detail (tag_id); +CREATE INDEX idx_tenant_id ON ydoyun_tag_sync_detail (tenant_id); + + +-- ========== 报表库标签表(ydoyun_tag_*)新增 color 字段 ========== +-- 以下表在报表数据库中,用于同步标签数据。若表已存在且需保留数据,可执行以下 SQL Server 语法: +-- ALTER TABLE ydoyun_tag_product ADD color NVARCHAR(32) NULL; +-- ALTER TABLE ydoyun_tag_supplier ADD color NVARCHAR(32) NULL; +-- ALTER TABLE ydoyun_tag_store ADD color NVARCHAR(32) NULL; +-- ALTER TABLE ydoyun_tag_member ADD color NVARCHAR(32) NULL; +-- 注:执行「初始化标签库」会删除并重建这些表,新表已包含 color 字段。 diff --git a/src/views/ydoyun/日报表 - 副本1.HTML b/src/views/ydoyun/日报表 - 副本1.HTML new file mode 100644 index 0000000..c133289 --- /dev/null +++ b/src/views/ydoyun/日报表 - 副本1.HTML @@ -0,0 +1,297 @@ + + + + + 销售日报 + + + + +
+

销售日报

+
昨日 12/18° ☁️
+
今日 14/22° ☀️
+
3/25 15/24° 🌧️
+
3/26 11/19° ☁️
+
3/27 10/17° ⛅
+
+ +
+ + + + +
+
+
+
全店销售达成
+
+ +
+
+ 今日目标完成率 + 80.1% +
+
+
+
+
+
+
+ 0 + 时间进度: 75% + 目标: 100% +
+
+ +
+
+ 本月累计完成率 + 52.4% +
+
+
+
+
+
+
+ 0 + 时间进度: 77% + 目标: 100% +
+
+
+ +
+
当日实收
40,042
+
昨日实收
42,935
+
当日销量
1,751
+
昨日销量
1,925
+ +
同比增长
+11.4%
+
会员消费 / 会销比
18,100/ 45.2%
+
小票总数 / 客单价
648/ ¥61.8
+
去年同期
35,946
+ +
标准金额
49,218
+
当月累计
629,412
+
当月目标
1,200,000
+
毛利预估
--
+
+
+ +
+
时段销售趋势 (平滑曲线图)
+
+ + + + + + + + + + + +
+ 8:0011:0014:0017:00 (18:30处线)20:0023:00 +
+
+
+ +
+
品类全维度深度分析 (实收/标准/正价/特价/对比/目标)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
品牌品类当日概况销售分类对比昨日进度
总销量总金额标准金额成本正价(金额/量)特价(金额/量)销量金额月累计金额日目标日目标对比月目标月目标对比
女装19310,79915,105--9,152 / 1371,647 / 561859,726163,826252,000 +
+
+
+
+
252,000 +
+
+
+
+
男装1328,90810,714--6,969 / 791,939 / 531569,299136,944318,000 +
+
+
+
+
252,000 +
+
+
+
+
童装1375,9786,914--4,666 / 821,196 / 511586,92654,634120,000 +
+
+
+
+
252,000 +
+
+
+
+
总计1,75140,042----31,6178,4251,92542,935629,4121,200,000--
+
+ + \ No newline at end of file