feat(core): 添加AI功能开关控制
- 在环境配置中新增VITE_APP_AI_ENABLED开关,默认设置为false
- 实现isAiFeatureEnabled工具函数用于判断AI功能是否启用
- 添加isAiRoute函数识别AI相关路由路径
- 实现filterDisabledAiRoutes函数过滤禁用的AI路由
- 在路由访问控制中集成AI功能开关过滤逻辑
- 为AI模块路由添加条件渲染支持
- 更新部署脚本中的服务器配置信息
- 在MES生产管理表单中集成AI功能启用状态检查
- 在质量控制表单中添加AI助手和建议组件的条件显示
- 为CRM销售报价单添加AI识别录入功能开关控制
- 移除百度地图密钥的重复定义
| | |
| | | # ææ¡£å°åçå¼å
³ |
| | | VITE_APP_DOCALERT_ENABLE=true |
| | | |
| | | # AI åè½æ»å¼å
³ï¼è½»éçé»è®¤å
³éï¼ |
| | | VITE_APP_AI_ENABLED=false |
| | | |
| | | # ç¾åº¦ç»è®¡ |
| | | VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093 |
| | | |
| | |
| | | # VITE_APP_API_ENCRYPT_RESPONSE_KEY = MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOH8IfIFxL/MR9XIg1UDv5z1fGXQI93E8wrU4iPFovL/sEt9uSgSkjyidC2O7N+m7EKtoN6b1u7cEwXSkwf3kfK0jdWLSQaNpX5YshqXCBzbDfugDaxuyYrNA4/tIMs7mzZAk0APuRXB35Dmupou7Yw7TFW/7QhQmGfzeEKULQvnAgMBAAECgYAw8LqlQGyQoPv5p3gRxEMOCfgL0JzD3XBJKztiRd35RDh40Nx1ejgjW4dPioFwGiVWd2W8cAGHLzALdcQT2KDJh+T/tsd4SPmI6uSBBK6Ff2DkO+kFFcuYvfclQQKqxma5CaZOSqhgenacmgTMFeg2eKlY3symV6JlFNu/IKU42QJBAOhxAK/Eq3e61aYQV2JSguhMR3b8NXJJRroRs/QHEanksJtl+M+2qhkC9nQVXBmBkndnkU/l2tYcHfSBlAyFySMCQQD445tgm/J2b6qMQmuUGQAYDN8FIkHjeKmha+l/fv0igWm8NDlBAem91lNDIPBUzHL1X1+pcts5bjmq99YdOnhtAkAg2J8dN3B3idpZDiQbC8fd5bGPmdI/pSUudAP27uzLEjr2qrE/QPPGdwm2m7IZFJtK7kK1hKio6u48t/bg0iL7AkEAuUUs94h+v702Fnym+jJ2CHEkXvz2US8UDs52nWrZYiM1o1y4tfSHm8H8bv8JCAa9GHyriEawfBraILOmllFdLQJAQSRZy4wmlaG48MhVXodB85X+VZ9krGXZ2TLhz7kz9iuToy53l9jTkESt6L5BfBDCVdIwcXLYgK+8KFdHN5W7HQ== |
| | | |
| | | # ç¾åº¦å°å¾ |
| | | VITE_BAIDU_MAP_KEY=Y2aJXiswwPxy6mwFs1z9c7U5gwX9WfUN |
| | | VITE_BAIDU_MAP_KEY=Y2aJXiswwPxy6mwFs1z9c7U5gwX9WfUN |
| | |
| | | :: ================= Config ================= |
| | | set "COMPANY=TEST" |
| | | set "USER=root" |
| | | set "PASS=Xdrjkj@10086#" |
| | | set "IP=36.213.90.123" |
| | | set "PORT=65022" |
| | | set "PASS=Wqsdp@123" |
| | | set "IP=1.95.78.124" |
| | | set "PORT=22" |
| | | set "ZIP_FILE=dist.zip" |
| | | set "REMOTE_DIR=/data/project/mom/vue/test/dist" |
| | | set "REMOTE_DIR=/data/project/xgdl/vue/dist" |
| | | set "SSH_OPTS=-o StrictHostKeyChecking=no -o ConnectTimeout=10" |
| | | :: =========================================== |
| | | |
| | |
| | | import { convertServerMenuToRouteRecordStringComponent } from '@vben/utils'; |
| | | |
| | | import { BasicLayout, IFrameView } from '#/layouts'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue'); |
| | | |
| | | function isAiRoute(route: AppRouteRecordRaw) { |
| | | const path = String(route.path || '').toLowerCase(); |
| | | const component = String(route.component || '').toLowerCase(); |
| | | return ( |
| | | path === '/ai' || |
| | | path.startsWith('/ai/') || |
| | | component.startsWith('ai/') || |
| | | component.includes('/views/ai/') |
| | | ); |
| | | } |
| | | |
| | | function filterDisabledAiRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] { |
| | | if (isAiFeatureEnabled) { |
| | | return routes; |
| | | } |
| | | return routes |
| | | .filter((route) => !isAiRoute(route)) |
| | | .map((route) => ({ |
| | | ...route, |
| | | children: route.children |
| | | ? filterDisabledAiRoutes(route.children as AppRouteRecordRaw[]) |
| | | : route.children, |
| | | })); |
| | | } |
| | | |
| | | async function generateAccess(options: GenerateMenuAndRoutesOptions) { |
| | | const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue'); |
| | |
| | | fetchMenuListAsync: async () => { |
| | | // ç±äº yudao éè¿ accessStore 读åï¼æä»¥ä¸å¨è¿è¡ message.loading æç¤º |
| | | // è¡¥å
说æï¼accessStore.accessMenus ä¸å¼å§æ¯ AppRouteRecordRaw ç±»åï¼å端å è½½ï¼ï¼åé¢è¢«èµå¼æ MenuRecordRaw ç±»åï¼å端转æ¢ï¼ |
| | | const accessMenus = accessStore.accessMenus as AppRouteRecordRaw[]; |
| | | const accessMenus = filterDisabledAiRoutes( |
| | | accessStore.accessMenus as AppRouteRecordRaw[], |
| | | ); |
| | | return convertServerMenuToRouteRecordStringComponent(accessMenus); |
| | | }, |
| | | // å¯ä»¥æå®æ²¡ææé跳转403é¡µé¢ |
| | |
| | | |
| | | import { mergeRouteModules, traverseTreeValues } from '@vben/utils'; |
| | | |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import { coreRoutes, fallbackNotFoundRoute } from './core'; |
| | | |
| | | const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', { |
| | |
| | | import.meta.glob('../../views/**/*.vue'), |
| | | ) |
| | | .filter((item) => !item.includes('/modules/')) |
| | | .filter((item) => isAiFeatureEnabled || !item.includes('/views/ai/')) |
| | | .map((v) => { |
| | | const path = v.replace('../../views/', '/'); |
| | | return path.endsWith('.vue') ? path.slice(0, -4) : path; |
| | |
| | | import type { RouteRecordRaw } from 'vue-router'; |
| | | |
| | | const routes: RouteRecordRaw[] = [ |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | const routes: RouteRecordRaw[] = isAiFeatureEnabled ? [ |
| | | { |
| | | path: '/ai', |
| | | name: 'Ai', |
| | |
| | | }, |
| | | ], |
| | | }, |
| | | ]; |
| | | ] : []; |
| | | |
| | | export default routes; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * æ°é«çµååè½å¼å
³ã |
| | | * é»è®¤å
³é AIï¼é¿å
è½»éåç«¯æªæºå¸¦ AI æ¨¡åæ¶ä»å±ç¤ºä¸å¯ç¨å
¥å£ã |
| | | */ |
| | | export const isAiFeatureEnabled = |
| | | import.meta.env.VITE_APP_AI_ENABLED === 'true'; |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ActionItem } from '#/adapter/vxe-table'; |
| | | import type { CrmSaleQuotationApi } from '#/api/crm/saleQuotation'; |
| | | |
| | | import { useRouter } from 'vue-router'; |
| | |
| | | getSaleQuotationPage, |
| | | } from '#/api/crm/saleQuotation'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import type { CrmSaleQuotationAiApi } from '#/api/crm/saleQuotation/ai'; |
| | | |
| | |
| | | function handleOcrSuccess(data: CrmSaleQuotationAiApi.OcrResultVO) { |
| | | nextTick(() => { |
| | | formModalApi.setData({ ocrData: data } as any).open(); |
| | | }); |
| | | } |
| | | |
| | | const toolbarActions: ActionItem[] = [ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['æ¥ä»·å']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['crm:sale-quotation:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]; |
| | | if (isAiFeatureEnabled) { |
| | | toolbarActions.push({ |
| | | label: 'AI è¯å«å½å
¥', |
| | | type: 'default', |
| | | auth: ['crm:sale-quotation:create'], |
| | | onClick: handleOcrCreate, |
| | | }); |
| | | } |
| | | |
| | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <OcrUpload ref="ocrUploadRef" @success="handleOcrSuccess" /> |
| | | <OcrUpload |
| | | v-if="isAiFeatureEnabled" |
| | | ref="ocrUploadRef" |
| | | @success="handleOcrSuccess" |
| | | /> |
| | | <Grid> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['æ¥ä»·å']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['crm:sale-quotation:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'AI è¯å«å½å
¥', |
| | | type: 'default', |
| | | auth: ['crm:sale-quotation:create'], |
| | | onClick: handleOcrCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | <TableAction :actions="toolbarActions" /> |
| | | </template> |
| | | <template #name="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| | | </template> |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createMps, getMps, updateMps } from '#/api/mes/pro/mps'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import PredictDelivery from '../../workorder/modules/ai/predict-delivery.vue'; |
| | | import PredictDuration from '../../workorder/modules/ai/predict-duration.vue'; |
| | |
| | | |
| | | const isEditable = computed(() => ['create', 'edit'].includes(formType.value)); |
| | | |
| | | const canUseAi = computed(() => !!formData.value?.id); |
| | | const canUseAi = computed(() => isAiFeatureEnabled && !!formData.value?.id); |
| | | |
| | | const getTitle = computed(() => { |
| | | switch (formType.value) { |
| | |
| | | } from "#/api/mes/pro/workorder"; |
| | | import { batchSaveWorkOrderProcess, getWorkOrderProcessListByWorkOrderId } from "#/api/mes/pro/workorder/process"; |
| | | import { $t } from "#/locales"; |
| | | import { isAiFeatureEnabled } from "#/utils/features"; |
| | | // import { BarcodeDetail } from "#/views/wls/barcode/components"; |
| | | |
| | | import { useFormSchema } from "../data"; |
| | |
| | | ); |
| | | const canUseAi = computed( |
| | | () => |
| | | isAiFeatureEnabled && |
| | | formData.value?.id && |
| | | (formData.value?.status === MesProWorkOrderStatusEnum.PREPARE || |
| | | formData.value?.status === MesProWorkOrderStatusEnum.CONFIRMED) |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createIpqc, finishIpqc, getIpqc, updateIpqc } from '#/api/mes/qc/ipqc'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import { QcIndicatorResultList } from '../../indicatorresult/components'; |
| | | import MesQcAiSuggest from '../../components/ai-suggest.vue'; |
| | |
| | | 宿 |
| | | </Button> |
| | | <MesQcAiSuggest |
| | | v-if="canFinish && formData?.id" |
| | | v-if="isAiFeatureEnabled && canFinish && formData?.id" |
| | | :qc-id="formData.id" |
| | | :qc-type="MesQcTypeEnum.IPQC" |
| | | /> |
| | | <MesQcAiAssistant /> |
| | | <MesQcAiAssistant v-if="isAiFeatureEnabled" /> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createIqc, finishIqc, getIqc, updateIqc } from '#/api/mes/qc/iqc'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import { QcIndicatorResultList } from '../../indicatorresult/components'; |
| | | import MesQcAiSuggest from '../../components/ai-suggest.vue'; |
| | |
| | | 宿 |
| | | </Button> |
| | | <MesQcAiSuggest |
| | | v-if="canFinish && formData?.id" |
| | | v-if="isAiFeatureEnabled && canFinish && formData?.id" |
| | | :qc-id="formData.id" |
| | | :qc-type="MesQcTypeEnum.IQC" |
| | | /> |
| | | <MesQcAiAssistant /> |
| | | <MesQcAiAssistant v-if="isAiFeatureEnabled" /> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createOqc, finishOqc, getOqc, updateOqc } from '#/api/mes/qc/oqc'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import { QcIndicatorResultList } from '../../indicatorresult/components'; |
| | | import MesQcAiSuggest from '../../components/ai-suggest.vue'; |
| | |
| | | 宿 |
| | | </Button> |
| | | <MesQcAiSuggest |
| | | v-if="canFinish && formData?.id" |
| | | v-if="isAiFeatureEnabled && canFinish && formData?.id" |
| | | :qc-id="formData.id" |
| | | :qc-type="MesQcTypeEnum.OQC" |
| | | /> |
| | | <MesQcAiAssistant /> |
| | | <MesQcAiAssistant v-if="isAiFeatureEnabled" /> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createRqc, finishRqc, getRqc, updateRqc } from '#/api/mes/qc/rqc'; |
| | | import { $t } from '#/locales'; |
| | | import { isAiFeatureEnabled } from '#/utils/features'; |
| | | |
| | | import { QcIndicatorResultList } from '../../indicatorresult/components'; |
| | | import MesQcAiSuggest from '../../components/ai-suggest.vue'; |
| | |
| | | 宿 |
| | | </Button> |
| | | <MesQcAiSuggest |
| | | v-if="canFinish && formData?.id" |
| | | v-if="isAiFeatureEnabled && canFinish && formData?.id" |
| | | :qc-id="formData.id" |
| | | :qc-type="MesQcTypeEnum.RQC" |
| | | /> |
| | | <MesQcAiAssistant /> |
| | | <MesQcAiAssistant v-if="isAiFeatureEnabled" /> |
| | | </div> |
| | | </template> |
| | | </Modal> |