neo-cmp-cli 1.12.7 → 1.12.9

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.
Files changed (144) hide show
  1. package/README.md +204 -6
  2. package/dist/index2.js +1 -1
  3. package/dist/neo/env.js +1 -1
  4. package/dist/package.json.js +1 -1
  5. package/package.json +1 -1
  6. package/template/antd-custom-cmp-template/package.json +1 -1
  7. package/template/{neo-bi-cmps → asset-manage-template}/README.md +65 -10
  8. package/template/asset-manage-template/docs/README.md +244 -0
  9. package/template/asset-manage-template/neo.config.js +60 -0
  10. package/template/{neo-bi-cmps → asset-manage-template}/package.json +28 -16
  11. package/template/asset-manage-template/src/assets/img/chart.svg +1 -0
  12. package/template/asset-manage-template/src/components/README.md +3 -0
  13. package/template/asset-manage-template/src/components/assetManage__c/assetApi.ts +70 -0
  14. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetCreateModal.tsx +260 -0
  15. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetGrid.tsx +48 -0
  16. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetSidebar.tsx +74 -0
  17. package/template/asset-manage-template/src/components/assetManage__c/cmps/AssetToolbar.tsx +79 -0
  18. package/template/asset-manage-template/src/components/assetManage__c/cmps/assetDisplay.tsx +72 -0
  19. package/template/asset-manage-template/src/components/assetManage__c/constants.ts +28 -0
  20. package/template/asset-manage-template/src/components/assetManage__c/index.tsx +258 -0
  21. package/template/asset-manage-template/src/components/assetManage__c/model.ts +75 -0
  22. package/template/asset-manage-template/src/components/assetManage__c/style.scss +425 -0
  23. package/template/asset-manage-template/src/components/assetManage__c/types.ts +60 -0
  24. package/template/asset-manage-template/src/components/bidList__c/cmps/BidCard.tsx +47 -0
  25. package/template/asset-manage-template/src/components/bidList__c/constants.ts +6 -0
  26. package/template/asset-manage-template/src/components/bidList__c/formatUtils.ts +14 -0
  27. package/template/asset-manage-template/src/components/bidList__c/index.tsx +194 -0
  28. package/template/asset-manage-template/src/components/bidList__c/model.ts +57 -0
  29. package/template/asset-manage-template/src/components/bidList__c/style.scss +179 -0
  30. package/template/asset-manage-template/src/components/bidList__c/types.ts +10 -0
  31. package/template/asset-manage-template/src/components/bidPackage__c/cmps/BidPackageHeader.tsx +140 -0
  32. package/template/asset-manage-template/src/components/bidPackage__c/cmps/PackageItemTable.tsx +148 -0
  33. package/template/asset-manage-template/src/components/bidPackage__c/index.tsx +394 -0
  34. package/template/asset-manage-template/src/components/bidPackage__c/mainTableColumns.tsx +57 -0
  35. package/template/asset-manage-template/src/components/bidPackage__c/model.ts +86 -0
  36. package/template/asset-manage-template/src/components/bidPackage__c/style.scss +256 -0
  37. package/template/asset-manage-template/src/components/bidPackage__c/types.ts +35 -0
  38. package/template/asset-manage-template/src/components/bidPackage__c/utils.ts +19 -0
  39. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/axiosFetcher.ts +0 -0
  40. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/queryObjectData.ts +0 -0
  41. package/template/asset-manage-template/src/utils/url.ts +82 -0
  42. package/template/{neo-bi-cmps → asset-manage-template}/src/utils/xobjects.ts +0 -0
  43. package/template/{neo-bi-cmps → asset-manage-template}/tsconfig.json +1 -1
  44. package/template/echarts-custom-cmp-template/package.json +1 -1
  45. package/template/empty-custom-cmp-template/package.json +2 -2
  46. package/template/neo-custom-cmp-template/package.json +2 -2
  47. package/template/neo-custom-cmp-template/src/components/entityTable__c/index.tsx +62 -6
  48. package/template/neo-h5-cmps/neo.config.js +34 -76
  49. package/template/neo-h5-cmps/package.json +7 -3
  50. package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +0 -4
  51. package/template/neo-h5-cmps/src/components/entityList__c/model.ts +10 -5
  52. package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +29 -17
  53. package/template/neo-h5-cmps/src/components/entityTabs__c/model.ts +25 -5
  54. package/template/neo-h5-cmps/src/components/entityTabs__c/style.scss +11 -22
  55. package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +3 -1
  56. package/template/neo-h5-cmps/src/utils/xobjects.ts +8 -3
  57. package/template/neo-h5-cmps/tsconfig.json +1 -1
  58. package/template/neo-order-cmps/package.json +2 -2
  59. package/template/react-custom-cmp-template/package.json +1 -1
  60. package/template/react-ts-custom-cmp-template/package.json +1 -1
  61. package/template/vue2-custom-cmp-template/package.json +1 -1
  62. package/template/develop/BI /351/241/271/347/233/256/345/210/206/346/236/220/346/212/245/345/221/212.md" +0 -562
  63. package/template/develop/ChatPage /347/273/204/344/273/266/344/275/277/347/224/250/350/257/264/346/230/216/346/226/207/346/241/243.md" +0 -507
  64. package/template/develop/EntityGrid Web /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -868
  65. package/template/develop/EntityList H5 /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -1386
  66. package/template/develop/GlobalSearch/347/273/204/344/273/266/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -866
  67. package/template/develop/Neo /344/270/255/345/217/257/347/224/250 amis /347/273/204/344/273/266.md" +0 -1490
  68. package/template/develop/cmpEventFunctions.ts +0 -257
  69. package/template/develop/cmpEvents.ts +0 -864
  70. package/template/develop/comTree/347/224/237/346/210/220/350/277/207/347/250/213/345/210/206/346/236/220.md +0 -469
  71. package/template/develop/commonModules.js +0 -55
  72. package/template/develop/components-table.md +0 -50
  73. package/template/develop/neo-custom-cmp-template/README.md +0 -48
  74. package/template/develop/neo-custom-cmp-template/docs/README.md +0 -13
  75. package/template/develop/neo-custom-cmp-template/neo.config.js +0 -121
  76. package/template/develop/neo-custom-cmp-template/package.json +0 -63
  77. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/README.md +0 -65
  78. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/index.tsx +0 -180
  79. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/model.ts +0 -50
  80. package/template/develop/neo-custom-cmp-template/src/components/contactCardList/style.scss +0 -260
  81. package/template/develop/neo-custom-cmp-template/src/components/contactForm/README.md +0 -94
  82. package/template/develop/neo-custom-cmp-template/src/components/contactForm/index.tsx +0 -252
  83. package/template/develop/neo-custom-cmp-template/src/components/contactForm/model.ts +0 -56
  84. package/template/develop/neo-custom-cmp-template/src/components/contactForm/style.scss +0 -120
  85. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/README.md +0 -115
  86. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/index.tsx +0 -304
  87. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/model.ts +0 -87
  88. package/template/develop/neo-custom-cmp-template/src/components/neoEntityGrid/style.scss +0 -127
  89. package/template/develop/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -29
  90. package/template/develop/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -39
  91. package/template/develop/neo-custom-cmp-template/src/utils/xobjects.ts +0 -203
  92. package/template/develop/neo-custom-cmp-template/tsconfig.json +0 -68
  93. package/template/develop/neo-ui-component-h5.md +0 -105
  94. package/template/develop/neo-ui-component-web-xregister.md +0 -31
  95. package/template/develop/neo-ui-component-web.md +0 -292
  96. package/template/develop/neoCmps.ts +0 -7508
  97. package/template/develop/neoGlobalSearchInput /347/273/204/344/273/266/347/232/204/350/257/246/347/273/206/345/210/206/346/236/220/346/226/207/346/241/243.md" +0 -497
  98. package/template/develop/pageSchema1.json +0 -744
  99. package/template/develop//344/272/213/344/273/266/345/212/250/344/275/234/344/277/235/345/255/230/346/265/201/347/250/213/345/210/206/346/236/220.md +0 -390
  100. package/template/develop//345/215/225/345/205/203/346/265/213/350/257/225/344/275/277/347/224/250/350/257/264/346/230/216.md +0 -1139
  101. package/template/neo-bi-cmps/.prettierrc.js +0 -12
  102. package/template/neo-bi-cmps/commitlint.config.js +0 -59
  103. package/template/neo-bi-cmps/neo.config.js +0 -124
  104. package/template/neo-bi-cmps/public/css/base.css +0 -283
  105. package/template/neo-bi-cmps/public/scripts/app/bluebird.js +0 -6679
  106. package/template/neo-bi-cmps/public/template.html +0 -13
  107. package/template/neo-bi-cmps/src/assets/css/common.scss +0 -127
  108. package/template/neo-bi-cmps/src/assets/css/mixin.scss +0 -47
  109. package/template/neo-bi-cmps/src/assets/img/NeoCRM.jpg +0 -0
  110. package/template/neo-bi-cmps/src/assets/img/custom-widget.svg +0 -1
  111. package/template/neo-bi-cmps/src/assets/img/favicon.png +0 -0
  112. package/template/neo-bi-cmps/src/assets/img/table.svg +0 -1
  113. package/template/neo-bi-cmps/src/components/targetNumber__c/README.md +0 -100
  114. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/configSchema.ts +0 -253
  115. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.scss +0 -76
  116. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +0 -148
  117. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +0 -440
  118. package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +0 -128
  119. package/template/neo-bi-cmps/src/components/targetNumber__c/style.scss +0 -173
  120. package/template/neo-h5-cmps/src/components/simpleTable__c/README.md +0 -90
  121. package/template/neo-h5-cmps/src/components/simpleTable__c/index.tsx +0 -277
  122. package/template/neo-h5-cmps/src/components/simpleTable__c/model.ts +0 -91
  123. package/template/neo-h5-cmps/src/components/simpleTable__c/style.scss +0 -116
  124. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/.prettierrc.js +0 -0
  125. /package/template/{neo-bi-cmps → asset-manage-template}/@types/neo-ui-common.d.ts +0 -0
  126. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/commitlint.config.js +0 -0
  127. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/css/base.css +0 -0
  128. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/scripts/app/bluebird.js +0 -0
  129. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/public/template.html +0 -0
  130. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/css/common.scss +0 -0
  131. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/css/mixin.scss +0 -0
  132. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/AIBtn.gif +0 -0
  133. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/NeoCRM.jpg +0 -0
  134. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/aiLogo.png +0 -0
  135. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/card-list.svg +0 -0
  136. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/contact-form.svg +0 -0
  137. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/custom-form.svg +0 -0
  138. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/custom-widget.svg +0 -0
  139. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/data-list.svg +0 -0
  140. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/detail.svg +0 -0
  141. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/favicon.png +0 -0
  142. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/map.svg +0 -0
  143. /package/template/{neo-bi-cmps → asset-manage-template}/src/assets/img/search.svg +0 -0
  144. /package/template/{develop/neo-custom-cmp-template → asset-manage-template}/src/assets/img/table.svg +0 -0
@@ -0,0 +1,148 @@
1
+ import * as React from 'react';
2
+ import { Table, InputNumber, Typography, Button, Empty } from 'antd';
3
+ import { PlusOutlined } from '@ant-design/icons';
4
+ import type { PackageData, PackageItemData } from '../types';
5
+ import { formatMoney } from '../utils';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export interface PackageItemTableProps {
10
+ pkg: PackageData;
11
+ editable: boolean;
12
+ onPriceChange: (
13
+ pkgKey: string,
14
+ itemKey: string,
15
+ field: 'price__c' | 'quantity__c',
16
+ val: number | null,
17
+ ) => void;
18
+ onAddItem: (pkg: PackageData) => void;
19
+ }
20
+
21
+ export function PackageItemTable({
22
+ pkg,
23
+ editable,
24
+ onPriceChange,
25
+ onAddItem,
26
+ }: PackageItemTableProps) {
27
+ if (pkg.items.length === 0) {
28
+ return (
29
+ <div className="item-table-wrapper item-empty-wrapper">
30
+ <Empty description="暂无明细" image={Empty.PRESENTED_IMAGE_SIMPLE}>
31
+ <Button
32
+ type="primary"
33
+ size="small"
34
+ icon={<PlusOutlined />}
35
+ onClick={() => onAddItem(pkg)}
36
+ >
37
+ 添加明细
38
+ </Button>
39
+ </Empty>
40
+ </div>
41
+ );
42
+ }
43
+
44
+ const columns: any[] = [
45
+ {
46
+ title: '品目名称',
47
+ dataIndex: 'name',
48
+ key: 'name',
49
+ width: '22%',
50
+ render: (v: string, record: PackageItemData) => (
51
+ <span>
52
+ {record.isDirty__c && <span className="dirty-dot" />}
53
+ {v}
54
+ </span>
55
+ ),
56
+ },
57
+ {
58
+ title: '规格型号',
59
+ dataIndex: 'spec__c',
60
+ key: 'spec__c',
61
+ width: '20%',
62
+ ellipsis: true,
63
+ },
64
+ {
65
+ title: '单位',
66
+ dataIndex: 'unit__c',
67
+ key: 'unit__c',
68
+ width: 70,
69
+ align: 'center' as const,
70
+ },
71
+ {
72
+ title: '单价 (¥)',
73
+ dataIndex: 'price__c',
74
+ key: 'price__c',
75
+ width: 160,
76
+ render: (v: number, record: PackageItemData) =>
77
+ editable ? (
78
+ <InputNumber
79
+ value={v}
80
+ min={0}
81
+ precision={2}
82
+ style={{ width: '100%' }}
83
+ formatter={(val: any) =>
84
+ `${val}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
85
+ }
86
+ parser={(val: any) => val!.replace(/,/g, '')}
87
+ onChange={(newVal: number | null) =>
88
+ onPriceChange(pkg.key, record.key, 'price__c', newVal)
89
+ }
90
+ />
91
+ ) : (
92
+ <Text>{formatMoney(v)}</Text>
93
+ ),
94
+ },
95
+ {
96
+ title: '数量',
97
+ dataIndex: 'quantity__c',
98
+ key: 'quantity__c',
99
+ width: 120,
100
+ render: (v: number, record: PackageItemData) =>
101
+ editable ? (
102
+ <InputNumber
103
+ value={v}
104
+ min={1}
105
+ precision={0}
106
+ style={{ width: '100%' }}
107
+ onChange={(newVal: number | null) =>
108
+ onPriceChange(pkg.key, record.key, 'quantity__c', newVal)
109
+ }
110
+ />
111
+ ) : (
112
+ <Text>{v}</Text>
113
+ ),
114
+ },
115
+ {
116
+ title: '小计',
117
+ key: 'subtotal__c',
118
+ width: 140,
119
+ align: 'right' as const,
120
+ render: (_: any, record: PackageItemData) => (
121
+ <Text strong>{formatMoney(record.price__c * record.quantity__c)}</Text>
122
+ ),
123
+ },
124
+ ];
125
+
126
+ return (
127
+ <div className="item-table-wrapper">
128
+ <Table
129
+ columns={columns}
130
+ dataSource={pkg.items}
131
+ rowKey="key"
132
+ pagination={false}
133
+ size="small"
134
+ bordered
135
+ />
136
+ <div className="item-table-footer">
137
+ <Button
138
+ type="dashed"
139
+ size="small"
140
+ icon={<PlusOutlined />}
141
+ onClick={() => onAddItem(pkg)}
142
+ >
143
+ 添加明细
144
+ </Button>
145
+ </div>
146
+ </div>
147
+ );
148
+ }
@@ -0,0 +1,394 @@
1
+ /**
2
+ * @file 招投标标包查看组件
3
+ * @description 标包层级数据网格:展开明细、行内编辑报价、实时汇总、超支预警、批量保存
4
+ */
5
+ import * as React from 'react';
6
+ import { Table, Spin, Empty, message } from 'antd';
7
+ // @ts-ignore
8
+ import { xObject } from 'neo-open-api';
9
+ // @ts-ignore
10
+ import { NeoEvent } from 'neo-ui-common';
11
+ import { BidPackageHeader } from './cmps/BidPackageHeader';
12
+ import { PackageItemTable } from './cmps/PackageItemTable';
13
+ import { getPackageMainColumns } from './mainTableColumns';
14
+ import type { PackageData } from './types';
15
+ import { calcPackageTotal } from './utils';
16
+ import './style.scss';
17
+
18
+ interface BidPackageProps {
19
+ packageApiKey?: string;
20
+ packageItemApiKey?: string;
21
+ bidRelationField?: string;
22
+ packageRelationField?: string;
23
+ title?: string;
24
+ editable?: boolean;
25
+ totalBudget?: number;
26
+ data?: any;
27
+ env?: any;
28
+ className?: string;
29
+ }
30
+
31
+ interface BidPackageState {
32
+ packages: PackageData[];
33
+ loading: boolean;
34
+ saving: boolean;
35
+ expandedKeys: string[];
36
+ currentBidId: string | null;
37
+ currentBidName: string | null;
38
+ currentBidNo: string | null;
39
+ currentStatus: string | null;
40
+ currentBudget: number | null;
41
+ currentPurchaser: string | null;
42
+ currentDeadline: string | null;
43
+ }
44
+
45
+ export default class BidPackage extends React.PureComponent<
46
+ BidPackageProps,
47
+ BidPackageState
48
+ > {
49
+ constructor(props: BidPackageProps) {
50
+ super(props);
51
+ this.state = {
52
+ packages: [],
53
+ loading: false,
54
+ saving: false,
55
+ expandedKeys: [],
56
+ currentBidId: null,
57
+ currentBidName: null,
58
+ currentBidNo: null,
59
+ currentStatus: null,
60
+ currentBudget: null,
61
+ currentPurchaser: null,
62
+ currentDeadline: null,
63
+ };
64
+
65
+ this.handleBidSelected = this.handleBidSelected.bind(this);
66
+ }
67
+
68
+ componentDidMount() {
69
+ this.loadData();
70
+
71
+ try {
72
+ NeoEvent.listen('bidSelected', this.handleBidSelected);
73
+ } catch (e) {
74
+ // NeoEvent 不可用时忽略
75
+ }
76
+ }
77
+
78
+ componentDidUpdate(prev: BidPackageProps) {
79
+ if (
80
+ prev.packageApiKey !== this.props.packageApiKey ||
81
+ prev.packageItemApiKey !== this.props.packageItemApiKey
82
+ ) {
83
+ this.loadData();
84
+ }
85
+ }
86
+
87
+ handleBidSelected(eventData: any) {
88
+ const { bidId, bidName, bidNo, status, totalBudget, purchaser, deadline } =
89
+ eventData || {};
90
+ if (!bidId || bidId === this.state.currentBidId) return;
91
+ this.setState(
92
+ {
93
+ currentBidId: bidId,
94
+ currentBidName: bidName || null,
95
+ currentBidNo: bidNo || null,
96
+ currentStatus: status || null,
97
+ currentBudget: totalBudget ?? null,
98
+ currentPurchaser: purchaser || null,
99
+ currentDeadline: deadline || null,
100
+ },
101
+ () => {
102
+ this.loadData(bidId);
103
+ },
104
+ );
105
+ }
106
+
107
+ @NeoEvent.function
108
+ async loadData(bidId?: string) {
109
+ const {
110
+ packageApiKey,
111
+ packageItemApiKey,
112
+ bidRelationField,
113
+ packageRelationField,
114
+ data,
115
+ } = this.props;
116
+ const targetBidId = bidId || this.state.currentBidId;
117
+
118
+ if (!packageApiKey || !packageItemApiKey) {
119
+ this.setState({
120
+ packages: [],
121
+ loading: false,
122
+ expandedKeys: [],
123
+ });
124
+ return;
125
+ }
126
+
127
+ this.setState({ loading: true });
128
+
129
+ try {
130
+ const recordId = targetBidId || data?.__NeoSystemInfo?.recordId;
131
+
132
+ const pkgQuery: any = {
133
+ xObjectApiKey: packageApiKey,
134
+ fields: ['name', 'limitPrice__c', 'sortOrder__c'],
135
+ pageSize: 100,
136
+ };
137
+ if (recordId && bidRelationField) {
138
+ pkgQuery.where = `${bidRelationField} = '${recordId}'`;
139
+ }
140
+
141
+ const pkgResult = await xObject.query(pkgQuery);
142
+ const pkgRecords = pkgResult?.status ? pkgResult.data || [] : [];
143
+
144
+ if (pkgRecords.length === 0) {
145
+ this.setState({
146
+ packages: [],
147
+ loading: false,
148
+ expandedKeys: [],
149
+ });
150
+ return;
151
+ }
152
+
153
+ const itemQuery: any = {
154
+ xObjectApiKey: packageItemApiKey,
155
+ fields: [
156
+ 'name',
157
+ 'spec__c',
158
+ 'unit__c',
159
+ 'price__c',
160
+ 'quantity__c',
161
+ packageRelationField || 'packageId__c',
162
+ ],
163
+ pageSize: 500,
164
+ };
165
+ if (pkgRecords.length > 0 && packageRelationField) {
166
+ const ids = pkgRecords.map((p: any) => `'${p.id}'`).join(',');
167
+ itemQuery.where = `${packageRelationField} in (${ids})`;
168
+ }
169
+ const itemResult = await xObject.query(itemQuery);
170
+ const itemRecords = itemResult?.status ? itemResult.data || [] : [];
171
+
172
+ const relField = packageRelationField || 'packageId__c';
173
+ const packages: PackageData[] = pkgRecords.map((pkg: any) => ({
174
+ id: pkg.id,
175
+ key: pkg.id,
176
+ name: pkg.name,
177
+ limitPrice: pkg.limitPrice__c || 0,
178
+ sortOrder: pkg.sortOrder__c || 0,
179
+ items: itemRecords
180
+ .filter((it: any) => String(it[relField]) === String(pkg.id))
181
+ .map((it: any) => ({
182
+ id: it.id,
183
+ key: it.id,
184
+ name: it.name,
185
+ spec__c: it.spec__c || '',
186
+ unit__c: it.unit__c || '',
187
+ price__c: it.price__c || 0,
188
+ quantity__c: it.quantity__c || 0,
189
+ isDirty__c: false,
190
+ })),
191
+ }));
192
+
193
+ packages.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
194
+
195
+ this.setState({
196
+ packages,
197
+ loading: false,
198
+ expandedKeys: packages.length > 0 ? [packages[0].key] : [],
199
+ });
200
+ } catch (e) {
201
+ console.error('加载标包数据失败:', e);
202
+ this.setState({
203
+ packages: [],
204
+ loading: false,
205
+ expandedKeys: [],
206
+ });
207
+ }
208
+ }
209
+
210
+ handlePriceChange = (
211
+ pkgKey: string,
212
+ itemKey: string,
213
+ field: 'price__c' | 'quantity__c',
214
+ val: number | null,
215
+ ) => {
216
+ this.setState((prev) => ({
217
+ packages: prev.packages.map((pkg) => {
218
+ if (pkg.key !== pkgKey) return pkg;
219
+ return {
220
+ ...pkg,
221
+ items: pkg.items.map((it) =>
222
+ it.key === itemKey
223
+ ? { ...it, [field]: val || 0, isDirty__c: true }
224
+ : it,
225
+ ),
226
+ };
227
+ }),
228
+ }));
229
+ };
230
+
231
+ handleSave = async () => {
232
+ const { packageItemApiKey } = this.props;
233
+ const { packages } = this.state;
234
+
235
+ const dirtyItems = packages.flatMap((pkg) =>
236
+ pkg.items.filter((it) => it.isDirty__c),
237
+ );
238
+
239
+ if (dirtyItems.length === 0) {
240
+ message.info('没有需要保存的修改');
241
+ return;
242
+ }
243
+
244
+ if (!packageItemApiKey) {
245
+ message.warning('请先配置明细实体 API Key(packageItemApiKey)');
246
+ return;
247
+ }
248
+
249
+ this.setState({ saving: true });
250
+ try {
251
+ for (const item of dirtyItems) {
252
+ await xObject.update(packageItemApiKey, item.id, {
253
+ data: { price__c: item.price__c, quantity__c: item.quantity__c },
254
+ method: 'PATCH',
255
+ });
256
+ }
257
+ message.success(`已保存 ${dirtyItems.length} 条报价修改`);
258
+ this.clearDirty();
259
+ } catch (e) {
260
+ console.error('保存失败:', e);
261
+ message.error('保存失败,请重试');
262
+ } finally {
263
+ this.setState({ saving: false });
264
+ }
265
+ };
266
+
267
+ clearDirty() {
268
+ this.setState((prev) => ({
269
+ packages: prev.packages.map((pkg) => ({
270
+ ...pkg,
271
+ items: pkg.items.map((it) => ({ ...it, isDirty__c: false })),
272
+ })),
273
+ }));
274
+ }
275
+
276
+ handleExpand = (expanded: boolean, record: PackageData) => {
277
+ this.setState((prev) => ({
278
+ expandedKeys: expanded
279
+ ? [...prev.expandedKeys, record.key]
280
+ : prev.expandedKeys.filter((k) => k !== record.key),
281
+ }));
282
+ };
283
+
284
+ handleAddItem = (pkg: PackageData) => {
285
+ const { env, packageItemApiKey, packageRelationField } = this.props;
286
+ const ctx = env?.ctx;
287
+
288
+ if (!packageItemApiKey) {
289
+ message.warning('请先配置明细实体 API Key(packageItemApiKey)');
290
+ return;
291
+ }
292
+
293
+ if (!ctx?.api?.openCreateForm) {
294
+ message.warning('当前环境不支持打开新建表单,请在 NeoCRM 平台中使用');
295
+ return;
296
+ }
297
+
298
+ ctx.api.openCreateForm({
299
+ objectApiKey: packageItemApiKey,
300
+ busiType: 'defaultBusiType',
301
+ defaultFieldVals: packageRelationField
302
+ ? [{ apiKey: packageRelationField, value: pkg.id }]
303
+ : undefined,
304
+ });
305
+ };
306
+
307
+ renderItemTable = (pkg: PackageData) => {
308
+ const { editable = true } = this.props;
309
+ return (
310
+ <PackageItemTable
311
+ pkg={pkg}
312
+ editable={editable}
313
+ onPriceChange={this.handlePriceChange}
314
+ onAddItem={this.handleAddItem}
315
+ />
316
+ );
317
+ };
318
+
319
+ render() {
320
+ const {
321
+ className,
322
+ packageApiKey,
323
+ packageItemApiKey,
324
+ title = '标书在线概算',
325
+ totalBudget: propsBudget,
326
+ } = this.props;
327
+ const {
328
+ packages,
329
+ loading,
330
+ expandedKeys,
331
+ saving,
332
+ currentBidName,
333
+ currentBidNo,
334
+ currentStatus,
335
+ currentBudget,
336
+ currentPurchaser,
337
+ currentDeadline,
338
+ } = this.state;
339
+
340
+ const displayTitle = currentBidName || title;
341
+ const hasEntityKeys = Boolean(packageApiKey && packageItemApiKey);
342
+ const emptyDescription = hasEntityKeys
343
+ ? '暂无标包数据'
344
+ : '请在设计器中配置标包实体(packageApiKey)与明细实体(packageItemApiKey)';
345
+
346
+ return (
347
+ <div className={`bidPackage__c ${className || ''}`}>
348
+ <BidPackageHeader
349
+ displayTitle={displayTitle}
350
+ totalBudget={currentBudget}
351
+ propsBudget={propsBudget}
352
+ packages={packages}
353
+ saving={saving}
354
+ loading={loading}
355
+ currentBidNo={currentBidNo}
356
+ currentStatus={currentStatus}
357
+ currentPurchaser={currentPurchaser}
358
+ currentDeadline={currentDeadline}
359
+ onReload={() => this.loadData()}
360
+ onSave={this.handleSave}
361
+ />
362
+
363
+ <div className="bid-table-wrapper">
364
+ <Spin spinning={loading} tip="加载标包数据...">
365
+ {packages.length === 0 && !loading ? (
366
+ <Empty
367
+ description={emptyDescription}
368
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
369
+ />
370
+ ) : (
371
+ <Table
372
+ key={this.state.currentBidId || 'default'}
373
+ columns={getPackageMainColumns()}
374
+ dataSource={packages}
375
+ rowKey="key"
376
+ pagination={false}
377
+ expandable={{
378
+ expandedRowRender: this.renderItemTable,
379
+ expandedRowKeys: expandedKeys,
380
+ onExpand: this.handleExpand,
381
+ }}
382
+ rowClassName={(record: PackageData) =>
383
+ calcPackageTotal(record.items) > record.limitPrice
384
+ ? 'row-over-budget'
385
+ : ''
386
+ }
387
+ />
388
+ )}
389
+ </Spin>
390
+ </div>
391
+ </div>
392
+ );
393
+ }
394
+ }
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { Tag, Space, Typography } from 'antd';
3
+ import { WarningOutlined } from '@ant-design/icons';
4
+ import type { PackageData } from './types';
5
+ import { calcPackageTotal, formatMoney } from './utils';
6
+
7
+ const { Text } = Typography;
8
+
9
+ export function getPackageMainColumns(): any[] {
10
+ return [
11
+ {
12
+ title: '标包名称',
13
+ dataIndex: 'name',
14
+ key: 'name',
15
+ render: (v: string) => <Text strong>{v}</Text>,
16
+ },
17
+ {
18
+ title: '最高限价',
19
+ dataIndex: 'limitPrice',
20
+ key: 'limitPrice',
21
+ width: 160,
22
+ render: (v: number) => <Text type="secondary">{formatMoney(v)}</Text>,
23
+ },
24
+ {
25
+ title: '实时报价汇总',
26
+ key: 'total',
27
+ width: 240,
28
+ render: (_: any, record: PackageData) => {
29
+ const total = calcPackageTotal(record.items);
30
+ const isOver = total > record.limitPrice;
31
+ return (
32
+ <Space>
33
+ <Text type={isOver ? 'danger' : 'success'} strong>
34
+ {formatMoney(total)}
35
+ </Text>
36
+ {isOver && (
37
+ <Tag
38
+ color="error"
39
+ className="over-budget-tag"
40
+ icon={<WarningOutlined />}
41
+ >
42
+ 超支预警
43
+ </Tag>
44
+ )}
45
+ </Space>
46
+ );
47
+ },
48
+ },
49
+ {
50
+ title: '明细数',
51
+ key: 'count',
52
+ width: 80,
53
+ align: 'center' as const,
54
+ render: (_: any, record: PackageData) => <Tag>{record.items.length}</Tag>,
55
+ },
56
+ ];
57
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @file 招投标标包查看组件 - 模型文件
3
+ */
4
+ export class BidPackageModel {
5
+ label: string = '标包概算';
6
+
7
+ description: string =
8
+ '招投标标包层级数据网格,支持展开明细、行内编辑报价、实时汇总与超支预警';
9
+
10
+ targetPage: string[] = ['all'];
11
+
12
+ targetDevice: string = 'web';
13
+
14
+ enableDuplicate: boolean = true;
15
+
16
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/table.svg';
17
+
18
+ defaultComProps = {
19
+ title: '标书在线概算',
20
+ editable: true,
21
+ packageApiKey: 'PackageEntity__c',
22
+ packageItemApiKey: 'PackageItem__c',
23
+ bidRelationField: 'BidId__c',
24
+ packageRelationField: 'packageId__c',
25
+ };
26
+
27
+ functions = [
28
+ {
29
+ apiKey: 'loadData',
30
+ label: '刷新标包数据',
31
+ helpTextKey: '重新加载标包和明细数据',
32
+ },
33
+ ];
34
+
35
+ propsSchema = [
36
+ {
37
+ type: 'panelInput',
38
+ name: 'title',
39
+ label: '组件标题',
40
+ value: '标书在线概算',
41
+ placeholder: '请输入组件标题',
42
+ },
43
+ {
44
+ type: 'panelInput',
45
+ name: 'packageApiKey',
46
+ label: '标包实体 API Key',
47
+ value: '',
48
+ placeholder: '请输入标包实体的 API Key',
49
+ },
50
+ {
51
+ type: 'panelInput',
52
+ name: 'packageItemApiKey',
53
+ label: '明细实体 API Key',
54
+ value: '',
55
+ placeholder: '请输入标包明细实体的 API Key',
56
+ },
57
+ {
58
+ type: 'panelInput',
59
+ name: 'bidRelationField',
60
+ label: '标包关联标书字段',
61
+ value: 'BidId__c',
62
+ placeholder: '标包中关联标书ID的字段',
63
+ },
64
+ {
65
+ type: 'panelInput',
66
+ name: 'packageRelationField',
67
+ label: '明细关联标包字段',
68
+ value: 'packageId__c',
69
+ placeholder: '明细中关联标包ID的字段',
70
+ },
71
+ {
72
+ type: 'panelNumber',
73
+ name: 'totalBudget',
74
+ label: '预算总额',
75
+ placeholder: '招标方公布的总预算上限(可选)',
76
+ },
77
+ {
78
+ type: 'panelSwitch',
79
+ name: 'editable',
80
+ label: '允许编辑报价',
81
+ defaultChecked: true,
82
+ },
83
+ ];
84
+ }
85
+
86
+ export default BidPackageModel;