dsh-data-cleaning-agent 0.6.2 → 0.7.0
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/CHANGELOG.md +21 -0
- package/README.en.md +5 -1
- package/README.md +4 -2
- package/docs/RELEASE-0.6.3.md +38 -0
- package/docs/RELEASE-0.7.0.md +44 -0
- package/docs/UI-WORKFLOW-V2-ACCEPTANCE.md +40 -0
- package/docs/UI-WORKFLOW-V2.md +16 -7
- package/lib/client.js +363 -79
- package/lib/qcc-command.js +27 -16
- package/lib/qcc-field-catalog.js +185 -0
- package/lib/qcc-safety.js +11 -1
- package/lib/qcc.js +276 -79
- package/lib/skill-enrich.js +2 -2
- package/lib/workflow-contract.js +17 -70
- package/package.json +4 -2
package/lib/client.js
CHANGED
|
@@ -113,7 +113,9 @@ window.__ModuleLoader__.load({
|
|
|
113
113
|
.dcAgentWorkbench {
|
|
114
114
|
display: flex;
|
|
115
115
|
flex-direction: column;
|
|
116
|
-
width: min(
|
|
116
|
+
width: min(460px, 42vw);
|
|
117
|
+
max-width: calc(100vw - 420px);
|
|
118
|
+
min-width: 360px;
|
|
117
119
|
height: 100vh;
|
|
118
120
|
border: 1px solid light-dark(#d5dae4, #2a3038);
|
|
119
121
|
border-radius: 12px 0 0 12px;
|
|
@@ -397,8 +399,8 @@ window.__ModuleLoader__.load({
|
|
|
397
399
|
.dcAgentWbHeader {
|
|
398
400
|
display: flex;
|
|
399
401
|
align-items: center;
|
|
400
|
-
gap:
|
|
401
|
-
padding: 14px
|
|
402
|
+
gap: 8px;
|
|
403
|
+
padding: 12px 14px;
|
|
402
404
|
border-bottom: 1px solid light-dark(#e4e8f0, #303947);
|
|
403
405
|
}
|
|
404
406
|
|
|
@@ -421,8 +423,18 @@ window.__ModuleLoader__.load({
|
|
|
421
423
|
background: light-dark(#edf4ff, #172841);
|
|
422
424
|
}
|
|
423
425
|
|
|
426
|
+
.dcAgentWbTitle b,
|
|
427
|
+
.dcAgentWbTitle small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
424
428
|
.dcAgentWbTitle b { display: block; font-size: 15px; }
|
|
425
|
-
.dcAgentWbTitle small { display: block; color: light-dark(#697386, #aab5c7); font-size: 11px; }
|
|
429
|
+
.dcAgentWbTitle small { display: block; max-width: 190px; color: light-dark(#697386, #aab5c7); font-size: 11px; }
|
|
430
|
+
|
|
431
|
+
.dcAgentWbActions {
|
|
432
|
+
display: flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
gap: 6px;
|
|
435
|
+
flex: 0 0 auto;
|
|
436
|
+
margin-left: auto;
|
|
437
|
+
}
|
|
426
438
|
|
|
427
439
|
.dcAgentQccBadge {
|
|
428
440
|
flex: 0 0 auto;
|
|
@@ -562,6 +574,12 @@ window.__ModuleLoader__.load({
|
|
|
562
574
|
}
|
|
563
575
|
.dcAgentTable th { color: light-dark(#697386, #aab5c7); font-weight: 600; }
|
|
564
576
|
.dcAgentTable td.num { font-variant-numeric: tabular-nums; }
|
|
577
|
+
.dcAgentTableWrap { max-width: 100%; overflow-x: auto; }
|
|
578
|
+
.dcAgentTableWrap .dcAgentTable { min-width: 720px; table-layout: auto; }
|
|
579
|
+
.dcAgentTableWrap .dcAgentTable th,
|
|
580
|
+
.dcAgentTableWrap .dcAgentTable td { min-width: 112px; white-space: nowrap; }
|
|
581
|
+
.dcAgentTableWrap .dcAgentTable th:first-child,
|
|
582
|
+
.dcAgentTableWrap .dcAgentTable td:first-child { min-width: 190px; }
|
|
565
583
|
|
|
566
584
|
.dcAgentError {
|
|
567
585
|
padding: 10px 12px;
|
|
@@ -705,15 +723,46 @@ window.__ModuleLoader__.load({
|
|
|
705
723
|
.dcAgentMappingRow > b { text-align: center; color: light-dark(#8792a3, #8290a5); }
|
|
706
724
|
.dcAgentRulesGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
707
725
|
.dcAgentFieldGroup { display: grid; gap: 7px; margin-top: 12px; }
|
|
708
|
-
.
|
|
726
|
+
.dcAgentFieldGroupHead { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
|
|
727
|
+
.dcAgentFieldGroupHead > div:first-child { display: grid; gap: 2px; min-width: 0; }
|
|
728
|
+
.dcAgentFieldGroupHead b { font-size: 12px; }
|
|
729
|
+
.dcAgentFieldGroupActions { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
|
|
730
|
+
.dcAgentFieldAction {
|
|
731
|
+
padding: 2px 7px;
|
|
732
|
+
border: 0;
|
|
733
|
+
border-radius: 6px;
|
|
734
|
+
background: transparent;
|
|
735
|
+
color: light-dark(#2869e6, #8cb3ff);
|
|
736
|
+
font: inherit;
|
|
737
|
+
font-size: 11px;
|
|
738
|
+
cursor: pointer;
|
|
739
|
+
}
|
|
740
|
+
.dcAgentFieldAction:hover, .dcAgentFieldAction:focus-visible { background: light-dark(#edf4ff, #172841); }
|
|
709
741
|
.dcAgentPreviewTable { margin-top: 12px; overflow: auto; border: 1px solid light-dark(#e4e8f0, #303947); border-radius: 10px; }
|
|
710
742
|
.dcAgentHistoryTask { display: block; width: 100%; padding: 10px; text-align: left; }
|
|
711
743
|
.dcAgentHistoryTask h3 { margin: 0; font-size: 12px; }
|
|
744
|
+
/*
|
|
745
|
+
* DSH 的 root.side slot 是覆盖层。桌面端工作台打开时,让带稳定标记的
|
|
746
|
+
* 会话滚动区主动让出同宽空间,确保消息流和中央 composer 不被遮挡。
|
|
747
|
+
* 移动端仍保留全屏工作台,不压缩底层会话。
|
|
748
|
+
*/
|
|
749
|
+
@media (min-width: 1041px) {
|
|
750
|
+
body:has(.dcAgentWorkbench) [data-conversation-scroll] {
|
|
751
|
+
box-sizing: border-box;
|
|
752
|
+
padding-right: min(460px, 42vw);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
@media (min-width: 761px) and (max-width: 1040px) {
|
|
756
|
+
body:has(.dcAgentWorkbench) [data-conversation-scroll] {
|
|
757
|
+
box-sizing: border-box;
|
|
758
|
+
padding-right: min(420px, 48vw);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
712
761
|
@media (max-width: 760px) {
|
|
713
|
-
.dcAgentWorkbench, .dcAgentWorkbench.is-expanded { width: 100vw; border-radius: 0; }
|
|
762
|
+
.dcAgentWorkbench, .dcAgentWorkbench.is-expanded { width: 100vw; max-width: none; min-width: 0; border-radius: 0; }
|
|
714
763
|
.dcAgentOverlay { background: rgba(8, 10, 14, 0.22); pointer-events: auto; }
|
|
715
764
|
.dcAgentStepper { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
716
|
-
.dcAgentQccBadge { display: none; }
|
|
765
|
+
.dcAgentQccBadge, .dcAgentJobsPill { display: none; }
|
|
717
766
|
.dcAgentCandidate { grid-template-columns: 1fr; }
|
|
718
767
|
.dcAgentCapabilities { justify-content: flex-start; padding-inline: 12px; }
|
|
719
768
|
.dcAgentCapability { min-width: 92px; }
|
|
@@ -725,6 +774,10 @@ window.__ModuleLoader__.load({
|
|
|
725
774
|
.dcAgentMappingRow { grid-template-columns: 1fr; }
|
|
726
775
|
.dcAgentMappingRow > b { display: none; }
|
|
727
776
|
}
|
|
777
|
+
@media (min-width: 761px) and (max-width: 1040px) {
|
|
778
|
+
.dcAgentWorkbench { width: 420px; max-width: 48vw; min-width: 360px; }
|
|
779
|
+
.dcAgentQccBadge, .dcAgentJobsPill { display: none; }
|
|
780
|
+
}
|
|
728
781
|
`;
|
|
729
782
|
|
|
730
783
|
/** 安装入口、会话能力按钮与右侧工作台样式。 */
|
|
@@ -792,30 +845,101 @@ window.__ModuleLoader__.load({
|
|
|
792
845
|
['validate_identity', '主体标识校验'],
|
|
793
846
|
['complete_fields', '补全所选企业字段'],
|
|
794
847
|
];
|
|
848
|
+
const SELF_RISK_FIELD_OPTIONS = [
|
|
849
|
+
['dishonest', '失信信息'], ['judgment_debtor', '被执行人'], ['consumption_restriction', '限制高消费'],
|
|
850
|
+
['terminated_case', '终本案件'], ['judicial_document', '裁判文书'], ['case_filing', '立案信息'],
|
|
851
|
+
['hearing_announcement', '开庭公告'], ['court_announcement', '法院公告'], ['service_notice', '送达公告'],
|
|
852
|
+
['bankruptcy_reorganization', '破产重整'], ['equity_freeze', '股权冻结'], ['judicial_auction', '司法拍卖'],
|
|
853
|
+
['valuation_inquiry', '询价评估'], ['pre_litigation_mediation', '诉前调解'], ['exit_restriction', '限制出境'],
|
|
854
|
+
['administrative_penalty', '行政处罚'], ['operating_exception', '经营异常'], ['serious_violation', '严重违法'],
|
|
855
|
+
['environmental_penalty', '环保处罚'], ['abnormal_taxpayer', '税务非正常户'], ['tax_arrears', '欠税公告'],
|
|
856
|
+
['tax_violation', '税收违法'], ['disciplinary_list', '惩戒名单'], ['default_matter', '违约事项'],
|
|
857
|
+
['guarantee', '担保信息'], ['equity_pledge_registration', '股权出质'], ['stock_pledge', '股权质押'],
|
|
858
|
+
['chattel_mortgage', '动产抵押'], ['land_mortgage', '土地抵押'], ['simple_cancellation', '简易注销'],
|
|
859
|
+
['cancellation_filing', '注销备案'], ['liquidation', '清算信息'], ['labor_arbitration', '劳动仲裁'],
|
|
860
|
+
['public_notice', '公示催告'], ['property_reward_notice', '财产悬赏公告'],
|
|
861
|
+
];
|
|
862
|
+
const RELATED_RISK_FIELD_OPTIONS = [
|
|
863
|
+
['dishonest', '失信被执行人'], ['judgment_debtor', '被执行人'], ['consumption_restriction', '限制高消费'],
|
|
864
|
+
['serious_violation', '严重违法'], ['tax_violation', '税收违法'], ['administrative_penalty', '行政处罚'],
|
|
865
|
+
['bankruptcy_reorganization', '破产重整'], ['terminated_case', '终本案件'], ['equity_freeze', '股权冻结'],
|
|
866
|
+
['tax_arrears', '欠税公告'], ['operating_exception', '经营异常'],
|
|
867
|
+
];
|
|
868
|
+
const RELATED_RISK_KEY_FIELD_OPTIONS = [
|
|
869
|
+
['dishonest', '失信被执行人'], ['serious_violation', '严重违法'], ['bankruptcy_reorganization', '破产重整'],
|
|
870
|
+
['tax_violation', '税收违法'], ['judgment_debtor', '被执行人'], ['terminated_case', '终本案件'],
|
|
871
|
+
['equity_freeze', '股权冻结'],
|
|
872
|
+
];
|
|
795
873
|
const FIELD_GROUPS = [
|
|
796
|
-
['
|
|
874
|
+
['company_registration', '企业工商信息', [
|
|
797
875
|
['company_name', '企业名称'], ['credit_no', '统一社会信用代码'], ['reg_no', '注册号'],
|
|
798
|
-
['
|
|
876
|
+
['org_no', '组织机构代码'], ['tax_no', '纳税人识别号'], ['reg_status', '登记状态'],
|
|
877
|
+
['legal_rep', '法定代表人'], ['reg_capital', '注册资本'],
|
|
799
878
|
['paid_capital', '实缴资本'], ['establish_date', '成立日期'], ['company_type', '企业类型'],
|
|
800
|
-
['
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
['registered_address', '注册地址'], ['
|
|
804
|
-
['
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
['company_profile', '企业简介'],
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
['
|
|
816
|
-
]],
|
|
879
|
+
['approval_date', '核准日期'], ['registration_authority', '登记机关'],
|
|
880
|
+
['taxpayer_qualification', '纳税人资质'], ['payment_line_no', '支付系统行号'],
|
|
881
|
+
['import_export_company_code', '进出口企业代码'], ['short_name', '企业简称'], ['english_name', '英文名'],
|
|
882
|
+
['registered_address', '注册地址'], ['mailing_address', '通信地址'], ['region', '所属地区'],
|
|
883
|
+
['business_scope', '经营范围'], ['industry_category', '国标行业'],
|
|
884
|
+
['operating_period', '营业期限'], ['company_size', '人员规模'], ['insured_count', '参保人数'],
|
|
885
|
+
['branch_insured_count', '分支机构参保人数'],
|
|
886
|
+
], 'get_company_registration_info'],
|
|
887
|
+
['company_profile', '企业简介', [
|
|
888
|
+
['qcc_industry', '企查查行业'], ['company_profile', '企业简介'],
|
|
889
|
+
['industry_chain_overview', '产业链概览'],
|
|
890
|
+
], 'get_company_profile'],
|
|
891
|
+
['contact_info', '联系方式', [
|
|
892
|
+
['contact_preferred_phone', '首选联系电话'], ['contact_phone_invalid_flag', '首选电话无效标记'],
|
|
893
|
+
['contact_phone_tags', '首选电话标签'], ['contact_preferred_email', '首选邮箱'],
|
|
894
|
+
['contact_official_website', '官方网站'], ['contact_official_website_icp', '官网 ICP 备案'],
|
|
895
|
+
], 'get_contact_info'],
|
|
896
|
+
['listing_info', '上市信息', [
|
|
897
|
+
['listing_date', '上市日期'], ['listing_short_name', '股票简称'], ['listing_stock_code', '股票代码'],
|
|
898
|
+
['listing_exchange', '上市交易所'], ['listing_board', '上市板块'], ['listing_former_short_name', '上市曾用名'],
|
|
899
|
+
['listing_total_market_value', '总市值'], ['listing_total_shares', '总股本'], ['listing_predicted_pe', '预测市盈率'],
|
|
900
|
+
['listing_float_market_value', '流通值'], ['listing_float_shares', '流通股'], ['listing_pb_ratio', '市净率'],
|
|
901
|
+
['listing_eps', 'EPS'], ['listing_voting_rights_difference', '表决权差异'],
|
|
902
|
+
['listing_registration_based', '是否注册制'],
|
|
903
|
+
], 'get_listing_info'],
|
|
904
|
+
['tax_invoice_info', '税务开票信息', [
|
|
905
|
+
['tax_company_name', '税务主体名称'], ['tax_identification_no', '税务纳税人识别号'],
|
|
906
|
+
['tax_company_type', '税务企业类型'], ['tax_business_status', '税务经营状态'],
|
|
907
|
+
['invoice_address', '开票地址'], ['invoice_phone', '开票联系电话'],
|
|
908
|
+
['invoice_bank', '开户行'], ['invoice_bank_account', '开户行账号'],
|
|
909
|
+
], 'get_tax_invoice_info'],
|
|
910
|
+
['import_export_credit', '进出口信用', [
|
|
911
|
+
['import_export_credit_no', '进出口统一社会信用代码'], ['import_export_customs', '所在地海关'],
|
|
912
|
+
['import_export_admin_division', '进出口行政区划'], ['import_export_address', '进出口备案地址'],
|
|
913
|
+
['import_export_economic_area', '经济区划'], ['import_export_trade_type', '经营类别'],
|
|
914
|
+
['import_export_statistical_economic_area', '统计经济区划'], ['import_export_industry', '进出口行业种类'],
|
|
915
|
+
['import_export_ecommerce_type', '跨境贸易电子商务类型'], ['import_export_credit_grade', '海关信用等级'],
|
|
916
|
+
['import_export_filing_date', '进出口备案日期'],
|
|
917
|
+
], 'get_import_export_credit'],
|
|
918
|
+
['company_risk_scan', '企业自身风险扫描', [
|
|
919
|
+
['risk_recorded_factor_count', '风险有记录因子数'], ['risk_no_record_factor_count', '风险无记录因子数'],
|
|
920
|
+
['risk_hit_summary', '企业自身风险命中摘要'],
|
|
921
|
+
...SELF_RISK_FIELD_OPTIONS.map(([id, label]) => [`risk_${id}_count`, `${label}条目数`]),
|
|
922
|
+
], 'get_company_risk_scan'],
|
|
923
|
+
['company_related_risk_scan', '企业关联风险扫描', [
|
|
924
|
+
['related_risk_party_count', '有风险关联方数'], ['related_risk_summary', '企业关联风险摘要'],
|
|
925
|
+
...RELATED_RISK_FIELD_OPTIONS.map(([id, label]) => [`related_risk_${id}_count`, `关联风险-${label}条目数`]),
|
|
926
|
+
...RELATED_RISK_KEY_FIELD_OPTIONS.map(([id, label]) => [`related_risk_${id}_party_count`, `关联风险-${label}命中关联方数`]),
|
|
927
|
+
], 'get_company_related_risk_scan'],
|
|
817
928
|
];
|
|
818
929
|
const ENRICHMENT_OPTIONS = FIELD_GROUPS.flatMap(([, , fields]) => fields);
|
|
930
|
+
const INPUT_ONLY_MAPPING_OPTIONS = [['phone', '联系电话']];
|
|
931
|
+
const RESULT_FIELD_LABELS = new Map([
|
|
932
|
+
...ENRICHMENT_OPTIONS,
|
|
933
|
+
...INPUT_ONLY_MAPPING_OPTIONS,
|
|
934
|
+
['biz_status', '经营状态'],
|
|
935
|
+
['risk_tags', '风险标签'],
|
|
936
|
+
['qcc_match_status', '匹配状态'],
|
|
937
|
+
['qcc_source', '数据来源'],
|
|
938
|
+
['qcc_error', '错误原因'],
|
|
939
|
+
['match_status', '匹配状态'],
|
|
940
|
+
['input_company', '原始企业名称'],
|
|
941
|
+
['candidate_company', '候选企业名称'],
|
|
942
|
+
]);
|
|
819
943
|
const MATCH_ANCHOR_OPTIONS = [
|
|
820
944
|
['company_name', '企业名称'], ['credit_no', '统一社会信用代码'], ['reg_no', '注册号'],
|
|
821
945
|
];
|
|
@@ -852,9 +976,13 @@ window.__ModuleLoader__.load({
|
|
|
852
976
|
function isKnownCleaningDraft(value) {
|
|
853
977
|
if (value === DEFAULT_SESSION_PROMPT) return true;
|
|
854
978
|
if (typeof value !== 'string') return false;
|
|
855
|
-
|
|
979
|
+
const wizardDraft = value.startsWith('请执行一项企业名单数据清洗补全任务。')
|
|
856
980
|
&& value.includes('企查查连接、套餐额度和费用均由当前用户自己的账号承担。')
|
|
857
981
|
&& value.includes('提供结果和待复核清单的导出。');
|
|
982
|
+
const executionDraft = value.startsWith('请执行已在「数据清洗补全工作台」确认的企业数据任务。')
|
|
983
|
+
&& value.includes('安全任务凭证:dcq-')
|
|
984
|
+
&& value.includes('data_cleaning_qcc_run');
|
|
985
|
+
return wizardDraft || executionDraft;
|
|
858
986
|
}
|
|
859
987
|
|
|
860
988
|
function clearCleaningDraft(ctx, sessionId, onlyDefault = false) {
|
|
@@ -1029,12 +1157,27 @@ window.__ModuleLoader__.load({
|
|
|
1029
1157
|
};
|
|
1030
1158
|
}
|
|
1031
1159
|
|
|
1160
|
+
/**
|
|
1161
|
+
* 工作台粘贴区兼容「每行一个企业名称 / 信用代码」。
|
|
1162
|
+
* 无分隔符且首行不是显式表头时,不得把第一家企业误当 CSV 表头。
|
|
1163
|
+
*/
|
|
1164
|
+
function plainEntityListDataset(text) {
|
|
1165
|
+
const lines = String(text ?? '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1166
|
+
if (!lines.length || lines.some((line) => /[,\t;]/.test(line))) return null;
|
|
1167
|
+
const explicitHeader = /^(?:name|company|company_name|credit_no|creditcode|unified_credit_code|主体标识|企业名称|公司名称|单位名称|统一社会信用代码|信用代码|注册号)$/i;
|
|
1168
|
+
if (explicitHeader.test(lines[0])) return null;
|
|
1169
|
+
return entriesToDataset(lines);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function resultFieldLabel(key) {
|
|
1173
|
+
return RESULT_FIELD_LABELS.get(String(key ?? '')) ?? String(key ?? '');
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1032
1176
|
const MAPPING_PATTERNS = [
|
|
1033
1177
|
['company_name', /^(name|company|company_name|企业名称|公司名称|单位名称)$/i],
|
|
1034
1178
|
['credit_no', /^(credit_no|creditCode|unified_credit_code|统一社会信用代码|信用代码)$/i],
|
|
1035
1179
|
['reg_no', /^(reg_no|registration_no|注册号)$/i],
|
|
1036
1180
|
['phone', /^(phone|mobile|tel|telephone|联系电话|手机号码|手机号)$/i],
|
|
1037
|
-
['province', /^(province|省份|省份地区)$/i],
|
|
1038
1181
|
['registered_address', /^(address|registered_address|注册地址|企业地址|公司地址)$/i],
|
|
1039
1182
|
];
|
|
1040
1183
|
|
|
@@ -1131,10 +1274,8 @@ window.__ModuleLoader__.load({
|
|
|
1131
1274
|
return task;
|
|
1132
1275
|
}
|
|
1133
1276
|
|
|
1134
|
-
async function
|
|
1277
|
+
async function createWorkflowTask(actions, sessionId, draft = {}) {
|
|
1135
1278
|
const key = String(sessionId || 'unassigned');
|
|
1136
|
-
const cached = workflowTaskBySession.get(key);
|
|
1137
|
-
if (cached) return cacheWorkflowTask(actions, sessionId, cached);
|
|
1138
1279
|
let pending = workflowTaskCreationBySession.get(key);
|
|
1139
1280
|
if (!pending) {
|
|
1140
1281
|
pending = requestJson('/data-cleaning/api/workflow/tasks', 'POST', {
|
|
@@ -1155,6 +1296,29 @@ window.__ModuleLoader__.load({
|
|
|
1155
1296
|
return cacheWorkflowTask(actions, sessionId, response.task);
|
|
1156
1297
|
}
|
|
1157
1298
|
|
|
1299
|
+
async function ensureWorkflowTask(actions, sessionId, draft = {}) {
|
|
1300
|
+
const key = String(sessionId || 'unassigned');
|
|
1301
|
+
const cached = workflowTaskBySession.get(key);
|
|
1302
|
+
if (cached) return cacheWorkflowTask(actions, sessionId, cached);
|
|
1303
|
+
return createWorkflowTask(actions, sessionId, draft);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
const EDITABLE_WORKFLOW_STATES = new Set(['draft', 'uploaded', 'parse_failed']);
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* 完成 / 已确认规则的任务不能就地改写。用户再次录入名单时,
|
|
1310
|
+
* 在同一清洗会话内自动创建新 taskId,避免把新名单写入旧任务。
|
|
1311
|
+
*/
|
|
1312
|
+
async function ensureEditableWorkflowTask(actions, sessionId, draft = {}, preferredTask) {
|
|
1313
|
+
const key = String(sessionId || 'unassigned');
|
|
1314
|
+
const current = workflowTaskBySession.get(key) ?? preferredTask ?? null;
|
|
1315
|
+
if (!current) return createWorkflowTask(actions, sessionId, draft);
|
|
1316
|
+
if (EDITABLE_WORKFLOW_STATES.has(current.state)) return cacheWorkflowTask(actions, sessionId, current);
|
|
1317
|
+
workflowTaskBySession.delete(key);
|
|
1318
|
+
actions.resetRunForNewDataset?.();
|
|
1319
|
+
return createWorkflowTask(actions, sessionId, draft);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1158
1322
|
/** 同一会话的 Host 写操作串行化,确保 expectedRevision 不因 UI 事件竞态失效。 */
|
|
1159
1323
|
function queueWorkflowOperation(sessionId, operation) {
|
|
1160
1324
|
const key = String(sessionId || 'unassigned');
|
|
@@ -1273,6 +1437,15 @@ window.__ModuleLoader__.load({
|
|
|
1273
1437
|
draft.paidConfirmed = false;
|
|
1274
1438
|
draft.error = null;
|
|
1275
1439
|
},
|
|
1440
|
+
resetRunForNewDataset: (draft) => {
|
|
1441
|
+
draft.profile = null;
|
|
1442
|
+
draft.clean = null;
|
|
1443
|
+
draft.complete = null;
|
|
1444
|
+
draft.qccEstimate = null;
|
|
1445
|
+
draft.qccRun = null;
|
|
1446
|
+
draft.paidConfirmed = false;
|
|
1447
|
+
draft.error = null;
|
|
1448
|
+
},
|
|
1276
1449
|
setDataset: (draft, dataset) => { draft.dataset = dataset; draft.error = null; },
|
|
1277
1450
|
setProfile: (draft, profile) => { draft.profile = profile; draft.error = null; },
|
|
1278
1451
|
setClean: (draft, clean) => { draft.clean = clean; draft.error = null; },
|
|
@@ -1379,6 +1552,18 @@ window.__ModuleLoader__.load({
|
|
|
1379
1552
|
await sessionConversation.send(prompt);
|
|
1380
1553
|
}
|
|
1381
1554
|
|
|
1555
|
+
function setQccAgentDraft(ctx, sessionId, prompt) {
|
|
1556
|
+
const conversation = typeof ctx.get === 'function' ? ctx.get('conversation') : ctx.conversation;
|
|
1557
|
+
const shell = conversation?.input?.shell?.(sessionId);
|
|
1558
|
+
if (!shell || typeof shell.setDraft !== 'function') {
|
|
1559
|
+
throw new Error('当前 DSH 版本没有可用的会话草稿回填能力');
|
|
1560
|
+
}
|
|
1561
|
+
const accepted = shell.setDraft(prompt);
|
|
1562
|
+
if (accepted === false) throw new Error('任务说明未能回填到当前对话框');
|
|
1563
|
+
ctx.sessions?.open?.(sessionId);
|
|
1564
|
+
return true;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1382
1567
|
function capabilityIcon(item, size = 16) {
|
|
1383
1568
|
return typeof item.icon === 'function'
|
|
1384
1569
|
? h(item.icon, { size, 'aria-hidden': 'true' })
|
|
@@ -1819,6 +2004,15 @@ window.__ModuleLoader__.load({
|
|
|
1819
2004
|
}),
|
|
1820
2005
|
label,
|
|
1821
2006
|
));
|
|
2007
|
+
const setPromptFieldGroup = (fields, enabledGroup) => {
|
|
2008
|
+
const groupIds = new Set(fields.map(([id]) => id));
|
|
2009
|
+
const selected = new Set(enrichmentKeys);
|
|
2010
|
+
for (const id of groupIds) {
|
|
2011
|
+
if (enabledGroup) selected.add(id); else selected.delete(id);
|
|
2012
|
+
}
|
|
2013
|
+
const catalogOrder = FIELD_GROUPS.flatMap(([, , groupFields]) => groupFields.map(([id]) => id));
|
|
2014
|
+
setEnrichmentKeys(catalogOrder.filter((id) => selected.has(id)));
|
|
2015
|
+
};
|
|
1822
2016
|
|
|
1823
2017
|
return h('div', { className: 'dcAgentPromptLayer' },
|
|
1824
2018
|
h('button', {
|
|
@@ -1896,13 +2090,22 @@ window.__ModuleLoader__.load({
|
|
|
1896
2090
|
) : null,
|
|
1897
2091
|
wizardStep === 3 ? h('div', { className: 'dcAgentWizardPane' },
|
|
1898
2092
|
h('h4', null, '选择清洗目标和补全字段'),
|
|
1899
|
-
h('p', null, '
|
|
2093
|
+
h('p', null, '共 128 个一企一行字段:基础 30、第一批 40、第二批 58。最终可用字段以当前用户连接的 QCC MCP 能力为准。'),
|
|
1900
2094
|
h('div', { className: 'dcAgentPromptGroup' },
|
|
1901
2095
|
h('b', null, '清洗目标'),
|
|
1902
2096
|
h('div', { className: 'dcAgentPromptChoices' }, choice(CLEANING_OPTIONS, cleaningKeys, setCleaningKeys)),
|
|
1903
2097
|
),
|
|
1904
|
-
FIELD_GROUPS.map(([groupId, label, fields]) => h('div', { className: 'dcAgentPromptGroup', key: groupId },
|
|
1905
|
-
h('
|
|
2098
|
+
FIELD_GROUPS.map(([groupId, label, fields, sourceTool]) => h('div', { className: 'dcAgentPromptGroup', key: groupId },
|
|
2099
|
+
h('div', { className: 'dcAgentFieldGroupHead' },
|
|
2100
|
+
h('div', null,
|
|
2101
|
+
h('b', null, `${label} · ${fields.filter(([id]) => enrichmentKeys.includes(id)).length}/${fields.length}`),
|
|
2102
|
+
sourceTool ? h('small', { className: 'dcAgentHint' }, `来源:${sourceTool}`) : null,
|
|
2103
|
+
),
|
|
2104
|
+
h('div', { className: 'dcAgentFieldGroupActions' },
|
|
2105
|
+
h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setPromptFieldGroup(fields, true) }, '全选'),
|
|
2106
|
+
h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setPromptFieldGroup(fields, false) }, '清空'),
|
|
2107
|
+
),
|
|
2108
|
+
),
|
|
1906
2109
|
h('div', { className: 'dcAgentPromptChoices' }, choice(fields, enrichmentKeys, setEnrichmentKeys)),
|
|
1907
2110
|
)),
|
|
1908
2111
|
) : null,
|
|
@@ -2054,6 +2257,8 @@ window.__ModuleLoader__.load({
|
|
|
2054
2257
|
}
|
|
2055
2258
|
return;
|
|
2056
2259
|
}
|
|
2260
|
+
const entityList = plainEntityListDataset(trimmed);
|
|
2261
|
+
if (entityList) return entityList;
|
|
2057
2262
|
return api('/data-cleaning/api/mvp/parse', { filename: 'data.csv', content: trimmed });
|
|
2058
2263
|
}
|
|
2059
2264
|
|
|
@@ -2085,9 +2290,19 @@ window.__ModuleLoader__.load({
|
|
|
2085
2290
|
}
|
|
2086
2291
|
|
|
2087
2292
|
async function persistParsedWorkflow(actions, sessionId, result, source = {}, task) {
|
|
2088
|
-
let current =
|
|
2293
|
+
let current = await ensureEditableWorkflowTask(actions, sessionId, {}, task);
|
|
2089
2294
|
const stagingKey = `session:${sessionId || 'unassigned'}`;
|
|
2090
2295
|
moveRuntime(stagingKey, current.id);
|
|
2296
|
+
// handleParse 可能先把新数据写入旧 task runtime。无论是否新建 taskId,
|
|
2297
|
+
// 都以本次解析结果覆盖目标 runtime,避免复用已完成任务的一行旧数据。
|
|
2298
|
+
const target = runtimeFor(current.id);
|
|
2299
|
+
target.rows = Array.isArray(result.rows) ? result.rows : [];
|
|
2300
|
+
target.headers = Array.isArray(result.headers) ? result.headers : [];
|
|
2301
|
+
target.source = {
|
|
2302
|
+
type: source.type || result.fmt || 'csv',
|
|
2303
|
+
fileName: source.fileName || '',
|
|
2304
|
+
sizeBytes: source.sizeBytes || 0,
|
|
2305
|
+
};
|
|
2091
2306
|
if (['draft', 'uploaded', 'parse_failed'].includes(current.state)) {
|
|
2092
2307
|
current = await workflowAction(actions, sessionId, current, 'upload', {
|
|
2093
2308
|
source: {
|
|
@@ -2107,6 +2322,7 @@ window.__ModuleLoader__.load({
|
|
|
2107
2322
|
function WorkbenchDrawer(props) {
|
|
2108
2323
|
const { useStore, actions } = props;
|
|
2109
2324
|
const sendSessionCommand = props.sendSessionCommand;
|
|
2325
|
+
const setSessionDraft = props.setSessionDraft;
|
|
2110
2326
|
const open = useStore((state) => state.open);
|
|
2111
2327
|
const step = useStore((state) => state.step);
|
|
2112
2328
|
const expanded = useStore((state) => state.expanded);
|
|
@@ -2173,7 +2389,7 @@ window.__ModuleLoader__.load({
|
|
|
2173
2389
|
if (draft.fieldSelection) actions.setFieldSelection(draft.fieldSelection);
|
|
2174
2390
|
for (const [key, value] of Object.entries(draft.matchRules || {})) actions.setMatchRule(key, value);
|
|
2175
2391
|
void queueWorkflowOperation(detail.sessionId, async () => {
|
|
2176
|
-
const task = await
|
|
2392
|
+
const task = await ensureEditableWorkflowTask(actions, detail.sessionId, draft);
|
|
2177
2393
|
return updateWorkflowTask(actions, detail.sessionId, task, draft);
|
|
2178
2394
|
}).catch((eventError) => actions.setError(eventError instanceof Error ? eventError.message : String(eventError)));
|
|
2179
2395
|
};
|
|
@@ -2258,6 +2474,9 @@ window.__ModuleLoader__.load({
|
|
|
2258
2474
|
const runtimeKey = cachedTask?.id ?? `session:${activeSessionId || 'unassigned'}`;
|
|
2259
2475
|
const runtime = runtimeFor(runtimeKey);
|
|
2260
2476
|
const lastCsv = runtime.lastCsv;
|
|
2477
|
+
const qccPreviewColumns = Array.isArray(qccRun?.rows) && qccRun.rows.length
|
|
2478
|
+
? Object.keys(qccRun.rows[0] || {}).slice(0, 8)
|
|
2479
|
+
: [];
|
|
2261
2480
|
const fieldByPattern = (pattern) => runtime.headers.find((field) => pattern.test(field)) ?? null;
|
|
2262
2481
|
const phoneField = fieldByPattern(/^(phone|mobile|tel|telephone|联系电话|手机号码|手机号)$/i);
|
|
2263
2482
|
const amountField = fieldByPattern(/^(amount|price|金额|注册资本)$/i);
|
|
@@ -2412,14 +2631,18 @@ window.__ModuleLoader__.load({
|
|
|
2412
2631
|
|
|
2413
2632
|
const estimateQcc = async () => {
|
|
2414
2633
|
const uniqueCompanies = new Set(runtime.rows.map((row) => String(row?.[nameField] ?? '').trim()).filter(Boolean)).size;
|
|
2415
|
-
const
|
|
2416
|
-
const
|
|
2634
|
+
const selected = fieldSelection.length ? fieldSelection : DEFAULT_ENRICHMENT_KEYS;
|
|
2635
|
+
const selectedGroups = uiFieldGroups.filter(([, , fields]) => fields.some(([id]) => selected.includes(id)));
|
|
2636
|
+
const sourceTools = [...new Set(selectedGroups.map(([, , , sourceTool]) => sourceTool).filter(Boolean))];
|
|
2637
|
+
const callsPerCompany = 1 + sourceTools.length;
|
|
2638
|
+
const estimatedCalls = uniqueCompanies * callsPerCompany;
|
|
2417
2639
|
actions.setQccEstimate({
|
|
2418
2640
|
uniqueCompanies,
|
|
2419
|
-
tools:
|
|
2420
|
-
|
|
2641
|
+
tools: ['主体查询', ...selectedGroups.map(([, label]) => label)],
|
|
2642
|
+
sourceTools,
|
|
2643
|
+
estimatedCalls,
|
|
2421
2644
|
maxCalls: 300,
|
|
2422
|
-
withinLimit: runtime.rows.length <= 100,
|
|
2645
|
+
withinLimit: runtime.rows.length <= 100 && estimatedCalls <= 300,
|
|
2423
2646
|
estimateType: 'upper-bound',
|
|
2424
2647
|
});
|
|
2425
2648
|
};
|
|
@@ -2491,15 +2714,47 @@ window.__ModuleLoader__.load({
|
|
|
2491
2714
|
return completedCommand.run;
|
|
2492
2715
|
};
|
|
2493
2716
|
|
|
2717
|
+
const prepareEditableQccCommand = async (payload) => {
|
|
2718
|
+
if (!activeSessionId || typeof setSessionDraft !== 'function') {
|
|
2719
|
+
throw new Error('当前会话无法回填可编辑的数据清洗补全任务说明');
|
|
2720
|
+
}
|
|
2721
|
+
const prepared = await api('/data-cleaning/api/g5/commands', {
|
|
2722
|
+
...payload,
|
|
2723
|
+
confirmPaidCalls: true,
|
|
2724
|
+
});
|
|
2725
|
+
const command = prepared?.command;
|
|
2726
|
+
if (!command?.commandId || !command?.prompt) throw new Error('Host 未返回有效的企查查任务命令');
|
|
2727
|
+
setSessionDraft(activeSessionId, command.prompt);
|
|
2728
|
+
actions.close();
|
|
2729
|
+
|
|
2730
|
+
// 发送权仍在用户手中:草稿回填不会调用 QCC。用户发送后,
|
|
2731
|
+
// Agent-owned 工具改变 command 状态;本地安全轮询只读取结果并回写工作流。
|
|
2732
|
+
void waitForQccCommand(command.commandId, 1800).then(async (completedCommand) => {
|
|
2733
|
+
if (completedCommand.state === 'failed') {
|
|
2734
|
+
const commandError = new Error(completedCommand.error?.message || '智能体企查查任务执行失败');
|
|
2735
|
+
commandError.code = completedCommand.error?.code;
|
|
2736
|
+
throw commandError;
|
|
2737
|
+
}
|
|
2738
|
+
if (!completedCommand.run) throw new Error('智能体企查查任务完成但结果已失效');
|
|
2739
|
+
await queueWorkflowOperation(activeSessionId, () => applyQccRun(completedCommand.run));
|
|
2740
|
+
}).catch(async (commandError) => {
|
|
2741
|
+
if (['QCC_NOT_CONNECTED', 'QCC_TOOL_UNAVAILABLE', 'QCC_AUTH_REQUIRED'].includes(commandError?.code)) {
|
|
2742
|
+
const latest = workflowTaskBySession.get(String(activeSessionId || 'unassigned')) ?? cachedTask;
|
|
2743
|
+
try { await workflowAction(actions, activeSessionId, latest, 'authorization-required'); } catch (_workflowError) {}
|
|
2744
|
+
}
|
|
2745
|
+
actions.setError(commandError instanceof Error ? commandError.message : String(commandError));
|
|
2746
|
+
});
|
|
2747
|
+
return command;
|
|
2748
|
+
};
|
|
2749
|
+
|
|
2494
2750
|
const runQcc = async () => {
|
|
2495
2751
|
if (busy || !qccEstimate || !paidConfirmed || !qccEstimate.withinLimit) return;
|
|
2496
2752
|
actions.setBusy(true);
|
|
2497
2753
|
actions.setError(null);
|
|
2498
2754
|
try {
|
|
2499
|
-
|
|
2500
|
-
const r = await executePreparedQccCommand({
|
|
2755
|
+
await prepareEditableQccCommand({
|
|
2501
2756
|
kind: 'enrich',
|
|
2502
|
-
taskId:
|
|
2757
|
+
taskId: cachedTask.id,
|
|
2503
2758
|
rows: runtime.rows,
|
|
2504
2759
|
headers: runtime.headers,
|
|
2505
2760
|
nameField,
|
|
@@ -2507,11 +2762,7 @@ window.__ModuleLoader__.load({
|
|
|
2507
2762
|
includeRisk: false,
|
|
2508
2763
|
concurrency: 2,
|
|
2509
2764
|
});
|
|
2510
|
-
await applyQccRun(r);
|
|
2511
2765
|
} catch (err) {
|
|
2512
|
-
if (['QCC_NOT_CONNECTED', 'QCC_TOOL_UNAVAILABLE', 'QCC_AUTH_REQUIRED'].includes(err?.code)) {
|
|
2513
|
-
try { await workflowAction(actions, activeSessionId, cachedTask, 'authorization-required'); } catch (_workflowError) {}
|
|
2514
|
-
}
|
|
2515
2766
|
actions.setError(err instanceof Error ? err.message : String(err));
|
|
2516
2767
|
} finally {
|
|
2517
2768
|
actions.setBusy(false);
|
|
@@ -2733,9 +2984,20 @@ window.__ModuleLoader__.load({
|
|
|
2733
2984
|
h('b', { className: tone ? `is-${tone}` : null }, displayStatValue(value))
|
|
2734
2985
|
);
|
|
2735
2986
|
const uiFieldGroups = Array.isArray(workflowContract?.fieldCatalog)
|
|
2736
|
-
? workflowContract.fieldCatalog.map((group) => [group.id, group.label, (group.fields || []).map((field) => [field.id, field.label])])
|
|
2987
|
+
? workflowContract.fieldCatalog.map((group) => [group.id, group.label, (group.fields || []).map((field) => [field.id, field.label]), group.sourceTool])
|
|
2737
2988
|
: FIELD_GROUPS;
|
|
2738
|
-
const
|
|
2989
|
+
const inputOnlyMappingFields = Array.isArray(workflowContract?.inputOnlyMappingFields)
|
|
2990
|
+
? workflowContract.inputOnlyMappingFields.map((field) => [field.id, field.label])
|
|
2991
|
+
: INPUT_ONLY_MAPPING_OPTIONS;
|
|
2992
|
+
const targetFields = [...uiFieldGroups.flatMap(([, , fields]) => fields), ...inputOnlyMappingFields];
|
|
2993
|
+
const setWorkbenchFieldGroup = (fields, enabledGroup) => {
|
|
2994
|
+
const selected = new Set(fieldSelection);
|
|
2995
|
+
for (const [id] of fields) {
|
|
2996
|
+
if (enabledGroup) selected.add(id); else selected.delete(id);
|
|
2997
|
+
}
|
|
2998
|
+
const catalogOrder = uiFieldGroups.flatMap(([, , groupFields]) => groupFields.map(([id]) => id));
|
|
2999
|
+
actions.setFieldSelection(catalogOrder.filter((id) => selected.has(id)));
|
|
3000
|
+
};
|
|
2739
3001
|
|
|
2740
3002
|
let pane;
|
|
2741
3003
|
if (step === 'history') {
|
|
@@ -2808,8 +3070,22 @@ window.__ModuleLoader__.load({
|
|
|
2808
3070
|
),
|
|
2809
3071
|
h('section', { className: 'dcAgentSection' },
|
|
2810
3072
|
h('h3', null, `输出字段 · 已选 ${fieldSelection.length}`),
|
|
2811
|
-
|
|
2812
|
-
h('
|
|
3073
|
+
h('div', { className: 'dcAgentRow', 'aria-label': '补全字段批次概览' },
|
|
3074
|
+
h('span', { className: 'dcAgentChip' }, '基础字段 30'),
|
|
3075
|
+
h('span', { className: 'dcAgentChip' }, '第一批 40'),
|
|
3076
|
+
h('span', { className: 'dcAgentChip' }, '第二批 58'),
|
|
3077
|
+
),
|
|
3078
|
+
uiFieldGroups.map(([groupId, label, fields, sourceTool]) => h('div', { key: groupId, className: 'dcAgentFieldGroup' },
|
|
3079
|
+
h('div', { className: 'dcAgentFieldGroupHead' },
|
|
3080
|
+
h('div', null,
|
|
3081
|
+
h('b', null, `${label} · ${fields.filter(([id]) => fieldSelection.includes(id)).length}/${fields.length}`),
|
|
3082
|
+
sourceTool ? h('small', { className: 'dcAgentHint' }, `来源:${sourceTool}`) : null,
|
|
3083
|
+
),
|
|
3084
|
+
h('div', { className: 'dcAgentFieldGroupActions' },
|
|
3085
|
+
h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setWorkbenchFieldGroup(fields, true) }, '全选'),
|
|
3086
|
+
h('button', { type: 'button', className: 'dcAgentFieldAction', onClick: () => setWorkbenchFieldGroup(fields, false) }, '清空'),
|
|
3087
|
+
),
|
|
3088
|
+
),
|
|
2813
3089
|
h('div', { className: 'dcAgentChips' }, fields.map(([key, fieldLabel]) => h('label', { key, className: `dcAgentChip${fieldSelection.includes(key) ? ' is-selected' : ''}` },
|
|
2814
3090
|
h('input', { type: 'checkbox', checked: fieldSelection.includes(key), onChange: () => actions.toggleField(key) }), ` ${fieldLabel}`,
|
|
2815
3091
|
))),
|
|
@@ -2860,14 +3136,14 @@ window.__ModuleLoader__.load({
|
|
|
2860
3136
|
);
|
|
2861
3137
|
} else if (step === 'match') {
|
|
2862
3138
|
pane = h('div', { className: 'dcAgentPane' },
|
|
2863
|
-
h('p', { className: 'dcAgentHint' }, '通过当前用户连接的企查查 MCP
|
|
3139
|
+
h('p', { className: 'dcAgentHint' }, '通过当前用户连接的企查查 MCP 定位企业主体,并按已选维度补全一企一行字段。多候选始终由人工选择;界面不生成虚构置信度。'),
|
|
2864
3140
|
h('section', { className: 'dcAgentSection' },
|
|
2865
3141
|
h('h3', null, '匹配依据'),
|
|
2866
|
-
h('div', { className: 'dcAgentChips' }, mappings.filter((mapping) => ['company_name', 'credit_no', 'reg_no'].includes(mapping.targetField)).map((mapping) => h('span', { key: mapping.sourceField, className: 'dcAgentChip' }, `${mapping.sourceField} → ${
|
|
3142
|
+
h('div', { className: 'dcAgentChips' }, mappings.filter((mapping) => ['company_name', 'credit_no', 'reg_no'].includes(mapping.targetField)).map((mapping) => h('span', { key: mapping.sourceField, className: 'dcAgentChip' }, `${mapping.sourceField} → ${resultFieldLabel(mapping.targetField)}`))),
|
|
2867
3143
|
h('p', { className: 'dcAgentHint' }, '统一社会信用代码等强标识优先;名称匹配不明确时进入人工核验队列。'),
|
|
2868
3144
|
),
|
|
2869
3145
|
h('section', { className: 'dcAgentSection' },
|
|
2870
|
-
h('h3', null, '
|
|
3146
|
+
h('h3', null, '企查查一企一行补全能力'),
|
|
2871
3147
|
h('div', { className: 'dcAgentRow' },
|
|
2872
3148
|
h('button', { type: 'button', className: 'dcAgentButton', disabled: busy, onClick: loadQccCapabilities }, busy ? '检测中…' : '检测企查查连接'),
|
|
2873
3149
|
qccCapabilities ? h('span', { className: 'dcAgentHint' }, qccCapabilities.capabilities?.ready ? '主体查询与工商信息工具已就绪' : '连接未就绪或基础工具不完整') : null,
|
|
@@ -2886,8 +3162,9 @@ window.__ModuleLoader__.load({
|
|
|
2886
3162
|
h('input', { type: 'checkbox', checked: paidConfirmed, 'aria-label': '确认使用当前用户的企查查账号额度', onChange: (event) => actions.setPaidConfirmed(event.target.checked) }),
|
|
2887
3163
|
'我已核对企业数量和调用上界,并确认使用自己连接的企查查 MCP 账号;额度或费用由该账号自行承担',
|
|
2888
3164
|
),
|
|
3165
|
+
h('p', { className: 'dcAgentHint' }, '下一步只会把中文任务说明回填到中央对话框。你可先阅读、补充备注,点击发送后才开始企查查调用。'),
|
|
2889
3166
|
h('div', { className: 'dcAgentRow' },
|
|
2890
|
-
h('button', { type: 'button', className: 'dcAgentButton is-primary', disabled: busy || !paidConfirmed || !qccEstimate.withinLimit, onClick: runQcc }, busy ? '
|
|
3167
|
+
h('button', { type: 'button', className: 'dcAgentButton is-primary', disabled: busy || !paidConfirmed || !qccEstimate.withinLimit, onClick: runQcc }, busy ? '生成中…' : '生成可编辑任务说明'),
|
|
2891
3168
|
),
|
|
2892
3169
|
) : null,
|
|
2893
3170
|
),
|
|
@@ -2902,10 +3179,10 @@ window.__ModuleLoader__.load({
|
|
|
2902
3179
|
Array.isArray(qccRun.rows) && qccRun.rows.length ? h('div', { className: 'dcAgentTableWrap' },
|
|
2903
3180
|
h('table', { className: 'dcAgentTable', 'aria-label': '匹配补全结果预览' },
|
|
2904
3181
|
h('thead', null, h('tr', null,
|
|
2905
|
-
|
|
3182
|
+
qccPreviewColumns.map((key) => h('th', { key }, resultFieldLabel(key))),
|
|
2906
3183
|
)),
|
|
2907
3184
|
h('tbody', null, qccRun.rows.slice(0, 8).map((row, index) => h('tr', { key: `${index}-${row?.qcc_credit_no || row?.credit_no || ''}` },
|
|
2908
|
-
|
|
3185
|
+
qccPreviewColumns.map((key) => h('td', { key }, String(row?.[key] ?? ''))),
|
|
2909
3186
|
))),
|
|
2910
3187
|
),
|
|
2911
3188
|
h('p', { className: 'dcAgentHint' }, `显示前 ${Math.min(8, qccRun.rows.length)} 行;完整结果在下载阶段生成耐久制品。`),
|
|
@@ -2926,7 +3203,7 @@ window.__ModuleLoader__.load({
|
|
|
2926
3203
|
h('p', { className: 'dcAgentHint' }, '执行本地确定性清洗并核对本任务所选补全字段。外部字段以当前用户企查查 MCP 的真实返回为准,缺失值不会被编造。'),
|
|
2927
3204
|
h('section', { className: 'dcAgentSection' },
|
|
2928
3205
|
h('h3', null, `已选补全字段 · ${fieldSelection.length}`),
|
|
2929
|
-
h('div', { className: 'dcAgentChips' }, fieldSelection.map((id) => h('span', { className: 'dcAgentChip', key: id },
|
|
3206
|
+
h('div', { className: 'dcAgentChips' }, fieldSelection.map((id) => h('span', { className: 'dcAgentChip', key: id }, resultFieldLabel(id)))),
|
|
2930
3207
|
),
|
|
2931
3208
|
h('section', { className: 'dcAgentSection' },
|
|
2932
3209
|
h('h3', null, '本地清洗预处理'),
|
|
@@ -3047,26 +3324,29 @@ window.__ModuleLoader__.load({
|
|
|
3047
3324
|
h('small', null, cachedTask ? `${WORKFLOW_STATE_LABELS[cachedTask.state] || cachedTask.state} · ${cachedTask.id.slice(0, 18)}` : '正在创建 Host taskId…'),
|
|
3048
3325
|
),
|
|
3049
3326
|
),
|
|
3050
|
-
h('
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3327
|
+
h('div', { className: 'dcAgentWbActions' },
|
|
3328
|
+
h('span', {
|
|
3329
|
+
className: 'dcAgentQccBadge',
|
|
3330
|
+
title: qccRun ? `任务 ${qccRun.runId}` : '仅在当前用户确认使用自己的企查查账号后调用',
|
|
3331
|
+
}, qccRun ? `QCC · ${qccRun.state}` : qccCapabilities ? (qccCapabilities.capabilities?.ready ? 'QCC · 已连接' : 'QCC · 能力不完整') : 'QCC · 待检测'),
|
|
3332
|
+
h('span', { className: 'dcAgentJobsPill', 'data-state': jobsPill(jobs).state, title: '后台任务状态' }, jobsPill(jobs).label),
|
|
3333
|
+
h('button', {
|
|
3334
|
+
className: 'dcAgentWbClose',
|
|
3335
|
+
type: 'button',
|
|
3336
|
+
'aria-label': expanded ? '收起工作台' : '展开工作台',
|
|
3337
|
+
onClick: () => actions.toggleExpanded(),
|
|
3338
|
+
}, expanded ? '↘' : '↖'),
|
|
3339
|
+
h('button', {
|
|
3340
|
+
className: 'dcAgentWbClose',
|
|
3341
|
+
type: 'button',
|
|
3342
|
+
title: '关闭工作台(Esc)',
|
|
3343
|
+
'aria-label': '关闭',
|
|
3344
|
+
onClick: () => {
|
|
3345
|
+
stopJobsPolling();
|
|
3346
|
+
actions.close();
|
|
3347
|
+
},
|
|
3348
|
+
}, '✕'),
|
|
3349
|
+
),
|
|
3070
3350
|
),
|
|
3071
3351
|
h('nav', { className: 'dcAgentStepper', 'aria-label': '清洗流程' },
|
|
3072
3352
|
STEPS.map((st) => h('button', {
|
|
@@ -3114,6 +3394,7 @@ window.__ModuleLoader__.load({
|
|
|
3114
3394
|
store: workbenchStore,
|
|
3115
3395
|
inject: () => ({
|
|
3116
3396
|
sendSessionCommand: (sessionId, prompt) => sendQccAgentCommand(ctx, sessionId, prompt),
|
|
3397
|
+
setSessionDraft: (sessionId, prompt) => setQccAgentDraft(ctx, sessionId, prompt),
|
|
3117
3398
|
}),
|
|
3118
3399
|
}, WorkbenchDrawer));
|
|
3119
3400
|
|
|
@@ -3182,8 +3463,11 @@ window.__ModuleLoader__.load({
|
|
|
3182
3463
|
isKnownCleaningDraft,
|
|
3183
3464
|
markCleaningSession,
|
|
3184
3465
|
qualitySummaryFor,
|
|
3466
|
+
plainEntityListDataset,
|
|
3467
|
+
resultFieldLabel,
|
|
3185
3468
|
rewriteHeroChrome,
|
|
3186
3469
|
ensureWorkflowTask,
|
|
3470
|
+
ensureEditableWorkflowTask,
|
|
3187
3471
|
queueWorkflowOperation,
|
|
3188
3472
|
};
|
|
3189
3473
|
return module.exports;
|