| | |
| | | </el-table> |
| | | </div> |
| | | |
| | | <!-- æåä¸å¨ç» --> |
| | | <div v-if="message.manufacturingData" class="manufacturing-card"> |
| | | <div class="manufacturing-card__title">{{ getManufacturingTypeLabel(message.type) }}</div> |
| | | |
| | | <div |
| | | v-if="message.manufacturingData.summaryEntries?.length || message.manufacturingData.coreMetrics?.length" |
| | | class="manufacturing-summary-grid" |
| | | > |
| | | <div |
| | | v-for="(entry, entryIndex) in message.manufacturingData.summaryEntries" |
| | | :key="`summary-${entry.key}-${entryIndex}`" |
| | | class="manufacturing-summary-item" |
| | | > |
| | | <span class="manufacturing-summary-label">{{ entry.label }}</span> |
| | | <strong class="manufacturing-summary-value">{{ entry.value }}</strong> |
| | | </div> |
| | | <div |
| | | v-for="(metric, metricIndex) in message.manufacturingData.coreMetrics" |
| | | :key="`core-${metric.key}-${metricIndex}`" |
| | | class="manufacturing-summary-item manufacturing-summary-item--core" |
| | | > |
| | | <span class="manufacturing-summary-label">{{ metric.label }}</span> |
| | | <strong class="manufacturing-summary-value">{{ metric.value }}</strong> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="message.manufacturingData.warningItems?.length" class="manufacturing-warning-list"> |
| | | <div |
| | | v-for="(warning, warningIndex) in message.manufacturingData.warningItems" |
| | | :key="`warning-${warning.title || warningIndex}`" |
| | | class="manufacturing-warning-item" |
| | | > |
| | | <div class="manufacturing-warning-item__head"> |
| | | <el-tag size="small" :type="getManufacturingWarningLevelType(warning.level)"> |
| | | {{ getManufacturingWarningLevelLabel(warning.level) }} |
| | | </el-tag> |
| | | <strong>{{ warning.title || `é¢è¦ ${warningIndex + 1}` }}</strong> |
| | | <span v-if="warning.count !== '' && warning.count !== null && warning.count !== undefined" class="manufacturing-warning-count"> |
| | | {{ warning.count }} |
| | | </span> |
| | | </div> |
| | | <p v-if="warning.detail" class="manufacturing-warning-detail">{{ warning.detail }}</p> |
| | | </div> |
| | | </div> |
| | | |
| | | <div |
| | | v-if="message.manufacturingData.listItems?.length && message.manufacturingData.columns?.length" |
| | | class="table-wrapper manufacturing-table-wrapper" |
| | | > |
| | | <el-table :data="message.manufacturingData.listItems" border stripe size="small" style="width: 100%"> |
| | | <el-table-column |
| | | v-for="col in message.manufacturingData.columns" |
| | | :key="col" |
| | | :label="getStructuredFieldLabel(col)" |
| | | min-width="140" |
| | | show-overflow-tooltip |
| | | > |
| | | <template #default="{ row }"> |
| | | {{ formatStructuredValue(row[col]) }} |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | |
| | | <div v-if="message.manufacturingData.actionCards?.length" class="manufacturing-action-list"> |
| | | <div |
| | | v-for="(card, cardIndex) in message.manufacturingData.actionCards" |
| | | :key="card.runtimeKey || `${card.code}-${card.targetApi}-${cardIndex}`" |
| | | class="manufacturing-action-card" |
| | | > |
| | | <div class="manufacturing-action-card__head"> |
| | | <strong>{{ card.name || `å¨ä½ ${cardIndex + 1}` }}</strong> |
| | | <el-tag size="small" type="info">{{ getNormalizedRequestMethod(card.method) }}</el-tag> |
| | | </div> |
| | | <div class="manufacturing-action-card__meta"> |
| | | <span>{{ card.code || '--' }}</span> |
| | | <span>{{ card.targetApi || '--' }}</span> |
| | | </div> |
| | | <p v-if="card.description" class="manufacturing-action-card__desc">{{ card.description }}</p> |
| | | <div v-if="card.requiredFields?.length" class="manufacturing-required-fields"> |
| | | <span>å¿
å¡«åæ®µ</span> |
| | | <el-tag |
| | | v-for="field in card.requiredFields" |
| | | :key="field" |
| | | size="small" |
| | | type="warning" |
| | | > |
| | | {{ getStructuredPathLabel(field) }} |
| | | </el-tag> |
| | | </div> |
| | | <el-input |
| | | v-model="card.payloadText" |
| | | type="textarea" |
| | | :rows="6" |
| | | resize="vertical" |
| | | :disabled="card.executing" |
| | | placeholder="请è¾å
¥ JSON 请æ±åæ°" |
| | | /> |
| | | <div class="manufacturing-action-footer"> |
| | | <span |
| | | v-if="card.executeResult" |
| | | :class="['manufacturing-action-result', card.executeError ? 'error' : 'success']" |
| | | > |
| | | {{ card.executeResult }} |
| | | </span> |
| | | <el-button |
| | | type="primary" |
| | | size="small" |
| | | :loading="card.executing" |
| | | @click="executeManufacturingAction(message, card, cardIndex)" |
| | | > |
| | | 确认并æ§è¡ |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="message.purchaseAnalysisData" class="purchase-confirm-card"> |
| | | <div class="purchase-confirm-header"> |
| | | <span>{{ businessTypeLabelMap[message.purchaseAnalysisData.businessType] || message.purchaseAnalysisData.businessType || 'éè´ä¸å¡' }}</span> |
| | |
| | | import request from '@/utils/request' |
| | | import * as echarts from 'echarts' |
| | | import { Cpu, User, Plus, Timer, Delete, ChatDotSquare, VideoPause, Upload, Document, Close, Promotion, RefreshRight } from '@element-plus/icons-vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { builtInAssistants, generalAssistant } from './assistants' |
| | | import todoAssistantAvatar from '@/assets/AI/å¾
å婿.png' |
| | | import salesAssistantAvatar from '@/assets/AI/éå®å©æ.png' |
| | |
| | | payment_registration: '仿¬¾ç»è®°', |
| | | purchase_return_order: 'éè´éè´§å', |
| | | unknown: 'æªç¥éè´ä¸å¡' |
| | | } |
| | | const manufacturingStructuredTypeSet = new Set([ |
| | | 'manufacturing_site_snapshot', |
| | | 'manufacturing_plan_list', |
| | | 'manufacturing_workorder_list', |
| | | 'manufacturing_device_list', |
| | | 'manufacturing_device_repair_list', |
| | | 'manufacturing_quality_list', |
| | | 'manufacturing_material_list', |
| | | 'manufacturing_exception_list', |
| | | 'manufacturing_warning', |
| | | 'manufacturing_analysis', |
| | | 'manufacturing_action_plan' |
| | | ]) |
| | | const manufacturingListTypeSet = new Set([ |
| | | 'manufacturing_plan_list', |
| | | 'manufacturing_workorder_list', |
| | | 'manufacturing_device_list', |
| | | 'manufacturing_device_repair_list', |
| | | 'manufacturing_quality_list', |
| | | 'manufacturing_material_list', |
| | | 'manufacturing_exception_list' |
| | | ]) |
| | | const manufacturingTypeLabelMap = { |
| | | manufacturing_site_snapshot: 'ç产ç°åºæ¦è§', |
| | | manufacturing_plan_list: 'è®¡åæ¥è¯¢', |
| | | manufacturing_workorder_list: 'å·¥åæ¥è¯¢', |
| | | manufacturing_device_list: 'è®¾å¤æ¥è¯¢', |
| | | manufacturing_device_repair_list: '设å¤ç»´ä¿®è®°å½æ¥è¯¢', |
| | | manufacturing_quality_list: 'è´¨éæ¥è¯¢', |
| | | manufacturing_material_list: 'ç©ææ¥è¯¢', |
| | | manufacturing_exception_list: 'å¼å¸¸æ¥è¯¢', |
| | | manufacturing_warning: 'é¢è¦çæ¿', |
| | | manufacturing_analysis: 'ç»è¥åæ', |
| | | manufacturing_action_plan: 'åç建议' |
| | | } |
| | | const structuredFieldLabelMap = { |
| | | workOrderNo: 'å·¥åå·', |
| | | planEndTime: '计åç»ææ¶é´', |
| | | planStartTime: '计åå¼å§æ¶é´', |
| | | timeRange: 'æ¶é´èå´', |
| | | startDate: 'å¼å§æ¥æ', |
| | | endDate: 'ç»ææ¥æ', |
| | | warningCount: 'é¢è¦æ°é', |
| | | overduePlanCount: 'é¾æè®¡åæ°', |
| | | overdueWorkOrderCount: '龿工忰', |
| | | actionCount: '建议å¨ä½æ°', |
| | | qualityOpenCount: 'è´¨éå¾
å¤çæ°', |
| | | lowStockCount: 'ä½åºåæ°', |
| | | exceptionCount: 'å¼å¸¸æ°', |
| | | userId: 'ç¨æ·ID', |
| | | tenantId: 'ç§æ·ID', |
| | | status: 'ç¶æ', |
| | | deviceName: '设å¤åç§°', |
| | | deviceModel: '设å¤åå·', |
| | | pendingRepairCount: 'å¾
ç»´ä¿®æ°', |
| | | repairTime: 'ç»´ä¿®æ¶é´', |
| | | repairName: 'æ¥ä¿®äºº', |
| | | maintenanceName: '维修人å', |
| | | level: 'é¢è¦ç级', |
| | | title: 'æ é¢', |
| | | count: 'æ°é', |
| | | detail: '详æ
', |
| | | remark: '夿³¨', |
| | | createTime: 'å建æ¶é´', |
| | | updateTime: 'æ´æ°æ¶é´', |
| | | exceptionType: 'å¼å¸¸ç±»å', |
| | | materialName: 'ç©æåç§°', |
| | | stockQty: 'åºåé' |
| | | } |
| | | const purchasePayloadFieldLabelMap = { |
| | | purchaseLedgers: 'éè´å°è´¦', |
| | |
| | | inventoryWarningQuantity: 'inventoryWarningQuantity', |
| | | isInspected: 'isInspected', |
| | | isChecked: 'isInspected' |
| | | } |
| | | const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) |
| | | |
| | | const stringifyStructuredPayload = (value, spaces = 2) => { |
| | | if (typeof value === 'string') return value |
| | | try { |
| | | return JSON.stringify(value ?? {}, null, spaces) |
| | | } catch (err) { |
| | | return '{}' |
| | | } |
| | | } |
| | | |
| | | const structuredFieldTokenLabelMap = { |
| | | time: 'æ¶é´', |
| | | range: 'èå´', |
| | | start: 'å¼å§', |
| | | end: 'ç»æ', |
| | | date: 'æ¥æ', |
| | | warning: 'é¢è¦', |
| | | overdue: '龿', |
| | | plan: '计å', |
| | | work: 'å·¥', |
| | | order: 'å', |
| | | workorder: 'å·¥å', |
| | | count: 'æ°é', |
| | | quality: 'è´¨é', |
| | | low: 'ä½', |
| | | stock: 'åºå', |
| | | exception: 'å¼å¸¸', |
| | | action: 'å¨ä½', |
| | | user: 'ç¨æ·', |
| | | tenant: 'ç§æ·', |
| | | id: 'ID', |
| | | no: 'ç¼å·', |
| | | number: 'ç¼å·', |
| | | code: 'ç¼ç ', |
| | | name: 'åç§°', |
| | | status: 'ç¶æ', |
| | | level: 'ç级', |
| | | title: 'æ é¢', |
| | | detail: '详æ
', |
| | | total: 'æ»æ°', |
| | | rate: 'æ¯ç', |
| | | type: 'ç±»å', |
| | | pending: 'å¾
', |
| | | repair: 'ç»´ä¿®', |
| | | device: '设å¤', |
| | | material: 'ç©æ' |
| | | } |
| | | |
| | | const convertStructuredFieldKeyToChinese = (fieldKey = '') => { |
| | | const key = String(fieldKey || '').trim() |
| | | if (!key) return '-' |
| | | if (structuredFieldLabelMap[key]) return structuredFieldLabelMap[key] |
| | | if (/[\u4e00-\u9fa5]/.test(key)) return key |
| | | |
| | | const rawTokens = key |
| | | .replace(/([a-z0-9])([A-Z])/g, '$1 $2') |
| | | .replace(/[_-]+/g, ' ') |
| | | .trim() |
| | | .split(/\s+/) |
| | | .filter(Boolean) |
| | | .map(token => token.toLowerCase()) |
| | | |
| | | if (!rawTokens.length) return 'åæ®µ' |
| | | |
| | | const mappedTokens = rawTokens |
| | | .map(token => structuredFieldTokenLabelMap[token] || '') |
| | | .filter(Boolean) |
| | | |
| | | if (mappedTokens.length) { |
| | | return mappedTokens.join('') |
| | | } |
| | | |
| | | return 'åæ®µ' |
| | | } |
| | | |
| | | const getStructuredFieldLabel = (fieldKey = '') => { |
| | | return convertStructuredFieldKeyToChinese(fieldKey) |
| | | } |
| | | |
| | | const getStructuredPathLabel = (fieldPath = '') => { |
| | | const path = String(fieldPath || '').trim() |
| | | if (!path) return '-' |
| | | |
| | | const segments = path |
| | | .replace(/\[(\d+)]/g, '.$1') |
| | | .split('.') |
| | | .filter(Boolean) |
| | | |
| | | if (!segments.length) return getStructuredFieldLabel(path) |
| | | |
| | | return segments.map((segment) => { |
| | | if (/^\d+$/.test(segment)) { |
| | | return `第${Number(segment) + 1}项` |
| | | } |
| | | return getStructuredFieldLabel(segment) |
| | | }).join(' / ') |
| | | } |
| | | |
| | | const formatStructuredValue = (value) => { |
| | | if (value === null || value === undefined || value === '') return '-' |
| | | if (Array.isArray(value)) { |
| | | const preview = value.slice(0, 3).map(item => formatStructuredValue(item)).join('ã') |
| | | return value.length > 3 ? `${preview} ç${value.length}项` : preview |
| | | } |
| | | if (isPlainObject(value)) return stringifyStructuredPayload(value, 0) |
| | | return String(value) |
| | | } |
| | | |
| | | const normalizeManufacturingSummaryEntries = (summary) => { |
| | | if (!isPlainObject(summary)) return [] |
| | | return Object.entries(summary) |
| | | .filter(([, value]) => value !== undefined && value !== null && `${value}`.trim() !== '') |
| | | .map(([key, value]) => ({ |
| | | key, |
| | | label: getStructuredFieldLabel(key), |
| | | value: formatStructuredValue(value) |
| | | })) |
| | | } |
| | | |
| | | const normalizeManufacturingCoreMetrics = (coreMetrics) => { |
| | | if (Array.isArray(coreMetrics)) { |
| | | return coreMetrics.map((item, index) => { |
| | | if (isPlainObject(item)) { |
| | | const label = item.label || item.name || item.key || `ææ ${index + 1}` |
| | | const metricValue = item.value ?? item.metricValue ?? item.data ?? '-' |
| | | const unit = item.unit ? ` ${item.unit}` : '' |
| | | return { |
| | | key: String(item.key || item.name || index), |
| | | label, |
| | | value: `${formatStructuredValue(metricValue)}${unit}`.trim() |
| | | } |
| | | } |
| | | return { |
| | | key: String(index), |
| | | label: `ææ ${index + 1}`, |
| | | value: formatStructuredValue(item) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (isPlainObject(coreMetrics)) { |
| | | return Object.entries(coreMetrics).map(([key, value]) => ({ |
| | | key, |
| | | label: getStructuredFieldLabel(key), |
| | | value: formatStructuredValue(value) |
| | | })) |
| | | } |
| | | |
| | | return [] |
| | | } |
| | | |
| | | const normalizeManufacturingWarningItems = (items = []) => { |
| | | if (!Array.isArray(items)) return [] |
| | | return items |
| | | .filter(item => isPlainObject(item)) |
| | | .map(item => ({ |
| | | level: String(item.level || '').toLowerCase(), |
| | | title: item.title || '', |
| | | count: item.count ?? '', |
| | | detail: item.detail ?? '' |
| | | })) |
| | | } |
| | | |
| | | const inferManufacturingColumns = (items = []) => { |
| | | if (!Array.isArray(items) || !items.length) return [] |
| | | const fieldSet = new Set() |
| | | items.forEach((item) => { |
| | | if (!isPlainObject(item)) return |
| | | Object.keys(item).forEach((key) => fieldSet.add(key)) |
| | | }) |
| | | return Array.from(fieldSet) |
| | | } |
| | | |
| | | const getManufacturingActionCardRuntimeKey = (card = {}, index = 0) => { |
| | | const code = String(card?.code || '').trim() |
| | | const api = String(card?.targetApi || '').trim() |
| | | const name = String(card?.name || '').trim() |
| | | return `${code}::${api}::${name}::${index}` |
| | | } |
| | | |
| | | const normalizeManufacturingActionCards = (actionCards = [], previousCards = []) => { |
| | | const previousMap = new Map() |
| | | if (Array.isArray(previousCards)) { |
| | | previousCards.forEach((card, index) => { |
| | | previousMap.set(getManufacturingActionCardRuntimeKey(card, index), card) |
| | | }) |
| | | } |
| | | |
| | | return (Array.isArray(actionCards) ? actionCards : []) |
| | | .filter(card => isPlainObject(card)) |
| | | .map((card, index) => { |
| | | const runtimeKey = getManufacturingActionCardRuntimeKey(card, index) |
| | | const previousCard = previousMap.get(runtimeKey) |
| | | const fallbackPayloadText = stringifyStructuredPayload(card.examplePayload, 2) |
| | | return { |
| | | ...card, |
| | | runtimeKey, |
| | | payloadText: previousCard?.payloadText ?? fallbackPayloadText, |
| | | executing: Boolean(previousCard?.executing), |
| | | executed: Boolean(previousCard?.executed), |
| | | executeResult: previousCard?.executeResult || '', |
| | | executeError: Boolean(previousCard?.executeError) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | const buildManufacturingStructuredData = (parsedData, previousData = null) => { |
| | | const type = String(parsedData?.type || '') |
| | | if (!manufacturingStructuredTypeSet.has(type)) return null |
| | | |
| | | const rawData = isPlainObject(parsedData?.data) ? parsedData.data : {} |
| | | const items = Array.isArray(rawData.items) ? rawData.items.filter(item => isPlainObject(item)) : [] |
| | | const warningItems = type === 'manufacturing_warning' ? normalizeManufacturingWarningItems(items) : [] |
| | | const listItems = manufacturingListTypeSet.has(type) ? items : [] |
| | | const actionCards = type === 'manufacturing_action_plan' |
| | | ? normalizeManufacturingActionCards(rawData.actionCards, previousData?.actionCards) |
| | | : [] |
| | | |
| | | return { |
| | | type, |
| | | summaryEntries: normalizeManufacturingSummaryEntries(parsedData?.summary), |
| | | coreMetrics: normalizeManufacturingCoreMetrics(rawData.coreMetrics), |
| | | listItems, |
| | | columns: inferManufacturingColumns(listItems), |
| | | warningItems, |
| | | actionCards |
| | | } |
| | | } |
| | | |
| | | const getManufacturingTypeLabel = (type = '') => manufacturingTypeLabelMap[String(type || '')] || 'å¶é ç»æ' |
| | | |
| | | const getManufacturingWarningLevelType = (level = '') => { |
| | | const normalizedLevel = String(level || '').toLowerCase() |
| | | if (normalizedLevel === 'high') return 'danger' |
| | | if (normalizedLevel === 'medium') return 'warning' |
| | | return 'info' |
| | | } |
| | | |
| | | const getManufacturingWarningLevelLabel = (level = '') => { |
| | | const normalizedLevel = String(level || '').toLowerCase() |
| | | if (normalizedLevel === 'high') return 'é«' |
| | | if (normalizedLevel === 'medium') return 'ä¸' |
| | | return normalizedLevel ? normalizedLevel.toUpperCase() : 'ä¸è¬' |
| | | } |
| | | |
| | | const normalizeRequestMethod = (method = 'POST') => { |
| | | const normalized = String(method || 'POST').trim().toUpperCase() |
| | | if (['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(normalized)) return normalized |
| | | return 'POST' |
| | | } |
| | | |
| | | const getNormalizedRequestMethod = (method) => normalizeRequestMethod(method) |
| | | |
| | | const getPayloadValueByPath = (payload, fieldPath = '') => { |
| | | const normalizedPath = String(fieldPath || '').trim() |
| | | if (!normalizedPath || !isPlainObject(payload)) return undefined |
| | | |
| | | const pathSegments = normalizedPath |
| | | .replace(/\[(\d+)]/g, '.$1') |
| | | .split('.') |
| | | .filter(Boolean) |
| | | |
| | | return pathSegments.reduce((current, segment) => { |
| | | if (current === null || current === undefined) return undefined |
| | | if (!['object', 'function'].includes(typeof current)) return undefined |
| | | return current[segment] |
| | | }, payload) |
| | | } |
| | | |
| | | const getMissingRequiredFields = (requiredFields = [], payload = {}) => { |
| | | if (!Array.isArray(requiredFields) || !requiredFields.length) return [] |
| | | return requiredFields.filter((fieldPath) => { |
| | | const value = getPayloadValueByPath(payload, fieldPath) |
| | | return !hasMeaningfulPayloadValue(value) |
| | | }) |
| | | } |
| | | |
| | | const parseManufacturingActionPayload = (payloadText = '') => { |
| | | const text = String(payloadText ?? '').trim() |
| | | if (!text) return {} |
| | | return JSON.parse(text) |
| | | } |
| | | |
| | | const executeManufacturingAction = async (message, actionCard, cardIndex = 0) => { |
| | | if (!message?.manufacturingData || !actionCard || actionCard.executing) return |
| | | |
| | | const actionName = actionCard.name || `å¨ä½ ${cardIndex + 1}` |
| | | const targetApi = String(actionCard.targetApi || '').trim() |
| | | if (!targetApi) { |
| | | actionCard.executeError = true |
| | | actionCard.executeResult = 'ç¼ºå° targetApiï¼æ æ³æ§è¡å¨ä½' |
| | | return |
| | | } |
| | | |
| | | let payload = {} |
| | | try { |
| | | payload = parseManufacturingActionPayload(actionCard.payloadText) |
| | | } catch (err) { |
| | | actionCard.executeError = true |
| | | actionCard.executeResult = '请æ±åæ°ä¸æ¯åæ³ JSONï¼è¯·æ£æ¥åéè¯' |
| | | return |
| | | } |
| | | |
| | | const requiredFields = Array.isArray(actionCard.requiredFields) ? actionCard.requiredFields : [] |
| | | if (requiredFields.length && !isPlainObject(payload)) { |
| | | actionCard.executeError = true |
| | | actionCard.executeResult = 'å¿
å¡«åæ®µæ ¡éªå¤±è´¥ï¼è¯·æ±åæ°å¿
é¡»æ¯ JSON 对象' |
| | | return |
| | | } |
| | | |
| | | const missingFields = getMissingRequiredFields(requiredFields, payload) |
| | | if (missingFields.length) { |
| | | actionCard.executeError = true |
| | | const missingFieldLabels = missingFields.map(field => getStructuredPathLabel(field)) |
| | | actionCard.executeResult = `缺å°å¿
å¡«åæ®µï¼${missingFieldLabels.join('ã')}` |
| | | return |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm(`确认æ§è¡ã${actionName}ãåï¼`, 'æ§è¡ç¡®è®¤', { |
| | | confirmButtonText: '确认æ§è¡', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }) |
| | | } catch (err) { |
| | | return |
| | | } |
| | | |
| | | actionCard.executing = true |
| | | actionCard.executeError = false |
| | | actionCard.executeResult = '' |
| | | |
| | | const method = normalizeRequestMethod(actionCard.method) |
| | | const requestConfig = { |
| | | url: targetApi, |
| | | method: method.toLowerCase() |
| | | } |
| | | |
| | | if (method === 'GET') { |
| | | requestConfig.params = payload |
| | | } else { |
| | | requestConfig.data = payload |
| | | } |
| | | |
| | | try { |
| | | const res = await request(requestConfig) |
| | | const successMsg = res?.msg || `${actionName}æ§è¡æå` |
| | | actionCard.executed = true |
| | | actionCard.executeError = false |
| | | actionCard.executeResult = successMsg |
| | | ElMessage.success(successMsg) |
| | | } catch (err) { |
| | | actionCard.executed = false |
| | | actionCard.executeError = true |
| | | actionCard.executeResult = err?.message || `${actionName}æ§è¡å¤±è´¥ï¼è¯·ç¨åéè¯` |
| | | } finally { |
| | | actionCard.executing = false |
| | | } |
| | | } |
| | | |
| | | // åå²ä¼è¯ç¸å
³ |
| | |
| | | tableData: null, |
| | | payloadTreeData: null, |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null, |
| | | localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : [] |
| | | } |
| | | |
| | |
| | | const applyStructuredMessageData = (messageObj, parsedData, msgIndex, shouldRenderCharts = true) => { |
| | | if (!messageObj || !parsedData?.success) return |
| | | |
| | | const previousManufacturingData = messageObj.manufacturingData |
| | | messageObj.type = parsedData.type || '' |
| | | messageObj.tableData = null |
| | | messageObj.purchaseAnalysisData = null |
| | | messageObj.manufacturingData = null |
| | | |
| | | if (messageObj.type === 'todo_list' && parsedData.data) { |
| | | messageObj.tableData = parsedData.data |
| | | } |
| | | |
| | | const manufacturingData = buildManufacturingStructuredData(parsedData, previousManufacturingData) |
| | | if (manufacturingData) { |
| | | messageObj.manufacturingData = manufacturingData |
| | | } |
| | | |
| | | if (parsedData.action === 'confirm_required' && parsedData.businessType) { |
| | | messageObj.type = 'purchase_analysis_confirm' |
| | | messageObj.purchaseAnalysisData = parsedData |
| | | messageObj.manufacturingData = null |
| | | if (!Array.isArray(messageObj.payloadTreeData) || !messageObj.payloadTreeData.length) { |
| | | initializePurchasePayloadTree(messageObj, parsedData.payload || {}) |
| | | } |
| | |
| | | } |
| | | |
| | | return null |
| | | } |
| | | |
| | | const getStructuredFallbackText = (parsedData) => { |
| | | if (!parsedData) return 'æ£å¨ä¸ºæ¨å±ç¤ºåæç»æ...' |
| | | if (parsedData.type === 'todo_list') return 'å·²ä¸ºæ¨æ´ç好ç¸å
³æ°æ®ã' |
| | | if (manufacturingStructuredTypeSet.has(parsedData.type)) { |
| | | if (parsedData.type === 'manufacturing_action_plan') return '已为æ¨çæåç建议ï¼è¯·ç¡®è®¤å¨ä½åæ§è¡ã' |
| | | if (parsedData.type === 'manufacturing_warning') return '已为æ¨çæå¶é é¢è¦çæ¿ã' |
| | | if (parsedData.type === 'manufacturing_analysis') return '已为æ¨çæå¶é åæç»æã' |
| | | return 'å·²è¿åå¶é æ¥è¯¢ç»æã' |
| | | } |
| | | if (parsedData.charts && Object.keys(parsedData.charts).length > 0) return '已为æ¨çæåæå¾è¡¨ã' |
| | | return 'æ£å¨ä¸ºæ¨å±ç¤ºåæç»æ...' |
| | | } |
| | | |
| | | const buildPurchaseMaterialRankCharts = (parsedData) => { |
| | |
| | | type: '', |
| | | tableData: null, |
| | | payloadTreeData: null, |
| | | payloadHiddenData: null |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null |
| | | }) |
| | | |
| | | outputState.value[botMsgIndex] = { |
| | |
| | | type: '', |
| | | tableData: null, |
| | | payloadTreeData: null, |
| | | payloadHiddenData: null |
| | | payloadHiddenData: null, |
| | | purchaseAnalysisData: null, |
| | | manufacturingData: null |
| | | } |
| | | messages.value.push(botMsg) |
| | | |
| | |
| | | } |
| | | |
| | | if (!display) { |
| | | if (parsed.type === 'todo_list') { |
| | | display = 'å·²ä¸ºæ¨æ´ç好ç¸å
³æ°æ®ã' |
| | | } else if (parsed.charts && Object.keys(parsed.charts).length > 0) { |
| | | display = '已为æ¨çæåæå¾è¡¨ã' |
| | | } else { |
| | | display = 'æ£å¨ä¸ºæ¨å±ç¤ºåæç»æ...' |
| | | } |
| | | display = getStructuredFallbackText(parsed) |
| | | } |
| | | } else if (startIdx !== -1) { |
| | | const lastBraceIdx = output.lastIndexOf('}') |
| | |
| | | } |
| | | |
| | | if (!display) { |
| | | if (parsed.type === 'todo_list') { |
| | | display = 'å·²ä¸ºæ¨æ´ç好ç¸å
³æ°æ®ï¼' |
| | | } else if (parsed.charts && Object.keys(parsed.charts).length > 0) { |
| | | display = '已为æ¨çæåæå¾è¡¨ï¼' |
| | | } else { |
| | | display = 'æ£å¨ä¸ºæ¨å±ç¤ºåæç»æ...' |
| | | } |
| | | display = getStructuredFallbackText(parsed) |
| | | } |
| | | } catch (e) { |
| | | // è§£æå¤±è´¥ï¼è¯´æ JSON è¿å¨ä¼ è¾ä¸ææ ¼å¼ä¸æ£ç¡® |
| | |
| | | } |
| | | } |
| | | |
| | | .manufacturing-card { |
| | | margin-top: 12px; |
| | | width: 100%; |
| | | background: #fff; |
| | | border: 1px solid rgba(0, 85, 212, 0.12); |
| | | border-radius: 12px; |
| | | box-shadow: $shadow-card; |
| | | padding: 14px; |
| | | } |
| | | |
| | | .manufacturing-card__title { |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | color: $deep-blue; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .manufacturing-summary-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); |
| | | gap: 8px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .manufacturing-summary-item { |
| | | border-radius: 10px; |
| | | padding: 10px 12px; |
| | | border: 1px solid rgba(0, 85, 212, 0.08); |
| | | background: linear-gradient(180deg, #f8fbff, #f1f7ff); |
| | | min-height: 66px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | gap: 6px; |
| | | } |
| | | |
| | | .manufacturing-summary-item--core { |
| | | border-color: rgba(30, 91, 255, 0.24); |
| | | } |
| | | |
| | | .manufacturing-summary-label { |
| | | font-size: 12px; |
| | | color: #4b5563; |
| | | } |
| | | |
| | | .manufacturing-summary-value { |
| | | font-size: 15px; |
| | | color: #1f2937; |
| | | line-height: 1.4; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .manufacturing-warning-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .manufacturing-warning-item { |
| | | border-radius: 10px; |
| | | border: 1px solid rgba(245, 158, 11, 0.22); |
| | | background: linear-gradient(135deg, rgba(255, 247, 237, 0.9), rgba(255, 255, 255, 0.98)); |
| | | padding: 10px 12px; |
| | | } |
| | | |
| | | .manufacturing-warning-item__head { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | color: #92400e; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .manufacturing-warning-count { |
| | | margin-left: auto; |
| | | font-weight: 700; |
| | | color: #c2410c; |
| | | } |
| | | |
| | | .manufacturing-warning-detail { |
| | | margin: 8px 0 0; |
| | | font-size: 12px; |
| | | line-height: 1.6; |
| | | color: #7c2d12; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .manufacturing-table-wrapper { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .manufacturing-action-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .manufacturing-action-card { |
| | | border: 1px solid rgba(0, 85, 212, 0.1); |
| | | border-radius: 10px; |
| | | padding: 10px 12px; |
| | | background: #f8fbff; |
| | | } |
| | | |
| | | .manufacturing-action-card__head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 12px; |
| | | font-size: 13px; |
| | | color: #1f2937; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .manufacturing-action-card__meta { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 4px; |
| | | margin-bottom: 8px; |
| | | font-size: 12px; |
| | | color: #64748b; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .manufacturing-action-card__desc { |
| | | margin: 0 0 8px; |
| | | font-size: 12px; |
| | | line-height: 1.6; |
| | | color: #475467; |
| | | } |
| | | |
| | | .manufacturing-required-fields { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | align-items: center; |
| | | gap: 6px; |
| | | margin-bottom: 8px; |
| | | font-size: 12px; |
| | | color: #7c2d12; |
| | | } |
| | | |
| | | .manufacturing-action-footer { |
| | | margin-top: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-end; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .manufacturing-action-result { |
| | | flex: 1; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | |
| | | &.success { |
| | | color: #1f9d55; |
| | | } |
| | | |
| | | &.error { |
| | | color: #d93025; |
| | | } |
| | | } |
| | | |
| | | .purchase-confirm-card { |
| | | margin-top: 12px; |
| | | width: 100%; |