customer-info-query.yida.jsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. var _customState = {};
  2. var APP_TYPE = 'APP_N9NPHVTQLPBPO8MR6WFG';
  3. var CUSTOMER_FORM_UUID = 'FORM-NO966791YOK2XKW9DMSEGD5L1IN73UHW3ME6LR1';
  4. var STORAGE_KEY = 'tyson.customer-info-query.selected-customer';
  5. var requestSequence = 0;
  6. var FIELDS = {
  7. customerNumber: 'textField_l6em70k2',
  8. customerName: 'textField_l6em70k4'
  9. };
  10. export function didMount() {
  11. var cached = null;
  12. try {
  13. cached = JSON.parse(window.localStorage.getItem(STORAGE_KEY) || 'null');
  14. } catch (e) {
  15. cached = null;
  16. }
  17. this.setCustomState({
  18. keyword: cached && cached.customerNumber ? cached.customerNumber + ' - ' + (cached.customerName || '') : '',
  19. searching: false,
  20. customers: [],
  21. selectedCustomer: cached && cached.customerNumber ? cached : null,
  22. queryType: 'balance',
  23. querying: false,
  24. result: null,
  25. error: '',
  26. searchVisible: false
  27. });
  28. }
  29. export function didUnmount() {
  30. if (this._customerSearchTimer) {
  31. clearTimeout(this._customerSearchTimer);
  32. }
  33. }
  34. export function setQueryType(queryType) {
  35. this.setCustomState({
  36. queryType: queryType,
  37. result: null,
  38. error: ''
  39. });
  40. this.forceUpdate();
  41. }
  42. export function updateKeyword(value) {
  43. var self = this;
  44. var keyword = value || '';
  45. self.setCustomState({
  46. keyword: keyword,
  47. searchVisible: true,
  48. error: ''
  49. });
  50. self.forceUpdate();
  51. if (self._customerSearchTimer) {
  52. clearTimeout(self._customerSearchTimer);
  53. }
  54. if (keyword.trim().length < 2) {
  55. self.setCustomState({
  56. customers: [],
  57. searching: false
  58. });
  59. self.forceUpdate();
  60. return;
  61. }
  62. self._customerSearchTimer = setTimeout(function () {
  63. self.searchCustomers(keyword.trim());
  64. }, 300);
  65. }
  66. export function searchCustomers(keyword) {
  67. var self = this;
  68. var base = {
  69. formUuid: CUSTOMER_FORM_UUID,
  70. appType: APP_TYPE,
  71. currentPage: 1,
  72. pageSize: 20
  73. };
  74. var codeCondition = [{
  75. key: FIELDS.customerNumber,
  76. value: keyword,
  77. type: 'TEXT',
  78. operator: 'contains',
  79. componentName: 'TextField'
  80. }];
  81. var nameCondition = [{
  82. key: FIELDS.customerName,
  83. value: keyword,
  84. type: 'TEXT',
  85. operator: 'contains',
  86. componentName: 'TextField'
  87. }];
  88. self.setCustomState({
  89. searching: true
  90. });
  91. self.forceUpdate();
  92. var codeParams = {};
  93. var nameParams = {};
  94. Object.keys(base).forEach(key => {
  95. codeParams[key] = base[key];
  96. nameParams[key] = base[key];
  97. });
  98. codeParams.searchFieldJson = JSON.stringify(codeCondition);
  99. nameParams.searchFieldJson = JSON.stringify(nameCondition);
  100. return Promise.all([self.utils.yida.searchFormDatas(codeParams), self.utils.yida.searchFormDatas(nameParams)]).then(function (responses) {
  101. var seen = {};
  102. var customers = [];
  103. responses.forEach(response => {
  104. var rows = response && response.data || response && response.content && response.content.data || [];
  105. rows.forEach(row => {
  106. var formData = row.formData || {};
  107. var customerNumber = formData[FIELDS.customerNumber];
  108. if (customerNumber && !seen[customerNumber]) {
  109. seen[customerNumber] = true;
  110. customers.push({
  111. customerNumber: customerNumber,
  112. customerName: formData[FIELDS.customerName] || ''
  113. });
  114. }
  115. });
  116. });
  117. self.setCustomState({
  118. customers: customers,
  119. searching: false
  120. });
  121. self.forceUpdate();
  122. }).catch(function (error) {
  123. self.setCustomState({
  124. searching: false,
  125. customers: []
  126. });
  127. self.utils.toast({
  128. title: error && error.message ? error.message : '客户查询失败',
  129. type: 'error'
  130. });
  131. self.forceUpdate();
  132. });
  133. }
  134. export function selectCustomer(customer) {
  135. try {
  136. window.localStorage.setItem(STORAGE_KEY, JSON.stringify(customer));
  137. } catch (e) {
  138. // The query remains usable when browser storage is unavailable.
  139. }
  140. this.setCustomState({
  141. selectedCustomer: customer,
  142. keyword: customer.customerNumber + ' - ' + customer.customerName,
  143. searchVisible: false,
  144. customers: [],
  145. result: null,
  146. error: ''
  147. });
  148. this.forceUpdate();
  149. }
  150. export function clearCustomer() {
  151. try {
  152. window.localStorage.removeItem(STORAGE_KEY);
  153. } catch (e) {
  154. // Ignore storage cleanup errors.
  155. }
  156. this.setCustomState({
  157. keyword: '',
  158. selectedCustomer: null,
  159. customers: [],
  160. result: null,
  161. error: ''
  162. });
  163. this.forceUpdate();
  164. }
  165. export function queryCustomerInfo() {
  166. var self = this;
  167. var state = self.getCustomState();
  168. var selected = state.selectedCustomer;
  169. if (!selected) {
  170. self.utils.toast({
  171. title: '请先选择客户',
  172. type: 'warning'
  173. });
  174. return;
  175. }
  176. var dataSourceName = state.queryType === 'balance' ? 'customerBalance' : 'customerUnlockOrders';
  177. var dataSource = self.dataSourceMap && self.dataSourceMap[dataSourceName];
  178. if (!dataSource || !dataSource.load) {
  179. var availableDataSources = self.dataSourceMap ? Object.keys(self.dataSourceMap).join(', ') : '未初始化';
  180. var message = '查询数据源不可用:' + dataSourceName + ';当前数据源:' + availableDataSources;
  181. self.setCustomState({
  182. error: message,
  183. result: null
  184. });
  185. self.utils.toast({
  186. title: message,
  187. type: 'error'
  188. });
  189. self.forceUpdate();
  190. return;
  191. }
  192. self.setCustomState({
  193. querying: true,
  194. error: '',
  195. result: null
  196. });
  197. self.forceUpdate();
  198. var request = {
  199. inputs: JSON.stringify({
  200. Headers: {
  201. 'Content-Type': 'application/json'
  202. },
  203. Query: {},
  204. Body: {
  205. requestID: self.createRequestId(),
  206. CUSTOMER_NUMBER: selected.customerNumber,
  207. CUSTOMER_NAME: selected.customerName
  208. }
  209. })
  210. };
  211. try {
  212. return Promise.resolve(dataSource.load(request)).then(function (result) {
  213. var response = result && result.Response !== undefined ? result.Response : result;
  214. response = response && response.content !== undefined ? response.content : response;
  215. response = response && response.serviceReturnValue !== undefined ? response.serviceReturnValue : response;
  216. if (typeof response === 'string') {
  217. try {
  218. response = JSON.parse(response);
  219. } catch (e) {
  220. // Preserve the original value so the query can still recover gracefully.
  221. }
  222. }
  223. self.setCustomState({
  224. querying: false,
  225. result: response || {}
  226. });
  227. self.forceUpdate();
  228. }).catch(function (error) {
  229. self.setCustomState({
  230. querying: false,
  231. result: null,
  232. error: error && error.message ? error.message : 'SAP查询失败'
  233. });
  234. self.utils.toast({
  235. title: error && error.message ? error.message : 'SAP查询失败',
  236. type: 'error'
  237. });
  238. self.forceUpdate();
  239. });
  240. } catch (error) {
  241. self.setCustomState({
  242. querying: false,
  243. result: null,
  244. error: error && error.message ? error.message : 'SAP查询初始化失败'
  245. });
  246. self.utils.toast({
  247. title: error && error.message ? error.message : 'SAP查询初始化失败',
  248. type: 'error'
  249. });
  250. self.forceUpdate();
  251. }
  252. }
  253. export function createRequestId() {
  254. requestSequence += 1;
  255. if (window.crypto && window.crypto.randomUUID) {
  256. return window.crypto.randomUUID();
  257. }
  258. if (window.crypto && window.crypto.getRandomValues) {
  259. var values = new Uint32Array(4);
  260. window.crypto.getRandomValues(values);
  261. return values[0].toString(16) + '-' + values[1].toString(16) + '-' + values[2].toString(16) + '-' + values[3].toString(16);
  262. }
  263. return Date.now().toString(36) + '-' + requestSequence.toString(36) + '-' + Math.random().toString(36).slice(2);
  264. }
  265. export function formatAmount(value) {
  266. var amount = Number(value || 0);
  267. if (isNaN(amount)) {
  268. return value || '-';
  269. }
  270. return amount.toLocaleString('zh-CN', {
  271. minimumFractionDigits: 2,
  272. maximumFractionDigits: 2
  273. });
  274. }
  275. export function formatDate(value) {
  276. if (!value) {
  277. return value || '-';
  278. }
  279. var text = String(value);
  280. if (text.length === 8) {
  281. return text.slice(0, 4) + '-' + text.slice(4, 6) + '-' + text.slice(6, 8);
  282. }
  283. var matched = text.match(/^(\d{4}-\d{2}-\d{2})/);
  284. return matched ? matched[1] : text;
  285. }
  286. export function formatTime(value) {
  287. if (!value) {
  288. return '-';
  289. }
  290. var matched = String(value).match(/T(\d{2}:\d{2}:\d{2})/);
  291. return matched ? matched[1] : String(value);
  292. }
  293. export function renderBalanceSummary(result) {
  294. return <div style={styles.balanceSummary}><div><span style={styles.summaryLabel}>客户组</span><strong>{result.CUSTOMER_GROUP || '-'}</strong></div><div><span style={styles.summaryLabel}>客户组描述</span><strong>{result.CUSTOMER_GROUP_DESC || '-'}</strong></div><div><span style={styles.summaryLabel}>总风险金额</span><strong style={styles.summaryAmount}>{this.formatAmount(result.TOTAL_RISK_AMT)}</strong></div></div>;
  295. }
  296. export function renderBalance(state, mobile) {
  297. var result = state.result || {};
  298. var items = result.ITEM || [];
  299. if (!items.length) {
  300. return <div style={styles.empty}>未查询到余额数据</div>;
  301. }
  302. return <div>{this.renderBalanceSummary(result)}<div style={styles.tableWrap}><table className="customer-query-table" style={styles.table}><thead><tr><th>公司代码</th><th>币种</th><th>未结金额</th></tr></thead><tbody>{items.map((item, index) => <tr key={index}><td>{item.COMPANY_CODE || '-'}</td><td>{item.CURRENCY || '-'}</td><td style={styles.amountHighlight}>{this.formatAmount(item.OUTSTAND_AMT)}</td></tr>)}</tbody></table></div></div>;
  303. }
  304. export function renderOrders(state, mobile) {
  305. var items = state.result && state.result.ITEM || [];
  306. if (!items.length) {
  307. return <div style={styles.empty}>未查询到收款解锁订单</div>;
  308. }
  309. return <div style={styles.tableWrap}><table className="customer-query-table" style={styles.table}><thead><tr><th>客户编码</th><th>客户名称</th><th>公司代码</th><th>销售订单号</th><th>销售组织</th><th>订单金额</th><th>币种</th><th>释放日期</th><th>释放时间</th><th>状态</th></tr></thead><tbody>{items.map((item, index) => <tr key={index}><td>{item.CUSTOMER_NUMBER || '-'}</td><td style={styles.nameCell}>{item.CUSTOMER_NAME || '-'}</td><td>{item.COMPANY_CODE || '-'}</td><td style={styles.orderCell}>{item.SALES_ORDER || '-'}</td><td>{item.SALES_ORG || '-'}</td><td style={styles.amountCell}>{this.formatAmount(item.SALES_AMOUNT)}</td><td>{item.CURRENCY || '-'}</td><td>{this.formatDate(item.RELEASE_DATE)}</td><td>{this.formatTime(item.RELEASE_TIME)}</td><td><span style={item.RELEASE_STATUS === 'S' ? styles.statusSuccess : styles.statusError}>{item.RELEASE_STATUS === 'S' ? '成功' : '失败'}</span></td></tr>)}</tbody></table></div>;
  310. }
  311. export function renderJsx() {
  312. var self = this;
  313. var state = self.getCustomState();
  314. var mobile = self.utils.isMobile();
  315. var selected = state.selectedCustomer;
  316. var result = state.result;
  317. return <div style={styles.page}>
  318. <style>{'.customer-query-table th{background:#EEF4F0;color:#405149;font-weight:700;white-space:nowrap;padding:10px 12px;border-bottom:1px solid #D7E1DB}.customer-query-table td{padding:9px 12px;border-bottom:1px solid #E7ECE9;white-space:nowrap}.customer-query-table tbody tr:last-child td{border-bottom:0}.customer-query-table tbody tr:nth-child(even){background:#FAFCFB}.customer-query-table tbody tr:hover{background:#F1F6F3}'}</style>
  319. <div style={styles.shell}>
  320. <div style={styles.header}><div><div style={styles.eyebrow}>CUSTOMER SERVICE</div><h1 style={styles.title}>客户信息查询</h1><p style={styles.subtitle}>查询客户余额与收款解锁订单</p></div></div>
  321. <section style={styles.panel}>
  322. <div style={mobile ? styles.queryStack : styles.queryRow}>
  323. <div style={styles.customerField}>
  324. <div style={styles.fieldLabel}>选择客户</div>
  325. <div style={styles.searchWrap}>
  326. <input key={selected ? selected.customerNumber : 'empty'} defaultValue={state.keyword || ''} placeholder="输入客户编码或名称,至少 2 个字符" onChange={e => {
  327. self.updateKeyword(e.target.value);
  328. }} onFocus={e => {
  329. self.setCustomState({
  330. searchVisible: true
  331. });
  332. self.forceUpdate();
  333. }} style={styles.input} />
  334. {selected && <button type="button" onClick={e => {
  335. self.clearCustomer();
  336. }} style={styles.clearButton}>清除</button>}
  337. {state.searchVisible && (state.searching || state.customers && state.customers.length > 0) && <div style={styles.searchMenu}>
  338. {state.searching && <div style={styles.searchHint}>正在搜索客户...</div>}
  339. {!state.searching && state.customers.map(customer => <button type="button" key={customer.customerNumber} onClick={e => {
  340. self.selectCustomer(customer);
  341. }} style={styles.customerOption}><b>{customer.customerNumber}</b><span>{customer.customerName || '未维护名称'}</span></button>)}
  342. </div>}
  343. </div>
  344. </div>
  345. <div style={styles.queryTypeField}>
  346. <div style={styles.fieldLabel}>查询内容</div>
  347. <div style={styles.typeRow}>
  348. <button type="button" onClick={e => {
  349. self.setQueryType('balance');
  350. }} style={state.queryType === 'balance' ? styles.typeActive : styles.typeButton}>客户余额</button>
  351. <button type="button" onClick={e => {
  352. self.setQueryType('unlockOrders');
  353. }} style={state.queryType === 'unlockOrders' ? styles.typeActive : styles.typeButton}>收款解锁订单</button>
  354. </div>
  355. </div>
  356. <div style={styles.queryActionField}>
  357. <div style={styles.fieldLabel}>操作</div>
  358. <button type="button" disabled={state.querying} onClick={e => {
  359. if (!selected) {
  360. self.setCustomState({
  361. error: '请先选择客户后再查询',
  362. result: null
  363. });
  364. self.forceUpdate();
  365. return;
  366. }
  367. self.queryCustomerInfo();
  368. }} style={state.querying || !selected ? styles.queryDisabled : styles.queryButton}>{state.querying ? '查询中...' : '查询'}</button>
  369. </div>
  370. </div>
  371. {!selected && <div style={styles.validationHint}>请选择客户后再查询</div>}
  372. </section>
  373. {state.error && <div style={styles.error}>{state.error}</div>}
  374. {result && <section style={styles.resultPanel}>
  375. <div style={styles.resultHeader}><div><div style={styles.eyebrow}>{state.queryType === 'balance' ? 'BALANCE' : 'UNLOCKED ORDERS'}</div><h2 style={styles.resultTitle}>{state.queryType === 'balance' ? '客户余额' : '收款解锁订单'}</h2></div><div style={styles.customerSummary}>{(result.CUSTOMER_NUMBER || selected && selected.customerNumber || '-') + ' ' + (result.CUSTOMER_NAME || selected && selected.customerName || '')}</div></div>
  376. {state.queryType === 'balance' ? self.renderBalance(state, mobile) : self.renderOrders(state, mobile)}
  377. </section>}
  378. {!result && !state.error && <div style={styles.empty}>选择客户和查询内容后,可查看对应的 SAP 信息。</div>}
  379. </div>
  380. <div style={{
  381. display: 'none'
  382. }}>{this.state && this.state.timestamp}</div>
  383. </div>;
  384. }
  385. var styles = {
  386. page: {
  387. minHeight: '100vh',
  388. background: '#F4F6F5',
  389. color: '#17211E',
  390. fontFamily: 'Microsoft YaHei, Arial, sans-serif',
  391. padding: '20px 16px 40px'
  392. },
  393. shell: {
  394. maxWidth: 1120,
  395. margin: '0 auto'
  396. },
  397. header: {
  398. padding: '12px 0 22px',
  399. borderBottom: '1px solid #DCE3DF',
  400. marginBottom: 20,
  401. textAlign: 'center'
  402. },
  403. eyebrow: {
  404. color: '#3B6C55',
  405. fontSize: 11,
  406. fontWeight: 700,
  407. letterSpacing: 1.2,
  408. marginBottom: 6
  409. },
  410. title: {
  411. margin: 0,
  412. fontSize: 28,
  413. lineHeight: 1.2,
  414. letterSpacing: '-0.5px'
  415. },
  416. subtitle: {
  417. margin: '8px 0 0',
  418. color: '#66746E',
  419. fontSize: 14
  420. },
  421. panel: {
  422. background: '#FFFFFF',
  423. border: '1px solid #DCE3DF',
  424. padding: 20,
  425. boxShadow: '0 10px 28px rgba(25,48,38,.06)'
  426. },
  427. fieldLabel: {
  428. color: '#405149',
  429. fontSize: 13,
  430. fontWeight: 700,
  431. margin: '0 0 8px'
  432. },
  433. queryRow: {
  434. display: 'flex',
  435. alignItems: 'flex-end',
  436. gap: 14
  437. },
  438. queryStack: {
  439. display: 'flex',
  440. flexDirection: 'column',
  441. gap: 14
  442. },
  443. customerField: {
  444. flex: 1,
  445. minWidth: 260
  446. },
  447. queryTypeField: {
  448. width: 300
  449. },
  450. queryActionField: {
  451. width: 110
  452. },
  453. searchWrap: {
  454. position: 'relative'
  455. },
  456. input: {
  457. width: '100%',
  458. boxSizing: 'border-box',
  459. minHeight: 46,
  460. border: '1px solid #B7C5BD',
  461. borderRadius: 4,
  462. padding: '0 72px 0 13px',
  463. fontSize: 15,
  464. outline: 'none',
  465. background: '#fff'
  466. },
  467. clearButton: {
  468. position: 'absolute',
  469. right: 8,
  470. top: 8,
  471. border: 0,
  472. background: '#EEF3F0',
  473. color: '#466253',
  474. minHeight: 30,
  475. borderRadius: 3,
  476. padding: '0 10px',
  477. cursor: 'pointer'
  478. },
  479. searchMenu: {
  480. position: 'absolute',
  481. zIndex: 20,
  482. top: 49,
  483. left: 0,
  484. right: 0,
  485. maxHeight: 280,
  486. overflowY: 'auto',
  487. background: '#fff',
  488. border: '1px solid #B7C5BD',
  489. boxShadow: '0 12px 24px rgba(22,43,34,.14)'
  490. },
  491. searchHint: {
  492. padding: 14,
  493. color: '#66746E',
  494. fontSize: 13
  495. },
  496. customerOption: {
  497. display: 'flex',
  498. width: '100%',
  499. minHeight: 48,
  500. padding: '10px 13px',
  501. gap: 12,
  502. alignItems: 'center',
  503. border: 0,
  504. borderBottom: '1px solid #EDF1EE',
  505. background: '#fff',
  506. textAlign: 'left',
  507. cursor: 'pointer',
  508. color: '#17211E'
  509. },
  510. typeRow: {
  511. display: 'flex',
  512. gap: 8
  513. },
  514. typeStack: {
  515. display: 'flex',
  516. flexDirection: 'column',
  517. gap: 10
  518. },
  519. typeButton: {
  520. flex: 1,
  521. minHeight: 44,
  522. border: '1px solid #B7C5BD',
  523. borderRadius: 4,
  524. background: '#fff',
  525. color: '#405149',
  526. fontSize: 14,
  527. cursor: 'pointer'
  528. },
  529. typeActive: {
  530. flex: 1,
  531. minHeight: 44,
  532. border: '1px solid #24563E',
  533. borderRadius: 4,
  534. background: '#24563E',
  535. color: '#fff',
  536. fontSize: 14,
  537. fontWeight: 700,
  538. cursor: 'pointer'
  539. },
  540. queryButton: {
  541. width: '100%',
  542. minHeight: 46,
  543. border: 0,
  544. borderRadius: 4,
  545. background: '#D76F2B',
  546. color: '#fff',
  547. fontSize: 15,
  548. fontWeight: 700,
  549. cursor: 'pointer'
  550. },
  551. queryDisabled: {
  552. width: '100%',
  553. minHeight: 46,
  554. border: 0,
  555. borderRadius: 4,
  556. background: '#C8D0CB',
  557. color: '#FFFFFF',
  558. fontSize: 15,
  559. fontWeight: 700,
  560. cursor: 'not-allowed'
  561. },
  562. validationHint: {
  563. marginTop: 8,
  564. color: '#9B3D15',
  565. fontSize: 12
  566. },
  567. resultPanel: {
  568. background: '#FFFFFF',
  569. border: '1px solid #DCE3DF',
  570. margin: '20px auto 0',
  571. padding: 24,
  572. maxWidth: 1120,
  573. boxSizing: 'border-box'
  574. },
  575. resultHeader: {
  576. display: 'flex',
  577. justifyContent: 'center',
  578. alignItems: 'center',
  579. flexDirection: 'column',
  580. gap: 8,
  581. borderBottom: '1px solid #E7ECE9',
  582. paddingBottom: 16,
  583. marginBottom: 18,
  584. textAlign: 'center'
  585. },
  586. resultTitle: {
  587. margin: 0,
  588. fontSize: 22
  589. },
  590. customerSummary: {
  591. color: '#5D6B64',
  592. fontSize: 13,
  593. textAlign: 'center'
  594. },
  595. balanceSummary: {
  596. display: 'flex',
  597. justifyContent: 'center',
  598. gap: 40,
  599. marginBottom: 18,
  600. padding: '14px 18px',
  601. background: '#F6F9F7',
  602. border: '1px solid #DDE6E0',
  603. color: '#405149',
  604. textAlign: 'center'
  605. },
  606. summaryLabel: {
  607. display: 'block',
  608. marginBottom: 5,
  609. color: '#718078',
  610. fontSize: 12,
  611. fontWeight: 400
  612. },
  613. summaryAmount: {
  614. color: '#1D6141',
  615. fontVariantNumeric: 'tabular-nums'
  616. },
  617. empty: {
  618. padding: '36px 18px',
  619. marginTop: 20,
  620. textAlign: 'center',
  621. color: '#7A8881',
  622. background: '#FFFFFF',
  623. border: '1px dashed #C9D5CE',
  624. fontSize: 14
  625. },
  626. error: {
  627. marginTop: 20,
  628. padding: '12px 14px',
  629. background: '#FFF1EB',
  630. border: '1px solid #F1B895',
  631. color: '#9B3D15',
  632. fontSize: 14
  633. },
  634. tableWrap: {
  635. overflowX: 'auto',
  636. border: '1px solid #DDE6E0',
  637. borderRadius: 4,
  638. background: '#FFFFFF'
  639. },
  640. table: {
  641. width: '100%',
  642. borderCollapse: 'collapse',
  643. fontSize: 13,
  644. minWidth: 620,
  645. textAlign: 'center',
  646. lineHeight: 1.35
  647. },
  648. amountCell: {
  649. textAlign: 'right',
  650. fontVariantNumeric: 'tabular-nums'
  651. },
  652. amountHighlight: {
  653. textAlign: 'right',
  654. color: '#1D6141',
  655. fontWeight: 700,
  656. fontVariantNumeric: 'tabular-nums'
  657. },
  658. nameCell: {
  659. textAlign: 'left',
  660. minWidth: 150
  661. },
  662. orderCell: {
  663. fontFamily: 'Consolas, monospace',
  664. fontWeight: 700
  665. },
  666. mobileCard: {
  667. border: '1px solid #DFE7E2',
  668. padding: 15,
  669. marginBottom: 10,
  670. background: '#FBFCFB'
  671. },
  672. cardTop: {
  673. display: 'flex',
  674. justifyContent: 'space-between',
  675. alignItems: 'center',
  676. marginBottom: 14
  677. },
  678. currency: {
  679. color: '#5F6E66',
  680. fontSize: 12
  681. },
  682. metric: {
  683. display: 'flex',
  684. justifyContent: 'space-between',
  685. gap: 12,
  686. padding: '8px 0',
  687. borderTop: '1px solid #E7ECE9',
  688. color: '#64726B',
  689. fontSize: 13
  690. },
  691. highlight: {
  692. color: '#1D6141',
  693. fontWeight: 700
  694. },
  695. detailGrid: {
  696. display: 'grid',
  697. gridTemplateColumns: '1fr 1fr',
  698. gap: '8px 12px',
  699. fontSize: 13,
  700. color: '#64726B'
  701. },
  702. statusSuccess: {
  703. display: 'inline-block',
  704. padding: '3px 8px',
  705. borderRadius: 12,
  706. color: '#1D6141',
  707. background: '#E4F1E8',
  708. fontSize: 12,
  709. fontWeight: 700
  710. },
  711. statusError: {
  712. display: 'inline-block',
  713. padding: '3px 8px',
  714. borderRadius: 12,
  715. color: '#A83B27',
  716. background: '#FCE9E4',
  717. fontSize: 12,
  718. fontWeight: 700
  719. }
  720. };
  721. export function getCustomState(key) {
  722. if (typeof _customState === "undefined") {
  723. return key ? undefined : {};
  724. }
  725. if (key) {
  726. return _customState[key];
  727. }
  728. return Object.assign({}, _customState);
  729. }
  730. export function setCustomState(newState) {
  731. if (typeof _customState === "undefined") {
  732. return;
  733. }
  734. Object.keys(newState || {}).forEach(function(key) {
  735. _customState[key] = newState[key];
  736. });
  737. this.forceUpdate();
  738. }
  739. export function forceUpdate() {
  740. this.setState({ timestamp: new Date().getTime() });
  741. }