@chenchaolong/plugin-trade-compliance-workbench 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -1
- package/dist/assets/branding/yuneec-logo.png +0 -0
- package/dist/assets/templates/CI.xlsx +0 -0
- package/dist/assets/templates/CUSTOMS_DECLARATION.xlsx +0 -0
- package/dist/assets/templates/PI.xlsx +0 -0
- package/dist/assets/templates/PL.xlsx +0 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/adapters/file-storage.d.ts +4 -2
- package/dist/lib/adapters/file-storage.d.ts.map +1 -1
- package/dist/lib/adapters/file-storage.js +25 -10
- package/dist/lib/adapters/file-storage.js.map +1 -1
- package/dist/lib/adapters/hsbianma.provider.d.ts +1 -0
- package/dist/lib/adapters/hsbianma.provider.d.ts.map +1 -1
- package/dist/lib/adapters/hsbianma.provider.js +32 -3
- package/dist/lib/adapters/hsbianma.provider.js.map +1 -1
- package/dist/lib/adapters/translation.provider.d.ts +3 -0
- package/dist/lib/adapters/translation.provider.d.ts.map +1 -1
- package/dist/lib/adapters/translation.provider.js +15 -6
- package/dist/lib/adapters/translation.provider.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +1 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/demo-data.definitions.d.ts +28 -0
- package/dist/lib/demo-data.definitions.d.ts.map +1 -0
- package/dist/lib/demo-data.definitions.js +236 -0
- package/dist/lib/demo-data.definitions.js.map +1 -0
- package/dist/lib/demo-data.entity.d.ts +9 -0
- package/dist/lib/demo-data.entity.d.ts.map +1 -0
- package/dist/lib/demo-data.entity.js +24 -0
- package/dist/lib/demo-data.entity.js.map +1 -0
- package/dist/lib/demo-data.ids.d.ts +4 -0
- package/dist/lib/demo-data.ids.d.ts.map +1 -0
- package/dist/lib/demo-data.ids.js +19 -0
- package/dist/lib/demo-data.ids.js.map +1 -0
- package/dist/lib/demo-data.service.d.ts +26 -0
- package/dist/lib/demo-data.service.d.ts.map +1 -0
- package/dist/lib/demo-data.service.js +124 -0
- package/dist/lib/demo-data.service.js.map +1 -0
- package/dist/lib/domain/default-settings.d.ts +1 -0
- package/dist/lib/domain/default-settings.d.ts.map +1 -1
- package/dist/lib/domain/default-settings.js +1 -0
- package/dist/lib/domain/default-settings.js.map +1 -1
- package/dist/lib/domain/risk-notice.d.ts +59 -0
- package/dist/lib/domain/risk-notice.d.ts.map +1 -0
- package/dist/lib/domain/risk-notice.js +30 -0
- package/dist/lib/domain/risk-notice.js.map +1 -0
- package/dist/lib/entities/catalog.entity.d.ts.map +1 -1
- package/dist/lib/entities/catalog.entity.js +4 -2
- package/dist/lib/entities/catalog.entity.js.map +1 -1
- package/dist/lib/entities/customer.entity.d.ts +9 -2
- package/dist/lib/entities/customer.entity.d.ts.map +1 -1
- package/dist/lib/entities/customer.entity.js +29 -1
- package/dist/lib/entities/customer.entity.js.map +1 -1
- package/dist/lib/entities/index.d.ts +2 -2
- package/dist/lib/entities/index.d.ts.map +1 -1
- package/dist/lib/entities/index.js +2 -2
- package/dist/lib/entities/index.js.map +1 -1
- package/dist/lib/entities/procurement.entity.d.ts +6 -4
- package/dist/lib/entities/procurement.entity.d.ts.map +1 -1
- package/dist/lib/entities/procurement.entity.js +9 -1
- package/dist/lib/entities/procurement.entity.js.map +1 -1
- package/dist/lib/entities/settings.entity.d.ts +12 -2
- package/dist/lib/entities/settings.entity.d.ts.map +1 -1
- package/dist/lib/entities/settings.entity.js +40 -0
- package/dist/lib/entities/settings.entity.js.map +1 -1
- package/dist/lib/entities/shared.entity.d.ts +10 -5
- package/dist/lib/entities/shared.entity.d.ts.map +1 -1
- package/dist/lib/entities/shared.entity.js +22 -1
- package/dist/lib/entities/shared.entity.js.map +1 -1
- package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -0
- package/dist/lib/remote-components/trade-compliance-workbench/app.js +63 -277
- package/dist/lib/remote-components/trade-compliance-workbench/editor-source.mjs +2 -1
- package/dist/lib/remote-components/trade-compliance-workbench/editor.js +171 -128
- package/dist/lib/remote-ui/analytics-contracts.d.ts +106 -0
- package/dist/lib/remote-ui/analytics-contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/analytics-contracts.js +4 -0
- package/dist/lib/remote-ui/analytics-contracts.js.map +1 -0
- package/dist/lib/remote-ui/app-source.d.ts +2 -0
- package/dist/lib/remote-ui/app-source.d.ts.map +1 -0
- package/dist/lib/remote-ui/app-source.js +796 -0
- package/dist/lib/remote-ui/app-source.js.map +1 -0
- package/dist/lib/remote-ui/bridge.d.ts +36 -0
- package/dist/lib/remote-ui/bridge.d.ts.map +1 -0
- package/dist/lib/remote-ui/bridge.js +159 -0
- package/dist/lib/remote-ui/bridge.js.map +1 -0
- package/dist/lib/remote-ui/components/data-grid.d.ts +34 -0
- package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -0
- package/dist/lib/remote-ui/components/data-grid.js +59 -0
- package/dist/lib/remote-ui/components/data-grid.js.map +1 -0
- package/dist/lib/remote-ui/components/forms.d.ts +63 -0
- package/dist/lib/remote-ui/components/forms.d.ts.map +1 -0
- package/dist/lib/remote-ui/components/forms.js +117 -0
- package/dist/lib/remote-ui/components/forms.js.map +1 -0
- package/dist/lib/remote-ui/components/layout.d.ts +25 -0
- package/dist/lib/remote-ui/components/layout.d.ts.map +1 -0
- package/dist/lib/remote-ui/components/layout.js +66 -0
- package/dist/lib/remote-ui/components/layout.js.map +1 -0
- package/dist/lib/remote-ui/contract-import.d.ts +82 -0
- package/dist/lib/remote-ui/contract-import.d.ts.map +1 -0
- package/dist/lib/remote-ui/contract-import.js +102 -0
- package/dist/lib/remote-ui/contract-import.js.map +1 -0
- package/dist/lib/remote-ui/contracts.d.ts +257 -0
- package/dist/lib/remote-ui/contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/contracts.js +12 -0
- package/dist/lib/remote-ui/contracts.js.map +1 -0
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts +227 -0
- package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/customer-contract-contracts.js +10 -0
- package/dist/lib/remote-ui/customer-contract-contracts.js.map +1 -0
- package/dist/lib/remote-ui/model.d.ts +31 -0
- package/dist/lib/remote-ui/model.d.ts.map +1 -0
- package/dist/lib/remote-ui/model.js +248 -0
- package/dist/lib/remote-ui/model.js.map +1 -0
- package/dist/lib/remote-ui/pages/analytics.d.ts +12 -0
- package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/analytics.js +123 -0
- package/dist/lib/remote-ui/pages/analytics.js.map +1 -0
- package/dist/lib/remote-ui/pages/catalogs.d.ts +225 -0
- package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/catalogs.js +572 -0
- package/dist/lib/remote-ui/pages/catalogs.js.map +1 -0
- package/dist/lib/remote-ui/pages/contracts.d.ts +80 -0
- package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/contracts.js +396 -0
- package/dist/lib/remote-ui/pages/contracts.js.map +1 -0
- package/dist/lib/remote-ui/pages/files.d.ts +31 -0
- package/dist/lib/remote-ui/pages/files.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/files.js +64 -0
- package/dist/lib/remote-ui/pages/files.js.map +1 -0
- package/dist/lib/remote-ui/pages/home.d.ts +71 -0
- package/dist/lib/remote-ui/pages/home.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/home.js +136 -0
- package/dist/lib/remote-ui/pages/home.js.map +1 -0
- package/dist/lib/remote-ui/pages/procurement.d.ts +36 -0
- package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/procurement.js +370 -0
- package/dist/lib/remote-ui/pages/procurement.js.map +1 -0
- package/dist/lib/remote-ui/pages/sales.d.ts +12 -0
- package/dist/lib/remote-ui/pages/sales.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/sales.js +106 -0
- package/dist/lib/remote-ui/pages/sales.js.map +1 -0
- package/dist/lib/remote-ui/pages/settings.d.ts +34 -0
- package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -0
- package/dist/lib/remote-ui/pages/settings.js +259 -0
- package/dist/lib/remote-ui/pages/settings.js.map +1 -0
- package/dist/lib/remote-ui/procurement-contracts.d.ts +177 -0
- package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/procurement-contracts.js +2 -0
- package/dist/lib/remote-ui/procurement-contracts.js.map +1 -0
- package/dist/lib/remote-ui/sales-contracts.d.ts +133 -0
- package/dist/lib/remote-ui/sales-contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/sales-contracts.js +2 -0
- package/dist/lib/remote-ui/sales-contracts.js.map +1 -0
- package/dist/lib/remote-ui/settings-contracts.d.ts +114 -0
- package/dist/lib/remote-ui/settings-contracts.d.ts.map +1 -0
- package/dist/lib/remote-ui/settings-contracts.js +6 -0
- package/dist/lib/remote-ui/settings-contracts.js.map +1 -0
- package/dist/lib/remote-ui/upload-policy.d.ts +18 -0
- package/dist/lib/remote-ui/upload-policy.d.ts.map +1 -0
- package/dist/lib/remote-ui/upload-policy.js +56 -0
- package/dist/lib/remote-ui/upload-policy.js.map +1 -0
- package/dist/lib/runtime-validation.d.ts +61 -0
- package/dist/lib/runtime-validation.d.ts.map +1 -0
- package/dist/lib/runtime-validation.js +243 -0
- package/dist/lib/runtime-validation.js.map +1 -0
- package/dist/lib/sales-file.service.d.ts +54 -6
- package/dist/lib/sales-file.service.d.ts.map +1 -1
- package/dist/lib/sales-file.service.js +278 -33
- package/dist/lib/sales-file.service.js.map +1 -1
- package/dist/lib/trade-compliance.config.d.ts +32 -0
- package/dist/lib/trade-compliance.config.d.ts.map +1 -1
- package/dist/lib/trade-compliance.config.js +16 -4
- package/dist/lib/trade-compliance.config.js.map +1 -1
- package/dist/lib/types.d.ts +253 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +24 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/workbench-view.provider.d.ts +675 -38
- package/dist/lib/workbench-view.provider.d.ts.map +1 -1
- package/dist/lib/workbench-view.provider.js +418 -42
- package/dist/lib/workbench-view.provider.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts +70 -19
- package/dist/lib/workbench.middleware.d.ts.map +1 -1
- package/dist/lib/workbench.middleware.js +5 -1
- package/dist/lib/workbench.middleware.js.map +1 -1
- package/dist/lib/workbench.plugin.d.ts.map +1 -1
- package/dist/lib/workbench.plugin.js +6 -4
- package/dist/lib/workbench.plugin.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +629 -48
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +1818 -300
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +15 -4
- package/scripts/seed-demo-data.mjs +138 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workbench-view.provider.d.ts","sourceRoot":"","sources":["../../src/lib/workbench-view.provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workbench-view.provider.d.ts","sourceRoot":"","sources":["../../src/lib/workbench-view.provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoG,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACnK,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGrD,OAAO,EAAE,+BAA+B,EAAqC,MAAM,wBAAwB,CAAA;AAC3G,OAAO,EAA6B,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAwO,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAExR,OAAO,EAAE,8BAA8B,EAA0B,MAAM,wBAAwB,CAAA;AAO/F,qBAEa,oCAAoC;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACY,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;gBAJjE,OAAO,EAAE,+BAA+B,EACxC,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,0BAA0B,EACvB,QAAQ,CAAC,EAAE,8BAA8B,YAAA,EACH,cAAc,CAAC,EAAE,qBAAqB,YAAA;IAG3G,QAAQ,CAAC,OAAO,EAAE,GAAG;IAErB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAatC,uBAAuB,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;;;;IAUtE,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsDtD,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAqGjF,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAoCvF,iBAAiB;IAsB/B,OAAO,CAAC,QAAQ;CAGjB;AAoDD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CASvG;AAsHD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,UAA0E"}
|
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { Injectable } from '@nestjs/common';
|
|
1
|
+
import { __decorate, __metadata, __param } from "tslib";
|
|
2
|
+
import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { readFile } from 'node:fs/promises';
|
|
7
|
-
import { renderRemoteReactIframeHtml, ViewExtensionProvider } from '@xpert-ai/plugin-sdk';
|
|
7
|
+
import { PLUGIN_CONFIG_RESOLVER_TOKEN, renderRemoteReactIframeHtml, RequestContext, ViewExtensionProvider } from '@xpert-ai/plugin-sdk';
|
|
8
|
+
import { PermissionsEnum } from '@xpert-ai/contracts';
|
|
9
|
+
import XLSX from 'xlsx';
|
|
8
10
|
import { FIXED_SLOT, FEATURE, ICON, MAIN_SLOT, PLUGIN_NAME, REMOTE_ENTRY_KEY, TOOL_NAMES, VIEW_KEY, VIEW_PROVIDER_KEY } from './constants.js';
|
|
9
11
|
import { TradeComplianceWorkbenchService } from './workbench.service.js';
|
|
10
|
-
import { SalesFileService } from './sales-file.service.js';
|
|
12
|
+
import { MAX_EDITED_WORKBOOK_BYTES, SalesFileService } from './sales-file.service.js';
|
|
11
13
|
import { TradeComplianceFileStorage } from './adapters/file-storage.js';
|
|
12
14
|
import { extractDocumentText } from './adapters/document-text-extractor.js';
|
|
13
15
|
import { getHsBianmaCodeDetail, searchHsBianmaCodes } from './adapters/hsbianma.provider.js';
|
|
14
|
-
import { DomainError, DomainErrorCodes, SourceFileFormats } from './types.js';
|
|
16
|
+
import { DomainError, DomainErrorCodes, SalesFileTypes, SourceFileFormats } from './types.js';
|
|
17
|
+
import { parseAnalyticsQuery, parseBankAccountType, parseExpectedRevision, parseRestoreSettingsInput, parseSettingsType } from './runtime-validation.js';
|
|
18
|
+
import { TradeComplianceDemoDataService } from './demo-data.service.js';
|
|
19
|
+
import { resolveTradeComplianceConfig } from './trade-compliance.config.js';
|
|
15
20
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
16
21
|
const requireFromHere = createRequire(import.meta.url);
|
|
17
22
|
const text = (en_US, zh_Hans) => ({ en_US, zh_Hans });
|
|
18
23
|
let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewProvider {
|
|
19
|
-
constructor(service, salesFiles, storage) {
|
|
24
|
+
constructor(service, salesFiles, storage, demoData, configResolver) {
|
|
20
25
|
this.service = service;
|
|
21
26
|
this.salesFiles = salesFiles;
|
|
22
27
|
this.storage = storage;
|
|
28
|
+
this.demoData = demoData;
|
|
29
|
+
this.configResolver = configResolver;
|
|
23
30
|
}
|
|
24
31
|
supports(context) { return context.hostType === 'agent'; }
|
|
25
32
|
getViewManifests(_context, slot) {
|
|
@@ -38,22 +45,78 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
|
|
|
38
45
|
async getRemoteComponentEntry(_context, viewKey, component) {
|
|
39
46
|
if (viewKey !== VIEW_KEY || component.entry !== REMOTE_ENTRY_KEY)
|
|
40
47
|
return { html: '<!doctype html><html><body>Unsupported component.</body></html>', contentType: 'text/html; charset=utf-8' };
|
|
41
|
-
const [appScript,
|
|
48
|
+
const [appScript, appCss, reactUmd, reactDomUmd] = await Promise.all([
|
|
42
49
|
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'app.js'), 'utf8'),
|
|
43
|
-
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, '
|
|
44
|
-
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.css'), 'utf8'),
|
|
50
|
+
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'app.css'), 'utf8'),
|
|
45
51
|
readPackageFile('react', 'umd/react.production.min.js'), readPackageFile('react-dom', 'umd/react-dom.production.min.js')
|
|
46
52
|
]);
|
|
47
|
-
|
|
48
|
-
return { html: renderRemoteReactIframeHtml({ title: '外贸合规工作台', lang: 'zh-Hans', reactUmd, reactDomUmd, appScript: spreadsheetRuntime }), contentType: 'text/html; charset=utf-8' };
|
|
53
|
+
return { html: renderRemoteReactIframeHtml({ title: '外贸合规工作台', lang: 'zh-Hans', reactUmd, reactDomUmd, appScript, appCss: sanitizeIframeCss(appCss) }), contentType: 'text/html; charset=utf-8' };
|
|
49
54
|
}
|
|
50
55
|
async getViewData(context, _viewKey, query) {
|
|
51
56
|
const scope = scopeFromContext(context);
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
57
|
+
const parameters = record(query?.parameters);
|
|
58
|
+
const collection = stringValue(parameters.collection);
|
|
59
|
+
if (collection) {
|
|
60
|
+
const pageQuery = { page: numberValue(query?.page, 1), pageSize: numberValue(query?.pageSize, 20), parameters: record(parameters.filters) };
|
|
61
|
+
if (collection === 'controlledGoods')
|
|
62
|
+
return { collection, ...await this.service.listControlledGoods(scope, pageQuery) };
|
|
63
|
+
if (collection === 'sanctionedCompanies')
|
|
64
|
+
return { collection, ...await this.service.listSanctionedCompanies(scope, pageQuery) };
|
|
65
|
+
if (collection === 'suppliers')
|
|
66
|
+
return { collection, ...await this.service.listSuppliers(scope, pageQuery) };
|
|
67
|
+
if (collection === 'purchaseOrders') {
|
|
68
|
+
const supplierId = requiredString(parameters.supplierId, 'supplierId');
|
|
69
|
+
const [page, supplier, metrics] = await Promise.all([
|
|
70
|
+
this.service.listPurchaseOrders(scope, supplierId, pageQuery),
|
|
71
|
+
this.service.getSupplier(scope, supplierId),
|
|
72
|
+
this.service.getSupplierProcurementMetrics(scope, supplierId)
|
|
73
|
+
]);
|
|
74
|
+
return { collection, ...page, supplier, metrics };
|
|
75
|
+
}
|
|
76
|
+
if (collection === 'purchaseLines') {
|
|
77
|
+
const purchaseOrderId = requiredString(parameters.purchaseOrderId, 'purchaseOrderId');
|
|
78
|
+
const [page, order] = await Promise.all([
|
|
79
|
+
this.service.listPurchaseLines(scope, purchaseOrderId, pageQuery),
|
|
80
|
+
this.service.getPurchaseOrder(scope, purchaseOrderId)
|
|
81
|
+
]);
|
|
82
|
+
return { collection, ...page, order, supplier: await this.service.getSupplier(scope, order.supplierId) };
|
|
83
|
+
}
|
|
84
|
+
if (collection === 'customerContracts')
|
|
85
|
+
return { collection, ...await this.service.listCustomerContracts(scope, pageQuery) };
|
|
86
|
+
if (collection === 'salesCustomers')
|
|
87
|
+
return { collection, ...await this.service.listSalesCustomers(scope, pageQuery) };
|
|
88
|
+
if (collection === 'salesOrders')
|
|
89
|
+
return { collection, ...await this.service.listSalesOrders(scope, requiredString(parameters.customerId, 'customerId'), pageQuery) };
|
|
90
|
+
if (collection === 'salesLines') {
|
|
91
|
+
const [customerContractId, purchaseOrderId] = salesOrderKey(requiredString(parameters.orderKey, 'orderKey'));
|
|
92
|
+
return { collection, ...await this.service.listSalesLines(scope, { customerId: requiredString(parameters.customerId, 'customerId'), customerContractId, purchaseOrderId, ...pageQuery }) };
|
|
93
|
+
}
|
|
94
|
+
if (collection === 'salesFileOrders')
|
|
95
|
+
return { collection, ...await this.service.listSalesFileOrders(scope, pageQuery) };
|
|
96
|
+
if (collection === 'generatedFiles')
|
|
97
|
+
return { collection, ...await this.service.listGeneratedFiles(scope, { ...pageQuery, parameters: { ...pageQuery.parameters, customerContractId: parameters.customerContractId, purchaseOrderId: parameters.purchaseOrderId } }) };
|
|
98
|
+
if (collection === 'analytics')
|
|
99
|
+
return { collection, ...await this.service.analytics(scope, parseAnalyticsQuery({ mode: parameters.mode, dimension: parameters.dimension, filters: record(parameters.filters), page: query?.page, pageSize: query?.pageSize })) };
|
|
100
|
+
if (collection === 'settings')
|
|
101
|
+
return { collection, ...await this.service.settings(scope) };
|
|
102
|
+
if (collection === 'customerContractAnalysis')
|
|
103
|
+
return { collection, ...await this.service.getCustomerContractReanalysisResult(scope, requiredString(parameters.runId, 'runId')) };
|
|
104
|
+
if (collection === 'importTask')
|
|
105
|
+
return { collection, ...await this.service.getImportTaskResult(scope, requiredString(parameters.taskId, 'taskId')) };
|
|
106
|
+
if (collection === 'importIssues')
|
|
107
|
+
return { collection, ...await this.service.listImportIssues(scope, {
|
|
108
|
+
documentType: catalogDocumentType(parameters.documentType), batchId: requiredString(parameters.batchId, 'batchId'), issueType: importIssueType(parameters.issueType), page: pageQuery.page, pageSize: pageQuery.pageSize
|
|
109
|
+
}) };
|
|
110
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的数据集合', { collection });
|
|
111
|
+
}
|
|
112
|
+
const all = { page: 1, pageSize: 200, search: query?.search, parameters };
|
|
113
|
+
const catalogPage = { page: 1, pageSize: 20, parameters: {} };
|
|
114
|
+
const [dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, customers, derivedSalesOrders, generatedFiles, settings, analytics, importTasks] = await Promise.all([
|
|
115
|
+
this.service.dashboard(scope), this.service.listControlledGoods(scope, catalogPage), this.service.listControlledBatches(scope, all), this.service.listSanctionedCompanies(scope, catalogPage), this.service.listSanctionBatches(scope, all), this.service.listCustomers(scope, all), this.service.listDerivedSalesOrders(scope), this.service.listGeneratedFiles(scope, all), this.service.settings(scope), this.service.analytics(scope, { mode: 'PURCHASE', dimension: 'SUPPLIER', filters: {}, page: 1, pageSize: 20 }), this.service.listImportTasks(scope, all)
|
|
55
116
|
]);
|
|
56
|
-
|
|
117
|
+
const emptyCollection = { items: [], total: 0, page: 1, pageSize: 20 };
|
|
118
|
+
const uploadPolicy = { maxUploadMb: resolveTradeComplianceConfig(this.configResolver, scope.organizationId).storage.maxUploadMb };
|
|
119
|
+
return { items: [], total: 0, summary: { uploadPolicy, dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, suppliers: emptyCollection, purchaseOrders: emptyCollection, purchaseLines: emptyCollection, customers, customerContracts: emptyCollection, derivedSalesOrders, generatedFiles, settings, analytics, importTasks } };
|
|
57
120
|
}
|
|
58
121
|
async executeViewAction(context, _viewKey, actionKey, request) {
|
|
59
122
|
const scope = scopeFromContext(context);
|
|
@@ -62,6 +125,22 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
|
|
|
62
125
|
let data;
|
|
63
126
|
switch (actionKey) {
|
|
64
127
|
case 'refresh': return { success: true, refresh: true, message: text('Refreshed', '已刷新') };
|
|
128
|
+
case 'load_excel_editor_runtime': {
|
|
129
|
+
const [script, editorCss] = await Promise.all([
|
|
130
|
+
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.js'), 'utf8'),
|
|
131
|
+
readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.css'), 'utf8')
|
|
132
|
+
]);
|
|
133
|
+
data = { script, css: sanitizeIframeCss(editorCss) };
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'seed_demo_data': {
|
|
137
|
+
RequestContext.hasPermission(PermissionsEnum.ORG_DEMO_EDIT, true);
|
|
138
|
+
parseDemoSeedInput(input);
|
|
139
|
+
if (!this.demoData)
|
|
140
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '演示数据服务不可用');
|
|
141
|
+
data = await this.demoData.seed(scope);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
65
144
|
case 'save_controlled_goods':
|
|
66
145
|
data = await this.service.saveControlledGoods(scope, input);
|
|
67
146
|
break;
|
|
@@ -104,21 +183,64 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
|
|
|
104
183
|
case 'get_customer_contract_review':
|
|
105
184
|
data = await this.service.getCustomerContractReview(scope, requiredString(input.id, 'id'));
|
|
106
185
|
break;
|
|
107
|
-
case 'update_customer_contract_review':
|
|
108
|
-
|
|
186
|
+
case 'update_customer_contract_review': {
|
|
187
|
+
const parsed = customerContractReviewUpdate(request?.input);
|
|
188
|
+
data = await this.service.updateCustomerContractReview(scope, parsed.id, parsed.update);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case 'reanalyze_customer_contract': {
|
|
192
|
+
const started = await this.service.startCustomerContractReanalysis(scope, { reviewSource: customerContractReviewSource(input.reviewSource), id: stringValue(input.id), importTaskId: stringValue(input.importTaskId) });
|
|
193
|
+
const proposedCommand = started.run.executionCommand ?? buildCustomerContractReanalysisCommand(started);
|
|
194
|
+
const run = await this.service.saveCustomerContractReanalysisExecution(scope, started.run.id, proposedCommand);
|
|
195
|
+
if (!run.executionCommand)
|
|
196
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '重新分析任务缺少已持久化的执行命令');
|
|
197
|
+
data = { run, commands: [run.executionCommand], polling: { intervalMs: 2000, timeoutMs: 1200000 } };
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case 'mark_customer_contract_reanalysis_dispatched':
|
|
201
|
+
data = await this.service.markCustomerContractReanalysisDispatched(scope, requiredString(input.runId, 'runId'));
|
|
202
|
+
break;
|
|
203
|
+
case 'mark_customer_contract_reanalysis_failed':
|
|
204
|
+
data = await this.service.markCustomerContractReanalysisFailed(scope, requiredString(input.runId, 'runId'), { errorCode: requiredString(input.errorCode, 'errorCode'), errorMessage: requiredString(input.errorMessage, 'errorMessage') });
|
|
109
205
|
break;
|
|
110
206
|
case 'delete_entity':
|
|
111
207
|
data = await this.service.softDelete(scope, requiredString(input.entityType, 'entityType'), requiredString(input.id, 'id'));
|
|
112
208
|
break;
|
|
113
|
-
case 'save_settings':
|
|
114
|
-
|
|
209
|
+
case 'save_settings': {
|
|
210
|
+
const settingsType = parseSettingsType(input.settingsType);
|
|
211
|
+
data = await this.service.saveSettings(scope, settingsType, record(input.values), parseExpectedRevision(input.expectedRevision, settingsType === 'rate'));
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case 'restore_settings_default':
|
|
215
|
+
data = await this.service.restoreSettingsDefault(scope, parseRestoreSettingsInput(input));
|
|
216
|
+
break;
|
|
217
|
+
case 'delete_settings_rate': {
|
|
218
|
+
const settingsType = parseSettingsType(input.settingsType);
|
|
219
|
+
if (settingsType !== 'rate')
|
|
220
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '删除月度汇率必须使用 rate 设置类型', { settingsType });
|
|
221
|
+
data = await this.service.deleteMonthlyRate(scope, { id: requiredString(input.id, 'id'), expectedRevision: parseExpectedRevision(input.expectedRevision) });
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case 'discard_company_logo': {
|
|
225
|
+
const logoFileId = requiredString(input.logoFileId, 'logoFileId');
|
|
226
|
+
await this.service.discardCompanyLogoDraft(scope, logoFileId);
|
|
227
|
+
data = { logoFileId };
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case 'export_analytics': {
|
|
231
|
+
const result = await this.service.exportAnalytics(scope, parseAnalyticsQuery({ mode: input.mode, dimension: input.dimension, filters: input.filters, page: 1, pageSize: 20 }));
|
|
232
|
+
data = { fileName: result.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case 'preflight_sales_file':
|
|
236
|
+
data = await this.salesFiles.preflight(scope, salesFileGenerationInput(input));
|
|
115
237
|
break;
|
|
116
238
|
case 'generate_sales_file':
|
|
117
|
-
data = await this.salesFiles.generate(scope, input);
|
|
239
|
+
data = await this.salesFiles.generate(scope, salesFileGenerationInput(input));
|
|
118
240
|
break;
|
|
119
241
|
case 'download_sales_file': {
|
|
120
242
|
const result = await this.salesFiles.download(scope, requiredString(input.id, 'id'));
|
|
121
|
-
data = { fileName: result.item.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
|
|
243
|
+
data = { fileName: result.item.fileName, storageFileId: result.item.storageFileId, editVersion: result.item.editVersion, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
|
|
122
244
|
break;
|
|
123
245
|
}
|
|
124
246
|
case 'download_import_issues': {
|
|
@@ -126,19 +248,39 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
|
|
|
126
248
|
data = { fileName: result.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
|
|
127
249
|
break;
|
|
128
250
|
}
|
|
251
|
+
case 'retry_import_task':
|
|
252
|
+
data = await this.startCatalogRetry(scope, requiredString(input.taskId, 'taskId'));
|
|
253
|
+
break;
|
|
254
|
+
case 'mark_import_command_dispatched':
|
|
255
|
+
data = await this.service.markImportCommandDispatched(scope, requiredString(input.taskId, 'taskId'));
|
|
256
|
+
break;
|
|
129
257
|
case 'download_source_file': {
|
|
130
|
-
const
|
|
131
|
-
|
|
258
|
+
const descriptor = await this.service.getSourceFileDescriptor(scope, requiredString(input.sourceFileId, 'sourceFileId'));
|
|
259
|
+
const buffer = await this.storage.read(descriptor.sourceFileId);
|
|
260
|
+
data = { fileName: descriptor.sourceFileName, mimeType: mimeTypeForFile(descriptor.sourceFileName), base64: buffer.toString('base64') };
|
|
132
261
|
break;
|
|
133
262
|
}
|
|
134
|
-
case '
|
|
135
|
-
|
|
263
|
+
case 'preview_source_file': {
|
|
264
|
+
const descriptor = await this.service.getSourceFileDescriptor(scope, requiredString(input.sourceFileId, 'sourceFileId'));
|
|
265
|
+
const buffer = await this.storage.read(descriptor.sourceFileId);
|
|
266
|
+
data = await sourcePreview(descriptor.sourceFileName, buffer);
|
|
136
267
|
break;
|
|
268
|
+
}
|
|
269
|
+
case 'save_edited_sales_file': {
|
|
270
|
+
const base64 = requiredString(input.base64, 'base64');
|
|
271
|
+
if (base64.length > Math.ceil(MAX_EDITED_WORKBOOK_BYTES / 3) * 4)
|
|
272
|
+
throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '编辑后的工作簿不能超过 20 MB');
|
|
273
|
+
const buffer = Buffer.from(base64, 'base64');
|
|
274
|
+
if (buffer.length > MAX_EDITED_WORKBOOK_BYTES)
|
|
275
|
+
throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '编辑后的工作簿不能超过 20 MB');
|
|
276
|
+
data = await this.salesFiles.saveEditedWorkbook(scope, requiredString(input.id, 'id'), buffer, { editVersion: requiredNonNegativeInteger(input.editVersion, 'editVersion'), expectedStorageFileId: requiredString(input.expectedStorageFileId, 'expectedStorageFileId') });
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
137
279
|
case 'search_hs_code':
|
|
138
|
-
data = await searchHsBianmaCodes({ keywords: requiredString(input.keywords, 'keywords'), page: numberValue(input.page, 1), filterFailureCode: true, displayChapter: false, displayEnName: true }, hsConfig());
|
|
280
|
+
data = await searchHsBianmaCodes({ keywords: requiredString(input.keywords, 'keywords'), page: numberValue(input.page, 1), filterFailureCode: true, displayChapter: false, displayEnName: true }, this.hsConfig(scope));
|
|
139
281
|
break;
|
|
140
282
|
case 'get_hs_code_detail':
|
|
141
|
-
data = await getHsBianmaCodeDetail({ code: stringValue(input.code), detailUrl: stringValue(input.detailUrl) }, hsConfig());
|
|
283
|
+
data = { ...await getHsBianmaCodeDetail({ code: stringValue(input.code), detailUrl: stringValue(input.detailUrl) }, this.hsConfig(scope)), queriedAt: new Date().toISOString() };
|
|
142
284
|
break;
|
|
143
285
|
default: throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的操作', { actionKey });
|
|
144
286
|
}
|
|
@@ -153,43 +295,96 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
|
|
|
153
295
|
return failure(new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的视图'));
|
|
154
296
|
const scope = scopeFromContext(context);
|
|
155
297
|
const input = record(request?.input);
|
|
298
|
+
let task;
|
|
156
299
|
try {
|
|
157
|
-
const
|
|
158
|
-
const format = formatOf(fileName);
|
|
159
|
-
const size = Number(file?.size ?? 0);
|
|
160
|
-
if (!format)
|
|
161
|
-
throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '仅支持 PDF、Word、Excel 和常见图片格式');
|
|
162
|
-
if (size > 20 * 1024 * 1024)
|
|
163
|
-
throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '文件不能超过 20 MB');
|
|
164
|
-
const buffer = bufferOf(file) ?? await bufferFromPath(input);
|
|
300
|
+
const buffer = bufferOf(file);
|
|
165
301
|
if (!buffer?.length)
|
|
166
302
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '无法读取上传文件内容');
|
|
303
|
+
if (actionKey === 'upload_company_logo') {
|
|
304
|
+
if (buffer.length > 2 * 1024 * 1024)
|
|
305
|
+
throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '公司 Logo 不能超过 2 MB');
|
|
306
|
+
const fileName = fileNameOf(file, input);
|
|
307
|
+
const format = validateSourceFile(fileName, String(file?.mimetype ?? file?.type ?? ''), buffer);
|
|
308
|
+
if (format !== 'PNG' && format !== 'JPEG')
|
|
309
|
+
throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '公司 Logo 仅支持 PNG、JPG/JPEG');
|
|
310
|
+
const asset = await this.service.createCompanyLogoDraft(scope, buffer, fileName);
|
|
311
|
+
return { success: true, refresh: false, message: text('Logo uploaded', '公司 Logo 上传成功'), data: { logoFileId: asset.id } };
|
|
312
|
+
}
|
|
313
|
+
const maxUploadMb = resolveTradeComplianceConfig(this.configResolver, scope.organizationId).storage.maxUploadMb;
|
|
314
|
+
if (buffer.length > maxUploadMb * 1024 * 1024)
|
|
315
|
+
throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, `文件不能超过 ${maxUploadMb} MB`);
|
|
316
|
+
const fileName = fileNameOf(file, input);
|
|
317
|
+
const format = validateSourceFile(fileName, String(file?.mimetype ?? file?.type ?? ''), buffer);
|
|
318
|
+
const sourcePlatformFileId = platformFileId(file, input);
|
|
319
|
+
if (!sourcePlatformFileId)
|
|
320
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '上传文件缺少平台文件标识,请重新上传');
|
|
167
321
|
const documentType = documentTypeForAction(actionKey);
|
|
168
322
|
const storageFileId = await this.storage.save(buffer, fileName, 'uploads');
|
|
169
|
-
const
|
|
323
|
+
const executionInput = executionInputForAction(actionKey, input);
|
|
324
|
+
task = await this.service.createImportTask(scope, { documentType, sourceFileId: storageFileId, sourceFileName: fileName, sourceFileFormat: format, sourcePlatformFileId, executionInput, status: 'EXTRACTING' });
|
|
170
325
|
let extracted = { text: '', chunks: [], kind: 'image', originalLength: 0 };
|
|
171
326
|
if (!['PNG', 'JPEG', 'WEBP'].includes(format))
|
|
172
327
|
extracted = await extractDocumentText({ buffer, fileName, mimeType: file?.mimetype ?? file?.type, maxChars: 100000 });
|
|
173
328
|
const parsingTask = await this.service.markImportTaskParsing(scope, task.id);
|
|
174
329
|
const command = buildAssistantCommand({ actionKey, file, input, task, extracted, catalogType: input.catalogType, replaceBatchId: input.replaceBatchId });
|
|
330
|
+
await this.service.saveImportTaskExecution(scope, task.id, command);
|
|
175
331
|
return { success: true, refresh: false, message: text('Parsing started', '文件已进入解析中,请勿重复操作'), data: { task: parsingTask, commands: [command], polling: { intervalMs: 2000, timeoutMs: 1200000 } } };
|
|
176
332
|
}
|
|
177
333
|
catch (error) {
|
|
334
|
+
if (task?.id)
|
|
335
|
+
await this.service.markImportTaskFailed(scope, task.id, { errorCode: error instanceof DomainError ? error.code : DomainErrorCodes.INVALID_INPUT, errorMessage: error instanceof Error ? error.message : String(error) });
|
|
178
336
|
return failure(error);
|
|
179
337
|
}
|
|
180
338
|
}
|
|
339
|
+
async startCatalogRetry(scope, sourceTaskId) {
|
|
340
|
+
let task;
|
|
341
|
+
try {
|
|
342
|
+
task = await this.service.createRetryImportTask(scope, sourceTaskId);
|
|
343
|
+
const buffer = await this.storage.read(task.sourceFileId);
|
|
344
|
+
let extracted = { text: '', chunks: [], kind: 'image', originalLength: 0 };
|
|
345
|
+
if (!['PNG', 'JPEG', 'WEBP'].includes(task.sourceFileFormat))
|
|
346
|
+
extracted = await extractDocumentText({ buffer, fileName: task.sourceFileName, maxChars: 100000 });
|
|
347
|
+
if (!task.sourcePlatformFileId)
|
|
348
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '源文件缺少平台文件标识,无法重新解析');
|
|
349
|
+
const executionInput = task.executionInput;
|
|
350
|
+
if (!executionInput)
|
|
351
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '重试任务缺少执行参数');
|
|
352
|
+
const file = { name: task.sourceFileName, size: buffer.length, fileAssetId: task.sourcePlatformFileId, fileId: task.sourcePlatformFileId };
|
|
353
|
+
const input = { ...executionInput, fileAssetId: task.sourcePlatformFileId };
|
|
354
|
+
const command = buildAssistantCommand({ actionKey: executionInput.actionKey, file, input, task, extracted, catalogType: executionInput.catalogType, replaceBatchId: executionInput.replaceBatchId });
|
|
355
|
+
const parsingTask = await this.service.markImportTaskParsing(scope, task.id);
|
|
356
|
+
await this.service.saveImportTaskExecution(scope, task.id, command);
|
|
357
|
+
return { task: parsingTask, commands: [command], polling: { intervalMs: 2000, timeoutMs: 1200000 } };
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
if (task?.id)
|
|
361
|
+
await this.service.markImportTaskFailed(scope, task.id, { errorCode: error instanceof DomainError ? error.code : DomainErrorCodes.INVALID_INPUT, errorMessage: error instanceof Error ? error.message : String(error) });
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
hsConfig(scope) {
|
|
366
|
+
return resolveTradeComplianceConfig(this.configResolver, scope.organizationId).hsBianma;
|
|
367
|
+
}
|
|
181
368
|
};
|
|
182
369
|
TradeComplianceWorkbenchViewProvider = __decorate([
|
|
183
370
|
Injectable(),
|
|
184
371
|
ViewExtensionProvider(VIEW_PROVIDER_KEY),
|
|
185
|
-
|
|
372
|
+
__param(3, Optional()),
|
|
373
|
+
__param(4, Optional()),
|
|
374
|
+
__param(4, Inject(PLUGIN_CONFIG_RESOLVER_TOKEN)),
|
|
375
|
+
__metadata("design:paramtypes", [TradeComplianceWorkbenchService,
|
|
376
|
+
SalesFileService,
|
|
377
|
+
TradeComplianceFileStorage,
|
|
378
|
+
TradeComplianceDemoDataService, Object])
|
|
186
379
|
], TradeComplianceWorkbenchViewProvider);
|
|
187
380
|
export { TradeComplianceWorkbenchViewProvider };
|
|
188
381
|
function actionManifests() {
|
|
189
382
|
const invoke = (key, label, icon = 'ri-edit-line') => ({ key, label: text(key, label), icon, actionType: 'invoke' });
|
|
190
383
|
const upload = (key, label) => ({ ...invoke(key, label, 'ri-upload-cloud-line'), transport: 'file', placement: 'toolbar' });
|
|
191
|
-
|
|
192
|
-
|
|
384
|
+
const fileInvoke = (key, label) => ({ ...invoke(key, label, 'ri-upload-cloud-line'), transport: 'file' });
|
|
385
|
+
return [invoke('refresh', '刷新', 'ri-refresh-line'), upload('upload_controlled_catalog', '上传管控目录'), upload('upload_sanction_catalog', '上传制裁目录'), upload('upload_purchase_contract', '上传采购合同'), upload('upload_customer_contract', '上传客户合同'), fileInvoke('upload_company_logo', '上传公司 Logo'),
|
|
386
|
+
{ ...invoke('seed_demo_data', 'seed_demo_data'), permissions: [PermissionsEnum.ORG_DEMO_EDIT] },
|
|
387
|
+
...['load_excel_editor_runtime', 'save_controlled_goods', 'update_controlled_goods', 'save_sanctioned_company', 'update_sanctioned_company', 'retry_import_task', 'mark_import_command_dispatched', 'save_purchase_review', 'save_customer_contract_review', 'update_supplier', 'update_purchase_order', 'add_purchase_line', 'update_purchase_line', 'update_customer', 'save_customer_profit_rate', 'save_derived_manual_data', 'get_customer_contract_review', 'update_customer_contract_review', 'reanalyze_customer_contract', 'mark_customer_contract_reanalysis_dispatched', 'mark_customer_contract_reanalysis_failed', 'delete_entity', 'save_settings', 'restore_settings_default', 'delete_settings_rate', 'discard_company_logo', 'export_analytics', 'preflight_sales_file', 'generate_sales_file', 'download_sales_file', 'download_import_issues', 'download_source_file', 'preview_source_file', 'save_edited_sales_file', 'search_hs_code', 'get_hs_code_detail'].map(key => invoke(key, key))];
|
|
193
388
|
}
|
|
194
389
|
function buildAssistantCommand({ actionKey, file, input, task, extracted, catalogType, replaceBatchId }) {
|
|
195
390
|
const toolName = actionKey === 'upload_controlled_catalog' ? TOOL_NAMES.saveControlledCatalog : actionKey === 'upload_sanction_catalog' ? TOOL_NAMES.saveSanctionCatalog : actionKey === 'upload_purchase_contract' ? TOOL_NAMES.savePurchaseDraft : TOOL_NAMES.saveCustomerContractDraft;
|
|
@@ -198,17 +393,179 @@ function buildAssistantCommand({ actionKey, file, input, task, extracted, catalo
|
|
|
198
393
|
const fixed = JSON.stringify({ importTaskId: task.id, sourceFileId: task.sourceFileId, sourceFileName: task.sourceFileName, catalogType, replaceBatchId });
|
|
199
394
|
return { commandKey: 'assistant.chat.send_message', payload: { text: `请解析上传的外贸合规文件。${requirements}\n解析完成后必须调用工具 ${toolName} 保存结果,以下固定参数必须原样传入:${fixed}\n统一使用中文说明,不要遗漏文件中的记录。${source}`, clientMessageId: `trade-compliance:${task.id}`, files: [assistantFile(file, input, task.sourceFileName)], followUpMode: 'queue', state: { tradeComplianceWorkbench: { actionKey, importTaskId: task.id } } } };
|
|
200
395
|
}
|
|
201
|
-
function
|
|
396
|
+
function buildCustomerContractReanalysisCommand(started) {
|
|
397
|
+
if (started.run.promptVersion !== 'CUSTOMER_CLAUSE_V1')
|
|
398
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的客户合同重新分析提示版本');
|
|
399
|
+
const fixed = JSON.stringify({ analysisRunId: started.run.id });
|
|
400
|
+
const clauses = JSON.stringify(started.clauses.map(clause => ({ clauseType: clause.clauseType, extractionStatus: clause.extractionStatus, reviewedText: clause.reviewedText, sourceText: clause.sourceText, sourceLocation: clause.sourceLocation, sourceContent: clause.sourceContent })));
|
|
401
|
+
return { commandKey: 'assistant.chat.send_message', payload: { text: `请重新分析客户合同七类关键条款。必须按 clauseType 返回风险等级、中文风险提示和修改建议,并调用工具 ${TOOL_NAMES.saveCustomerContractReanalysis} 保存结果。固定参数必须原样传入:${fixed}\n待分析条款:${clauses}`, clientMessageId: `trade-compliance:customer-reanalysis:${started.run.id}`, followUpMode: 'queue', state: { tradeComplianceWorkbench: { actionKey: 'reanalyze_customer_contract', analysisRunId: started.run.id } } } };
|
|
402
|
+
}
|
|
403
|
+
function assistantFile(file, input, fileName) {
|
|
404
|
+
const fileAssetId = input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId;
|
|
405
|
+
const storageFileId = input.storageFileId ?? file?.storageFileId;
|
|
406
|
+
return { id: fileAssetId, fileId: fileAssetId, fileAssetId, ...(storageFileId ? { storageFileId } : {}), name: fileName, originalName: fileName, mimeType: file?.mimetype ?? file?.type, size: file?.size, role: 'source_file' };
|
|
407
|
+
}
|
|
408
|
+
function platformFileId(file, input) { const value = input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId; return value ? String(value) : null; }
|
|
409
|
+
function executionInputForAction(actionKey, input) {
|
|
410
|
+
const typedActionKey = documentActionKey(actionKey);
|
|
411
|
+
const catalogType = input.catalogType === 'DUAL_USE' || input.catalogType === 'OVERSEAS_SANCTION' ? input.catalogType : undefined;
|
|
412
|
+
if (typedActionKey === 'upload_controlled_catalog' && !catalogType)
|
|
413
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '管控目录任务缺少目录类型');
|
|
414
|
+
return { actionKey: typedActionKey, ...(catalogType ? { catalogType } : {}), ...(input.replaceBatchId ? { replaceBatchId: String(input.replaceBatchId) } : {}) };
|
|
415
|
+
}
|
|
416
|
+
function documentActionKey(actionKey) { documentTypeForAction(actionKey); return actionKey; }
|
|
417
|
+
function catalogDocumentType(value) { if (value === 'CONTROLLED_CATALOG' || value === 'SANCTION_CATALOG')
|
|
418
|
+
return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的目录文档类型'); }
|
|
419
|
+
function importIssueType(value) { if (value === 'DUPLICATE' || value === 'INVALID')
|
|
420
|
+
return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的导入问题类型'); }
|
|
421
|
+
function customerContractReviewSource(value) { if (value === 'IMPORT_DRAFT' || value === 'EXISTING_CONTRACT')
|
|
422
|
+
return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的客户合同审核来源'); }
|
|
423
|
+
function salesOrderKey(value) { const parts = value.split(':'); if (parts.length !== 2 || parts.some(part => !part))
|
|
424
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售订单标识无效'); return parts; }
|
|
425
|
+
function salesFileGenerationInput(input) {
|
|
426
|
+
if (!SalesFileTypes.includes(input.fileType))
|
|
427
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售文件类型无效');
|
|
428
|
+
const fileType = input.fileType;
|
|
429
|
+
const bankType = input.bankType == null ? undefined : parseBankAccountType(input.bankType);
|
|
430
|
+
return { customerContractId: requiredString(input.customerContractId, 'customerContractId'), purchaseOrderId: requiredString(input.purchaseOrderId, 'purchaseOrderId'), fileType, ...(bankType ? { bankType } : {}) };
|
|
431
|
+
}
|
|
432
|
+
function requiredNonNegativeInteger(value, field) { const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 0)
|
|
433
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${field} 必须是非负整数`); return parsed; }
|
|
202
434
|
function documentTypeForAction(action) { const map = { upload_controlled_catalog: 'CONTROLLED_CATALOG', upload_sanction_catalog: 'SANCTION_CATALOG', upload_purchase_contract: 'PURCHASE_CONTRACT', upload_customer_contract: 'CUSTOMER_CONTRACT' }; const value = map[action]; if (!value)
|
|
203
435
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的上传操作', { action }); return value; }
|
|
204
436
|
function formatOf(fileName) { const extension = fileName.split('.').pop()?.toUpperCase(); const normalized = extension === 'JPG' ? 'JPEG' : extension; return SourceFileFormats.includes(normalized) ? normalized : null; }
|
|
437
|
+
export function validateSourceFile(fileName, mimeType, buffer) {
|
|
438
|
+
const extensionFormat = formatOf(fileName);
|
|
439
|
+
const mimeFormat = formatForMimeType(mimeType);
|
|
440
|
+
const signatureFormat = formatForSignature(buffer);
|
|
441
|
+
if (!extensionFormat)
|
|
442
|
+
throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '仅支持 PDF、Word、Excel 和常见图片格式');
|
|
443
|
+
if (extensionFormat !== mimeFormat || extensionFormat !== signatureFormat) {
|
|
444
|
+
throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '文件扩展名、MIME 类型与文件签名不一致');
|
|
445
|
+
}
|
|
446
|
+
return extensionFormat;
|
|
447
|
+
}
|
|
448
|
+
function formatForMimeType(mimeType) {
|
|
449
|
+
const formats = {
|
|
450
|
+
'application/pdf': 'PDF',
|
|
451
|
+
'application/msword': 'DOC',
|
|
452
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'DOCX',
|
|
453
|
+
'application/vnd.ms-excel': 'XLS',
|
|
454
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'XLSX',
|
|
455
|
+
'image/png': 'PNG',
|
|
456
|
+
'image/jpeg': 'JPEG',
|
|
457
|
+
'image/webp': 'WEBP'
|
|
458
|
+
};
|
|
459
|
+
return formats[mimeType.trim().toLowerCase()] ?? null;
|
|
460
|
+
}
|
|
461
|
+
function formatForSignature(buffer) {
|
|
462
|
+
if (buffer.subarray(0, 5).equals(Buffer.from('%PDF-')))
|
|
463
|
+
return 'PDF';
|
|
464
|
+
if (buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
|
|
465
|
+
return 'PNG';
|
|
466
|
+
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff)
|
|
467
|
+
return 'JPEG';
|
|
468
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString('ascii') === 'RIFF' && buffer.subarray(8, 12).toString('ascii') === 'WEBP')
|
|
469
|
+
return 'WEBP';
|
|
470
|
+
if (buffer.subarray(0, 8).equals(Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]))) {
|
|
471
|
+
let names;
|
|
472
|
+
try {
|
|
473
|
+
const container = XLSX.CFB.read(buffer, { type: 'buffer' });
|
|
474
|
+
names = container.FileIndex.filter((entry) => entry.type === 2).map((entry) => String(entry.name));
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const isWord = names.includes('WordDocument');
|
|
480
|
+
const isExcel = names.includes('Workbook') || names.includes('Book');
|
|
481
|
+
if (isWord !== isExcel)
|
|
482
|
+
return isWord ? 'DOC' : 'XLS';
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
if (buffer.length >= 4 && [[0x03, 0x04], [0x05, 0x06], [0x07, 0x08]].some(([third, fourth]) => buffer[0] === 0x50 && buffer[1] === 0x4b && buffer[2] === third && buffer[3] === fourth)) {
|
|
486
|
+
let container;
|
|
487
|
+
try {
|
|
488
|
+
container = XLSX.CFB.read(buffer, { type: 'buffer' });
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
const entries = container.FileIndex
|
|
494
|
+
.map((entry, index) => entry.type === 2 ? packageEntryName(container.FullPaths[index]) : null)
|
|
495
|
+
.filter((name) => Boolean(name));
|
|
496
|
+
if (!entries.includes('[Content_Types].xml'))
|
|
497
|
+
return null;
|
|
498
|
+
const isWord = entries.some(name => name.startsWith('word/'));
|
|
499
|
+
const isExcel = entries.some(name => name.startsWith('xl/'));
|
|
500
|
+
if (isWord !== isExcel)
|
|
501
|
+
return isWord ? 'DOCX' : 'XLSX';
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
function packageEntryName(fullPath) { const value = String(fullPath ?? '').replace(/\\/g, '/'); const rootSeparator = value.indexOf('/'); return rootSeparator < 0 ? value : value.slice(rootSeparator + 1); }
|
|
506
|
+
function mimeTypeForFile(fileName) { const format = formatOf(fileName); return format === 'PDF' ? 'application/pdf' : format === 'PNG' ? 'image/png' : format === 'JPEG' ? 'image/jpeg' : format === 'WEBP' ? 'image/webp' : format === 'DOC' ? 'application/msword' : format === 'DOCX' ? 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : format === 'XLS' ? 'application/vnd.ms-excel' : format === 'XLSX' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'application/octet-stream'; }
|
|
205
507
|
function scopeFromContext(context) { if (!context.tenantId || !context.organizationId)
|
|
206
508
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '请求上下文缺少租户或组织标识'); return { tenantId: context.tenantId, organizationId: context.organizationId, userId: context.userId }; }
|
|
509
|
+
function parseDemoSeedInput(input) {
|
|
510
|
+
if (Object.keys(input).length !== 1 || input.confirmation !== 'INIT_DEMO_DATA') {
|
|
511
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '初始化演示数据必须提供精确确认值 INIT_DEMO_DATA');
|
|
512
|
+
}
|
|
513
|
+
return { confirmation: input.confirmation };
|
|
514
|
+
}
|
|
207
515
|
function success(data) { return { success: true, message: text('Saved', '已保存'), data, refresh: true }; }
|
|
208
516
|
function failure(error) { const domain = error instanceof DomainError ? error : new DomainError(DomainErrorCodes.INVALID_INPUT, error instanceof Error ? error.message : String(error)); return { success: false, message: text(domain.message, domain.message), error: { code: domain.code, message: domain.message, details: domain.details }, refresh: false }; }
|
|
209
517
|
function record(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
210
518
|
function requiredString(value, field) { const result = String(value ?? '').trim(); if (!result)
|
|
211
519
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `缺少 ${field}`); return result; }
|
|
520
|
+
function customerContractReviewUpdate(value) {
|
|
521
|
+
const payload = objectInput(value, '客户合同审核载荷');
|
|
522
|
+
const contractInput = objectInput(payload.contract, '合同信息');
|
|
523
|
+
const contract = {
|
|
524
|
+
contractNo: typedRequiredString(contractInput.contractNo, '客户合同编码')
|
|
525
|
+
};
|
|
526
|
+
if ('signedAt' in contractInput)
|
|
527
|
+
contract.signedAt = nullableString(contractInput.signedAt, '签订日期');
|
|
528
|
+
if ('contractAmountReviewCny' in contractInput)
|
|
529
|
+
contract.contractAmountReviewCny = nullableDecimalString(contractInput.contractAmountReviewCny, '人民币预审合同金额');
|
|
530
|
+
const update = { contract };
|
|
531
|
+
if ('customer' in payload)
|
|
532
|
+
update.customer = objectInput(payload.customer, '客户信息');
|
|
533
|
+
if ('clauses' in payload) {
|
|
534
|
+
if (!Array.isArray(payload.clauses))
|
|
535
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '合同条款必须是数组');
|
|
536
|
+
update.clauses = payload.clauses;
|
|
537
|
+
}
|
|
538
|
+
if ('customerTransferConfirmation' in payload)
|
|
539
|
+
update.customerTransferConfirmation = objectInput(payload.customerTransferConfirmation, '客户转移确认');
|
|
540
|
+
return { id: typedRequiredString(payload.id, '客户合同标识'), update };
|
|
541
|
+
}
|
|
542
|
+
function objectInput(value, label) {
|
|
543
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
544
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是对象`);
|
|
545
|
+
return value;
|
|
546
|
+
}
|
|
547
|
+
function typedRequiredString(value, label) {
|
|
548
|
+
if (typeof value !== 'string')
|
|
549
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是字符串`);
|
|
550
|
+
const result = value.trim();
|
|
551
|
+
if (!result)
|
|
552
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}不能为空`);
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
function nullableString(value, label) {
|
|
556
|
+
if (value == null)
|
|
557
|
+
return null;
|
|
558
|
+
if (typeof value !== 'string')
|
|
559
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是字符串`);
|
|
560
|
+
return value.trim() || null;
|
|
561
|
+
}
|
|
562
|
+
function nullableDecimalString(value, label) {
|
|
563
|
+
if (value == null)
|
|
564
|
+
return null;
|
|
565
|
+
if (typeof value !== 'string' || !/^(?:0|[1-9]\d{0,15})(?:\.\d{1,4})?$/.test(value.trim()))
|
|
566
|
+
throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是最多四位小数的非负数字符串`);
|
|
567
|
+
return value.trim();
|
|
568
|
+
}
|
|
212
569
|
function stringValue(value) { const result = String(value ?? '').trim(); return result || undefined; }
|
|
213
570
|
function numberValue(value, fallback) { const result = Number(value); return Number.isFinite(result) ? result : fallback; }
|
|
214
571
|
function fileNameOf(file, input) { return String(file?.originalname ?? file?.name ?? input.fileName ?? 'uploaded-file'); }
|
|
@@ -216,7 +573,26 @@ function bufferOf(file) { const value = file?.buffer; if (Buffer.isBuffer(value)
|
|
|
216
573
|
return value; if (value instanceof ArrayBuffer)
|
|
217
574
|
return Buffer.from(value); if (ArrayBuffer.isView(value))
|
|
218
575
|
return Buffer.from(value.buffer, value.byteOffset, value.byteLength); return null; }
|
|
219
|
-
async function bufferFromPath(input) { const workspace = record(input.workspaceFile); const path = stringValue(workspace.workspacePath ?? workspace.filePath ?? input.workspacePath ?? input.filePath); return path ? readFile(path) : null; }
|
|
220
576
|
async function readPackageFile(packageName, filePath) { const root = dirname(requireFromHere.resolve(`${packageName}/package.json`)); return readFile(join(root, filePath), 'utf8'); }
|
|
221
|
-
function
|
|
577
|
+
async function sourcePreview(fileName, buffer) {
|
|
578
|
+
const format = formatForSignature(buffer);
|
|
579
|
+
if (format === 'PDF' || format === 'PNG' || format === 'JPEG' || format === 'WEBP') {
|
|
580
|
+
return { previewKind: 'URL', fileName, mimeType: mimeTypeForFormat(format), base64: buffer.toString('base64') };
|
|
581
|
+
}
|
|
582
|
+
if (format === 'DOC' || format === 'DOCX' || format === 'XLS' || format === 'XLSX') {
|
|
583
|
+
const extracted = await extractDocumentText({ buffer, fileName, mimeType: mimeTypeForFormat(format), maxChars: 100000 });
|
|
584
|
+
const content = (extracted.chunks?.join('\n\n') ?? extracted.text ?? '').trim();
|
|
585
|
+
if (!content)
|
|
586
|
+
return { previewKind: 'UNAVAILABLE', fileName, message: '该 Office 文件未提取到可预览文本' };
|
|
587
|
+
return { previewKind: 'TEXT', fileName, content };
|
|
588
|
+
}
|
|
589
|
+
return { previewKind: 'UNAVAILABLE', fileName, message: '该源文件暂无法在线预览' };
|
|
590
|
+
}
|
|
591
|
+
function mimeTypeForFormat(format) {
|
|
592
|
+
const values = {
|
|
593
|
+
PDF: 'application/pdf', DOC: 'application/msword', DOCX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', XLS: 'application/vnd.ms-excel', XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', PNG: 'image/png', JPEG: 'image/jpeg', WEBP: 'image/webp'
|
|
594
|
+
};
|
|
595
|
+
return values[format];
|
|
596
|
+
}
|
|
597
|
+
export function sanitizeIframeCss(css) { return css.replace(/<\/style/gi, (match) => `<\\/${match.slice(2)}`); }
|
|
222
598
|
//# sourceMappingURL=workbench-view.provider.js.map
|