customer-info-query.yida.jsx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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. self.handleQueryResponse(result);
  214. }).catch(function (error) {
  215. self.setCustomState({
  216. querying: false,
  217. result: null,
  218. error: error && error.message ? error.message : 'SAP查询失败'
  219. });
  220. self.utils.toast({
  221. title: error && error.message ? error.message : 'SAP查询失败',
  222. type: 'error'
  223. });
  224. self.forceUpdate();
  225. });
  226. } catch (error) {
  227. self.setCustomState({
  228. querying: false,
  229. result: null,
  230. error: error && error.message ? error.message : 'SAP查询初始化失败'
  231. });
  232. self.utils.toast({
  233. title: error && error.message ? error.message : 'SAP查询初始化失败',
  234. type: 'error'
  235. });
  236. self.forceUpdate();
  237. }
  238. }
  239. export function handleQueryResponse(result) {
  240. var response = result && result.Response !== undefined ? result.Response : result;
  241. response = response && response.content !== undefined ? response.content : response;
  242. response = response && response.serviceReturnValue !== undefined ? response.serviceReturnValue : response;
  243. if (typeof response === 'string') {
  244. try {
  245. response = JSON.parse(response);
  246. } catch (e) {
  247. var responseText = response.toLowerCase();
  248. var gatewayMessage = responseText.indexOf('502 bad gateway') >= 0 || responseText.indexOf('503 service unavailable') >= 0 || responseText.indexOf('<html') >= 0 || responseText.indexOf('<head') >= 0 ? 'SAP查询服务暂时不可用,请稍后重试' : 'SAP查询服务返回格式异常';
  249. this.setCustomState({
  250. querying: false,
  251. result: null,
  252. error: gatewayMessage
  253. });
  254. this.utils.toast({
  255. title: gatewayMessage,
  256. type: 'error'
  257. });
  258. this.forceUpdate();
  259. return;
  260. }
  261. }
  262. if (response && response.success === false) {
  263. var responseMessage = response.message || response.errorMsg || 'SAP查询失败';
  264. this.setCustomState({
  265. querying: false,
  266. result: null,
  267. error: responseMessage
  268. });
  269. this.utils.toast({
  270. title: responseMessage,
  271. type: 'error'
  272. });
  273. this.forceUpdate();
  274. return;
  275. }
  276. if (response && response.STATUS && response.STATUS !== 'S') {
  277. var sapMessage = response.MESSAGE || 'SAP查询失败,状态=' + response.STATUS;
  278. this.setCustomState({
  279. querying: false,
  280. result: null,
  281. error: sapMessage
  282. });
  283. this.utils.toast({
  284. title: sapMessage,
  285. type: 'error'
  286. });
  287. this.forceUpdate();
  288. return;
  289. }
  290. var message = response && response.MESSAGE;
  291. var errorItem = response && response.ITEM && response.ITEM.filter(item => item && item.ERRO_MESSAGE)[0];
  292. if (message || errorItem) {
  293. var errorMessage = message || errorItem.ERRO_MESSAGE;
  294. this.setCustomState({
  295. querying: false,
  296. result: null,
  297. error: errorMessage
  298. });
  299. this.utils.toast({
  300. title: errorMessage,
  301. type: 'error'
  302. });
  303. this.forceUpdate();
  304. return;
  305. }
  306. this.setCustomState({
  307. querying: false,
  308. result: response || {}
  309. });
  310. this.forceUpdate();
  311. }
  312. export function createRequestId() {
  313. requestSequence += 1;
  314. if (window.crypto && window.crypto.randomUUID) {
  315. return window.crypto.randomUUID();
  316. }
  317. if (window.crypto && window.crypto.getRandomValues) {
  318. var values = new Uint32Array(4);
  319. window.crypto.getRandomValues(values);
  320. return values[0].toString(16) + '-' + values[1].toString(16) + '-' + values[2].toString(16) + '-' + values[3].toString(16);
  321. }
  322. return Date.now().toString(36) + '-' + requestSequence.toString(36) + '-' + Math.random().toString(36).slice(2);
  323. }
  324. export function formatAmount(value) {
  325. var amount = Number(value || 0);
  326. if (isNaN(amount)) {
  327. return value || '-';
  328. }
  329. return amount.toLocaleString('zh-CN', {
  330. minimumFractionDigits: 2,
  331. maximumFractionDigits: 2
  332. });
  333. }
  334. export function formatDate(value) {
  335. if (!value) {
  336. return value || '-';
  337. }
  338. var text = String(value);
  339. if (text.length === 8) {
  340. return text.slice(0, 4) + '-' + text.slice(4, 6) + '-' + text.slice(6, 8);
  341. }
  342. var matched = text.match(/^(\d{4}-\d{2}-\d{2})/);
  343. return matched ? matched[1] : text;
  344. }
  345. export function formatTime(value) {
  346. if (!value) {
  347. return '-';
  348. }
  349. var matched = String(value).match(/T(\d{2}:\d{2}:\d{2})/);
  350. return matched ? matched[1] : String(value);
  351. }
  352. export function renderBalanceSummary(result) {
  353. 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>;
  354. }
  355. export function renderBalance(state, mobile) {
  356. var result = state.result || {};
  357. var items = result.ITEM || [];
  358. if (!items.length) {
  359. return <div style={styles.empty}>未查询到余额数据</div>;
  360. }
  361. return <div>{this.renderBalanceSummary(result)}<div style={mobile ? styles.balanceTableWrapMobile : styles.tableWrap}><table className="customer-query-table" style={mobile ? styles.balanceTableMobile : 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>;
  362. }
  363. export function renderOrders(state, mobile) {
  364. var items = state.result && state.result.ITEM || [];
  365. if (!items.length) {
  366. return <div style={styles.empty}>未查询到收款解锁订单</div>;
  367. }
  368. if (mobile) {
  369. return <div>{items.map((item, index) => <div key={index} style={styles.orderCard}><div style={styles.orderCardHeader}><strong style={styles.orderCardNumber}>{item.SALES_ORDER || '-'}</strong><span style={item.RELEASE_STATUS === 'S' ? styles.statusSuccess : styles.statusError}>{item.RELEASE_STATUS === 'S' ? '成功' : '失败'}</span></div><div style={styles.orderCardGrid}><div><span>公司代码</span><b>{item.COMPANY_CODE || '-'}</b></div><div><span>销售组织</span><b>{item.SALES_ORG || '-'}</b></div><div><span>订单金额</span><b style={styles.orderCardAmount}>{this.formatAmount(item.SALES_AMOUNT)} {item.CURRENCY || ''}</b></div><div><span>释放日期</span><b>{this.formatDate(item.RELEASE_DATE)}</b></div><div><span>释放时间</span><b>{this.formatTime(item.RELEASE_TIME)}</b></div></div></div>)}</div>;
  370. }
  371. 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></tr></thead><tbody>{items.map((item, index) => <tr key={index}><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>;
  372. }
  373. export function renderJsx() {
  374. var self = this;
  375. var state = self.getCustomState();
  376. var mobile = self.utils.isMobile();
  377. var selected = state.selectedCustomer;
  378. var result = state.result;
  379. return <div style={styles.page}>
  380. <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>
  381. <div style={styles.shell}>
  382. <div style={styles.header}><div><div style={styles.eyebrow}>CUSTOMER SERVICE</div><h1 style={styles.title}>客户信息查询</h1><p style={styles.subtitle}>查询客户余额与收款解锁订单</p></div></div>
  383. <section style={styles.panel}>
  384. <div style={mobile ? styles.queryStack : styles.queryRow}>
  385. <div style={styles.customerField}>
  386. <div style={styles.fieldLabel}>选择客户</div>
  387. <div style={styles.searchWrap}>
  388. <input key={selected ? selected.customerNumber : 'empty'} defaultValue={state.keyword || ''} placeholder="输入客户编码或名称,至少 2 个字符" onChange={e => {
  389. self.updateKeyword(e.target.value);
  390. }} onFocus={e => {
  391. self.setCustomState({
  392. searchVisible: true
  393. });
  394. self.forceUpdate();
  395. }} style={styles.input} />
  396. {selected && <button type="button" onClick={e => {
  397. self.clearCustomer();
  398. }} style={styles.clearButton}>清除</button>}
  399. {state.searchVisible && (state.searching || state.customers && state.customers.length > 0) && <div style={styles.searchMenu}>
  400. {state.searching && <div style={styles.searchHint}>正在搜索客户...</div>}
  401. {!state.searching && state.customers.map(customer => <button type="button" key={customer.customerNumber} onClick={e => {
  402. self.selectCustomer(customer);
  403. }} style={styles.customerOption}><b>{customer.customerNumber}</b><span>{customer.customerName || '未维护名称'}</span></button>)}
  404. </div>}
  405. </div>
  406. </div>
  407. <div style={styles.queryTypeField}>
  408. <div style={styles.fieldLabel}>查询内容</div>
  409. <div style={styles.switchRow}>
  410. <span style={state.queryType === 'balance' ? styles.switchLabelActive : styles.switchLabel}>客户余额</span>
  411. <button type="button" aria-label="切换查询内容" onClick={e => {
  412. self.setQueryType(state.queryType === 'balance' ? 'unlockOrders' : 'balance');
  413. }} style={state.queryType === 'balance' ? styles.switchTrack : styles.switchTrackActive}><span style={state.queryType === 'balance' ? styles.switchThumb : styles.switchThumbActive}></span></button>
  414. <span style={state.queryType === 'unlockOrders' ? styles.switchLabelActive : styles.switchLabel}>收款解锁订单</span>
  415. </div>
  416. </div>
  417. <div style={mobile ? styles.queryActionFieldMobile : styles.queryActionField}>
  418. {!mobile && <div style={styles.fieldLabel}>操作</div>}
  419. <button type="button" disabled={state.querying} onClick={e => {
  420. if (!selected) {
  421. self.setCustomState({
  422. error: '请先选择客户后再查询',
  423. result: null
  424. });
  425. self.forceUpdate();
  426. return;
  427. }
  428. self.queryCustomerInfo();
  429. }} style={state.querying || !selected ? mobile ? styles.queryDisabledMobile : styles.queryDisabled : mobile ? styles.queryButtonMobile : styles.queryButton}>{state.querying ? '查询中...' : '查询'}</button>
  430. </div>
  431. </div>
  432. {!selected && <div style={styles.validationHint}>请选择客户后再查询</div>}
  433. </section>
  434. {state.error && <div style={styles.error}>{state.error}</div>}
  435. {result && <section style={styles.resultPanel}>
  436. <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}><span>客户编码:{result.CUSTOMER_NUMBER || selected && selected.customerNumber || '-'}</span><span>客户名称:{result.CUSTOMER_NAME || selected && selected.customerName || '-'}</span></div></div>
  437. {state.queryType === 'balance' ? self.renderBalance(state, mobile) : self.renderOrders(state, mobile)}
  438. </section>}
  439. {!result && !state.error && <div style={styles.empty}>选择客户和查询内容后,可查看对应的 SAP 信息。</div>}
  440. </div>
  441. <div style={{
  442. display: 'none'
  443. }}>{this.state && this.state.timestamp}</div>
  444. </div>;
  445. }
  446. var styles = {
  447. page: {
  448. minHeight: '100vh',
  449. background: '#F4F6F5',
  450. color: '#17211E',
  451. fontFamily: 'Microsoft YaHei, Arial, sans-serif',
  452. padding: '20px 16px 40px'
  453. },
  454. shell: {
  455. maxWidth: 1120,
  456. margin: '0 auto'
  457. },
  458. header: {
  459. padding: '12px 0 22px',
  460. borderBottom: '1px solid #DCE3DF',
  461. marginBottom: 20,
  462. textAlign: 'center'
  463. },
  464. eyebrow: {
  465. color: '#3B6C55',
  466. fontSize: 11,
  467. fontWeight: 700,
  468. letterSpacing: 1.2,
  469. marginBottom: 6
  470. },
  471. title: {
  472. margin: 0,
  473. fontSize: 28,
  474. lineHeight: 1.2,
  475. letterSpacing: '-0.5px'
  476. },
  477. subtitle: {
  478. margin: '8px 0 0',
  479. color: '#66746E',
  480. fontSize: 14
  481. },
  482. panel: {
  483. background: '#FFFFFF',
  484. border: '1px solid #DCE3DF',
  485. padding: 20,
  486. boxShadow: '0 10px 28px rgba(25,48,38,.06)'
  487. },
  488. fieldLabel: {
  489. color: '#405149',
  490. fontSize: 13,
  491. fontWeight: 700,
  492. margin: '0 0 8px'
  493. },
  494. queryRow: {
  495. display: 'flex',
  496. alignItems: 'flex-end',
  497. gap: 14
  498. },
  499. queryStack: {
  500. display: 'flex',
  501. flexDirection: 'column',
  502. gap: 14
  503. },
  504. customerField: {
  505. flex: 1,
  506. minWidth: 260
  507. },
  508. queryTypeField: {
  509. width: 280
  510. },
  511. queryActionField: {
  512. width: 110
  513. },
  514. queryActionFieldMobile: {
  515. width: '100%'
  516. },
  517. searchWrap: {
  518. position: 'relative'
  519. },
  520. input: {
  521. width: '100%',
  522. boxSizing: 'border-box',
  523. minHeight: 46,
  524. border: '1px solid #B7C5BD',
  525. borderRadius: 4,
  526. padding: '0 72px 0 13px',
  527. fontSize: 15,
  528. outline: 'none',
  529. background: '#fff'
  530. },
  531. clearButton: {
  532. position: 'absolute',
  533. right: 8,
  534. top: 8,
  535. border: 0,
  536. background: '#EEF3F0',
  537. color: '#466253',
  538. minHeight: 30,
  539. borderRadius: 3,
  540. padding: '0 10px',
  541. cursor: 'pointer'
  542. },
  543. searchMenu: {
  544. position: 'absolute',
  545. zIndex: 20,
  546. top: 49,
  547. left: 0,
  548. right: 0,
  549. maxHeight: 280,
  550. overflowY: 'auto',
  551. background: '#fff',
  552. border: '1px solid #B7C5BD',
  553. boxShadow: '0 12px 24px rgba(22,43,34,.14)'
  554. },
  555. searchHint: {
  556. padding: 14,
  557. color: '#66746E',
  558. fontSize: 13
  559. },
  560. customerOption: {
  561. display: 'flex',
  562. width: '100%',
  563. minHeight: 48,
  564. padding: '10px 13px',
  565. gap: 12,
  566. alignItems: 'center',
  567. border: 0,
  568. borderBottom: '1px solid #EDF1EE',
  569. background: '#fff',
  570. textAlign: 'left',
  571. cursor: 'pointer',
  572. color: '#17211E'
  573. },
  574. switchRow: {
  575. display: 'flex',
  576. minHeight: 44,
  577. alignItems: 'center',
  578. justifyContent: 'space-between',
  579. gap: 8
  580. },
  581. switchLabel: {
  582. color: '#89958F',
  583. fontSize: 13,
  584. whiteSpace: 'nowrap'
  585. },
  586. switchLabelActive: {
  587. color: '#24563E',
  588. fontSize: 13,
  589. fontWeight: 700,
  590. whiteSpace: 'nowrap'
  591. },
  592. switchTrack: {
  593. position: 'relative',
  594. width: 46,
  595. height: 24,
  596. flex: '0 0 46px',
  597. padding: 0,
  598. border: 0,
  599. borderRadius: 12,
  600. background: '#24563E',
  601. cursor: 'pointer'
  602. },
  603. switchTrackActive: {
  604. position: 'relative',
  605. width: 46,
  606. height: 24,
  607. flex: '0 0 46px',
  608. padding: 0,
  609. border: 0,
  610. borderRadius: 12,
  611. background: '#D76F2B',
  612. cursor: 'pointer'
  613. },
  614. switchThumb: {
  615. position: 'absolute',
  616. top: 3,
  617. left: 3,
  618. width: 18,
  619. height: 18,
  620. borderRadius: '50%',
  621. background: '#fff',
  622. boxShadow: '0 1px 3px rgba(0,0,0,.22)'
  623. },
  624. switchThumbActive: {
  625. position: 'absolute',
  626. top: 3,
  627. right: 3,
  628. width: 18,
  629. height: 18,
  630. borderRadius: '50%',
  631. background: '#fff',
  632. boxShadow: '0 1px 3px rgba(0,0,0,.22)'
  633. },
  634. queryButton: {
  635. width: '100%',
  636. minHeight: 46,
  637. border: 0,
  638. borderRadius: 4,
  639. background: '#D76F2B',
  640. color: '#fff',
  641. fontSize: 15,
  642. fontWeight: 700,
  643. cursor: 'pointer'
  644. },
  645. queryDisabled: {
  646. width: '100%',
  647. minHeight: 46,
  648. border: 0,
  649. borderRadius: 4,
  650. background: '#C8D0CB',
  651. color: '#FFFFFF',
  652. fontSize: 15,
  653. fontWeight: 700,
  654. cursor: 'not-allowed'
  655. },
  656. queryButtonMobile: {
  657. width: '100%',
  658. minHeight: 42,
  659. border: 0,
  660. borderRadius: 4,
  661. background: '#D76F2B',
  662. color: '#fff',
  663. fontSize: 14,
  664. fontWeight: 700,
  665. cursor: 'pointer'
  666. },
  667. queryDisabledMobile: {
  668. width: '100%',
  669. minHeight: 42,
  670. border: 0,
  671. borderRadius: 4,
  672. background: '#C8D0CB',
  673. color: '#FFFFFF',
  674. fontSize: 14,
  675. fontWeight: 700,
  676. cursor: 'not-allowed'
  677. },
  678. validationHint: {
  679. marginTop: 8,
  680. color: '#9B3D15',
  681. fontSize: 12
  682. },
  683. resultPanel: {
  684. background: '#FFFFFF',
  685. border: '1px solid #DCE3DF',
  686. margin: '20px auto 0',
  687. padding: 24,
  688. maxWidth: 1120,
  689. boxSizing: 'border-box'
  690. },
  691. resultHeader: {
  692. display: 'flex',
  693. justifyContent: 'center',
  694. alignItems: 'center',
  695. flexDirection: 'column',
  696. gap: 8,
  697. borderBottom: '1px solid #E7ECE9',
  698. paddingBottom: 16,
  699. marginBottom: 18,
  700. textAlign: 'center'
  701. },
  702. resultTitle: {
  703. margin: 0,
  704. fontSize: 22
  705. },
  706. customerSummary: {
  707. display: 'flex',
  708. justifyContent: 'center',
  709. flexWrap: 'wrap',
  710. gap: '4px 16px',
  711. color: '#5D6B64',
  712. fontSize: 13,
  713. textAlign: 'center'
  714. },
  715. balanceSummary: {
  716. display: 'flex',
  717. justifyContent: 'center',
  718. gap: 40,
  719. marginBottom: 18,
  720. padding: '14px 18px',
  721. background: '#F6F9F7',
  722. border: '1px solid #DDE6E0',
  723. color: '#405149',
  724. textAlign: 'center'
  725. },
  726. summaryLabel: {
  727. display: 'block',
  728. marginBottom: 5,
  729. color: '#718078',
  730. fontSize: 12,
  731. fontWeight: 400
  732. },
  733. summaryAmount: {
  734. color: '#1D6141',
  735. fontVariantNumeric: 'tabular-nums'
  736. },
  737. empty: {
  738. padding: '36px 18px',
  739. marginTop: 20,
  740. textAlign: 'center',
  741. color: '#7A8881',
  742. background: '#FFFFFF',
  743. border: '1px dashed #C9D5CE',
  744. fontSize: 14
  745. },
  746. error: {
  747. marginTop: 20,
  748. padding: '12px 14px',
  749. background: '#FFF1EB',
  750. border: '1px solid #F1B895',
  751. color: '#9B3D15',
  752. fontSize: 14
  753. },
  754. tableWrap: {
  755. overflowX: 'auto',
  756. border: '1px solid #DDE6E0',
  757. borderRadius: 4,
  758. background: '#FFFFFF'
  759. },
  760. table: {
  761. width: '100%',
  762. borderCollapse: 'collapse',
  763. fontSize: 13,
  764. minWidth: 620,
  765. textAlign: 'center',
  766. lineHeight: 1.35
  767. },
  768. balanceTableWrapMobile: {
  769. overflowX: 'auto',
  770. border: '1px solid #DDE6E0',
  771. borderRadius: 4,
  772. background: '#FFFFFF'
  773. },
  774. balanceTableMobile: {
  775. width: '100%',
  776. borderCollapse: 'collapse',
  777. fontSize: 12,
  778. minWidth: 300,
  779. textAlign: 'center',
  780. lineHeight: 1.2
  781. },
  782. amountCell: {
  783. textAlign: 'right',
  784. fontVariantNumeric: 'tabular-nums'
  785. },
  786. amountHighlight: {
  787. textAlign: 'right',
  788. color: '#1D6141',
  789. fontWeight: 700,
  790. fontVariantNumeric: 'tabular-nums'
  791. },
  792. nameCell: {
  793. textAlign: 'left',
  794. minWidth: 150
  795. },
  796. orderCell: {
  797. fontFamily: 'Consolas, monospace',
  798. fontWeight: 700
  799. },
  800. orderCard: {
  801. marginBottom: 10,
  802. padding: '13px 14px',
  803. border: '1px solid #DDE6E0',
  804. borderRadius: 4,
  805. background: '#FFFFFF'
  806. },
  807. orderCardHeader: {
  808. display: 'flex',
  809. alignItems: 'center',
  810. justifyContent: 'space-between',
  811. paddingBottom: 10,
  812. marginBottom: 10,
  813. borderBottom: '1px solid #E7ECE9'
  814. },
  815. orderCardNumber: {
  816. fontFamily: 'Consolas, monospace',
  817. color: '#17211E',
  818. fontSize: 15
  819. },
  820. orderCardGrid: {
  821. display: 'grid',
  822. gridTemplateColumns: '1fr 1fr',
  823. gap: '10px 14px',
  824. color: '#64726B',
  825. fontSize: 12
  826. },
  827. orderCardAmount: {
  828. color: '#1D6141',
  829. fontVariantNumeric: 'tabular-nums'
  830. },
  831. mobileCard: {
  832. border: '1px solid #DFE7E2',
  833. padding: 15,
  834. marginBottom: 10,
  835. background: '#FBFCFB'
  836. },
  837. cardTop: {
  838. display: 'flex',
  839. justifyContent: 'space-between',
  840. alignItems: 'center',
  841. marginBottom: 14
  842. },
  843. currency: {
  844. color: '#5F6E66',
  845. fontSize: 12
  846. },
  847. metric: {
  848. display: 'flex',
  849. justifyContent: 'space-between',
  850. gap: 12,
  851. padding: '8px 0',
  852. borderTop: '1px solid #E7ECE9',
  853. color: '#64726B',
  854. fontSize: 13
  855. },
  856. highlight: {
  857. color: '#1D6141',
  858. fontWeight: 700
  859. },
  860. detailGrid: {
  861. display: 'grid',
  862. gridTemplateColumns: '1fr 1fr',
  863. gap: '8px 12px',
  864. fontSize: 13,
  865. color: '#64726B'
  866. },
  867. statusSuccess: {
  868. display: 'inline-block',
  869. padding: '3px 8px',
  870. borderRadius: 12,
  871. color: '#1D6141',
  872. background: '#E4F1E8',
  873. fontSize: 12,
  874. fontWeight: 700
  875. },
  876. statusError: {
  877. display: 'inline-block',
  878. padding: '3px 8px',
  879. borderRadius: 12,
  880. color: '#A83B27',
  881. background: '#FCE9E4',
  882. fontSize: 12,
  883. fontWeight: 700
  884. }
  885. };
  886. export function getCustomState(key) {
  887. if (typeof _customState === "undefined") {
  888. return key ? undefined : {};
  889. }
  890. if (key) {
  891. return _customState[key];
  892. }
  893. return Object.assign({}, _customState);
  894. }
  895. export function setCustomState(newState) {
  896. if (typeof _customState === "undefined") {
  897. return;
  898. }
  899. Object.keys(newState || {}).forEach(function(key) {
  900. _customState[key] = newState[key];
  901. });
  902. this.forceUpdate();
  903. }
  904. export function forceUpdate() {
  905. this.setState({ timestamp: new Date().getTime() });
  906. }