mixin.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _xeUtils = _interopRequireDefault(require("xe-utils"));
  7. var _ui = require("../../../ui");
  8. var _utils = require("../../../ui/src/utils");
  9. var _dom = require("../../../ui/src/dom");
  10. var _util = require("../../src/util");
  11. var _log = require("../../../ui/src/log");
  12. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  13. const {
  14. getConfig,
  15. validators
  16. } = _ui.VxeUI;
  17. /**
  18. * 校验规则
  19. */
  20. class Rule {
  21. constructor(rule) {
  22. Object.defineProperty(this, "$options", {
  23. enumerable: true,
  24. configurable: true,
  25. writable: true,
  26. value: void 0
  27. });
  28. Object.assign(this, {
  29. $options: rule,
  30. required: rule.required,
  31. min: rule.min,
  32. max: rule.max,
  33. type: rule.type,
  34. pattern: rule.pattern,
  35. validator: rule.validator,
  36. trigger: rule.trigger,
  37. maxWidth: rule.maxWidth
  38. });
  39. }
  40. /**
  41. * 获取校验不通过的消息
  42. * 支持国际化翻译
  43. */
  44. get content() {
  45. return (0, _utils.getFuncText)(this.$options.content || this.$options.message);
  46. }
  47. get message() {
  48. return this.content;
  49. }
  50. }
  51. // 如果存在 pattern,判断正则
  52. function validREValue(pattern, val) {
  53. if (pattern && !(_xeUtils.default.isRegExp(pattern) ? pattern : new RegExp(pattern)).test(val)) {
  54. return false;
  55. }
  56. return true;
  57. }
  58. // 如果存在 max,判断最大值
  59. function validMaxValue(max, num) {
  60. if (!_xeUtils.default.eqNull(max) && num > _xeUtils.default.toNumber(max)) {
  61. return false;
  62. }
  63. return true;
  64. }
  65. // 如果存在 min,判断最小值
  66. function validMinValue(min, num) {
  67. if (!_xeUtils.default.eqNull(min) && num < _xeUtils.default.toNumber(min)) {
  68. return false;
  69. }
  70. return true;
  71. }
  72. function validRuleValue(rule, val, required) {
  73. const {
  74. type,
  75. min,
  76. max,
  77. pattern
  78. } = rule;
  79. const isArrType = type === 'array';
  80. const isNumType = type === 'number';
  81. const isStrType = type === 'string';
  82. const strVal = `${val}`;
  83. if (!validREValue(pattern, strVal)) {
  84. return false;
  85. }
  86. if (isArrType) {
  87. if (!_xeUtils.default.isArray(val)) {
  88. return false;
  89. }
  90. if (required) {
  91. if (!val.length) {
  92. return false;
  93. }
  94. }
  95. if (!validMinValue(min, val.length)) {
  96. return false;
  97. }
  98. if (!validMaxValue(max, val.length)) {
  99. return false;
  100. }
  101. } else if (isNumType) {
  102. const numVal = Number(val);
  103. if (isNaN(numVal)) {
  104. return false;
  105. }
  106. if (!validMinValue(min, numVal)) {
  107. return false;
  108. }
  109. if (!validMaxValue(max, numVal)) {
  110. return false;
  111. }
  112. } else {
  113. if (isStrType) {
  114. if (!_xeUtils.default.isString(val)) {
  115. return false;
  116. }
  117. }
  118. if (required) {
  119. if (!strVal) {
  120. return false;
  121. }
  122. }
  123. if (!validMinValue(min, strVal.length)) {
  124. return false;
  125. }
  126. if (!validMaxValue(max, strVal.length)) {
  127. return false;
  128. }
  129. }
  130. return true;
  131. }
  132. function checkRuleStatus(rule, val) {
  133. const {
  134. required
  135. } = rule;
  136. const isEmptyVal = _xeUtils.default.isArray(val) ? !val.length : (0, _utils.eqEmptyValue)(val);
  137. if (required) {
  138. if (isEmptyVal) {
  139. return false;
  140. }
  141. if (!validRuleValue(rule, val, required)) {
  142. return false;
  143. }
  144. } else {
  145. if (!isEmptyVal) {
  146. if (!validRuleValue(rule, val, required)) {
  147. return false;
  148. }
  149. }
  150. }
  151. return true;
  152. }
  153. var _default = exports.default = {
  154. methods: {
  155. /**
  156. * 完整校验,和 validate 的区别就是会给有效数据中的每一行进行校验
  157. */
  158. _fullValidate(rows, cb) {
  159. if (_xeUtils.default.isFunction(cb)) {
  160. (0, _log.warnLog)('vxe.error.notValidators', ['fullValidate(rows, callback)', 'fullValidate(rows)']);
  161. }
  162. return this.beginValidate(rows, null, cb, true);
  163. },
  164. /**
  165. * 快速校验,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
  166. */
  167. _validate(rows, cb) {
  168. if (_xeUtils.default.isFunction(cb)) {
  169. (0, _log.warnLog)('vxe.error.notValidators', ['validate(rows, callback)', 'validate(rows)']);
  170. }
  171. return this.beginValidate(rows, null, cb);
  172. },
  173. /**
  174. * 完整校验单元格,和 validateField 的区别就是会给有效数据中的每一行进行校验
  175. */
  176. _fullValidateField(rows, fieldOrColumn) {
  177. const colList = (_xeUtils.default.isArray(fieldOrColumn) ? fieldOrColumn : fieldOrColumn ? [fieldOrColumn] : []).map(column => (0, _util.handleFieldOrColumn)(this, column));
  178. if (colList.length) {
  179. return this.beginValidate(rows, colList, null, true);
  180. }
  181. return this.$nextTick();
  182. },
  183. /**
  184. * 快速校验单元格,如果存在记录不通过的记录,则返回不再继续校验(异步校验除外)
  185. */
  186. _validateField(rows, fieldOrColumn) {
  187. const colList = (_xeUtils.default.isArray(fieldOrColumn) ? fieldOrColumn : fieldOrColumn ? [fieldOrColumn] : []).map(column => (0, _util.handleFieldOrColumn)(this, column));
  188. if (colList.length) {
  189. return this.beginValidate(rows, colList, null);
  190. }
  191. return this.$nextTick();
  192. },
  193. /**
  194. * 聚焦到校验通过的单元格并弹出校验错误提示
  195. */
  196. handleValidError(params) {
  197. const $xeTable = this;
  198. const {
  199. validOpts
  200. } = this;
  201. return new Promise(resolve => {
  202. if (validOpts.autoPos === false) {
  203. $xeTable.dispatchEvent('valid-error', params, null);
  204. resolve();
  205. } else {
  206. this.handleEdit(params, {
  207. type: 'valid-error',
  208. trigger: 'call'
  209. }).then(() => {
  210. setTimeout(() => {
  211. resolve(this.showValidTooltip(params));
  212. }, 10);
  213. });
  214. }
  215. });
  216. },
  217. handleErrMsgMode(validErrMaps) {
  218. const {
  219. validOpts
  220. } = this;
  221. if (validOpts.msgMode === 'single') {
  222. const keys = Object.keys(validErrMaps);
  223. const resMaps = {};
  224. if (keys.length) {
  225. const firstKey = keys[0];
  226. resMaps[firstKey] = validErrMaps[firstKey];
  227. }
  228. return resMaps;
  229. }
  230. return validErrMaps;
  231. },
  232. /**
  233. * 对表格数据进行校验
  234. * 如果不指定数据,则默认只校验临时变动的数据,例如新增或修改
  235. * 如果传 true 则校验当前表格数据
  236. * 如果传 row 指定行记录,则只验证传入的行
  237. * 如果传 rows 为多行记录,则只验证传入的行
  238. * 如果只传 callback 否则默认验证整个表格数据
  239. * 返回 Promise 对象,或者使用回调方式
  240. */
  241. beginValidate(rows, cols, cb, isFull) {
  242. const $xeTable = this;
  243. const props = $xeTable;
  244. const reactData = $xeTable;
  245. const internalData = $xeTable;
  246. const validRest = {};
  247. const {
  248. editRules,
  249. treeConfig
  250. } = props;
  251. const {
  252. isRowGroupStatus
  253. } = reactData;
  254. const {
  255. afterFullData,
  256. pendingRowMaps,
  257. removeRowMaps
  258. } = internalData;
  259. const treeOpts = $xeTable.computeTreeOpts;
  260. const aggregateOpts = $xeTable.computeAggregateOpts;
  261. let validList;
  262. if (rows === true) {
  263. validList = afterFullData;
  264. } else if (rows) {
  265. if (_xeUtils.default.isFunction(rows)) {
  266. cb = rows;
  267. } else {
  268. validList = _xeUtils.default.isArray(rows) ? rows : [rows];
  269. }
  270. }
  271. if (!validList) {
  272. validList = this.getInsertRecords().concat(this.getUpdateRecords());
  273. }
  274. const rowValidErrs = [];
  275. this.lastCallTime = Date.now();
  276. this.validRuleErr = false; // 如果为快速校验,当存在某列校验不通过时将终止执行
  277. this.clearValidate();
  278. const validErrMaps = {};
  279. if (editRules) {
  280. const columns = cols && cols.length ? cols : this.getColumns();
  281. const handleVaild = row => {
  282. const rowid = (0, _util.getRowid)($xeTable, row);
  283. // 是否删除
  284. if (removeRowMaps[rowid]) {
  285. return;
  286. }
  287. // 是否标记删除
  288. if (pendingRowMaps[rowid]) {
  289. return;
  290. }
  291. if ($xeTable.isAggregateRecord(row)) {
  292. return;
  293. }
  294. if (isFull || !this.validRuleErr) {
  295. const colVailds = [];
  296. columns.forEach(column => {
  297. const field = _xeUtils.default.isString(column) ? column : column.field;
  298. if ((isFull || !this.validRuleErr) && _xeUtils.default.has(editRules, field)) {
  299. colVailds.push(this.validCellRules('all', row, column).catch(({
  300. rule,
  301. rules
  302. }) => {
  303. const rest = {
  304. rule,
  305. rules,
  306. rowIndex: this.getRowIndex(row),
  307. row,
  308. columnIndex: this.getColumnIndex(column),
  309. column,
  310. field,
  311. $table: this
  312. };
  313. if (!validRest[field]) {
  314. validRest[field] = [];
  315. }
  316. validErrMaps[`${(0, _util.getRowid)(this, row)}:${column.id}`] = {
  317. column,
  318. row,
  319. rule,
  320. content: rule.content
  321. };
  322. validRest[field].push(rest);
  323. if (!isFull) {
  324. this.validRuleErr = true;
  325. return Promise.reject(rest);
  326. }
  327. }));
  328. }
  329. });
  330. rowValidErrs.push(Promise.all(colVailds));
  331. }
  332. };
  333. if (isRowGroupStatus) {
  334. _xeUtils.default.eachTree(validList, handleVaild, {
  335. children: aggregateOpts.mapChildrenField
  336. });
  337. } else if (treeConfig) {
  338. const childrenField = treeOpts.children || treeOpts.childrenField;
  339. _xeUtils.default.eachTree(validList, handleVaild, {
  340. children: childrenField
  341. });
  342. } else {
  343. validList.forEach(handleVaild);
  344. }
  345. return Promise.all(rowValidErrs).then(() => {
  346. const ruleProps = Object.keys(validRest);
  347. this.validErrorMaps = this.handleErrMsgMode(validErrMaps);
  348. return this.$nextTick().then(() => {
  349. if (ruleProps.length) {
  350. return Promise.reject(validRest[ruleProps[0]][0]);
  351. }
  352. if (cb) {
  353. cb();
  354. }
  355. });
  356. }).catch(firstErrParams => {
  357. return new Promise((resolve, reject) => {
  358. const finish = () => {
  359. this.$nextTick(() => {
  360. if (cb) {
  361. cb(validRest);
  362. resolve();
  363. } else {
  364. if (getConfig().validToReject === 'obsolete') {
  365. // 已废弃,校验失败将不会执行catch
  366. reject(validRest);
  367. } else {
  368. resolve(validRest);
  369. }
  370. }
  371. });
  372. };
  373. const posAndFinish = () => {
  374. firstErrParams.cell = this.getCellElement(firstErrParams.row, firstErrParams.column);
  375. (0, _dom.scrollToView)(firstErrParams.cell);
  376. this.handleValidError(firstErrParams).then(finish);
  377. };
  378. /**
  379. * 当校验不通过时
  380. * 将表格滚动到可视区
  381. * 由于提示信息至少需要占一行,定位向上偏移一行
  382. */
  383. if (this.validOpts.autoPos === false) {
  384. finish();
  385. } else {
  386. const row = firstErrParams.row;
  387. const column = firstErrParams.column;
  388. this.scrollToRow(row, column).then(posAndFinish);
  389. }
  390. });
  391. });
  392. } else {
  393. this.validErrorMaps = {};
  394. }
  395. return this.$nextTick().then(() => {
  396. if (cb) {
  397. cb();
  398. }
  399. });
  400. },
  401. hasCellRules(type, row, column) {
  402. const {
  403. editRules
  404. } = this;
  405. const {
  406. property
  407. } = column;
  408. if (property && editRules) {
  409. const rules = _xeUtils.default.get(editRules, property);
  410. return rules && _xeUtils.default.find(rules, rule => type === 'all' || !rule.trigger || type === rule.trigger);
  411. }
  412. return false;
  413. },
  414. /**
  415. * 校验数据
  416. * 按表格行、列顺序依次校验(同步或异步)
  417. * 校验规则根据索引顺序依次校验,如果是异步则会等待校验完成才会继续校验下一列
  418. * 如果校验失败则,触发回调或者Promise<不通过列的错误消息>
  419. * 如果是传回调方式这返回一个校验不通过列的错误消息
  420. *
  421. * rule 配置:
  422. * required=Boolean 是否必填
  423. * min=Number 最小长度
  424. * max=Number 最大长度
  425. * validator=Function({ cellValue, rule, rules, row, column, rowIndex, columnIndex }) 自定义校验,接收一个 Promise
  426. * trigger=blur|change 触发方式(除非特殊场景,否则默认为空就行)
  427. */
  428. validCellRules(validType, row, column, val) {
  429. const {
  430. editRules
  431. } = this;
  432. const {
  433. property
  434. } = column;
  435. const errorRules = [];
  436. const syncValidList = [];
  437. if (property && editRules) {
  438. const rules = _xeUtils.default.get(editRules, property);
  439. if (rules) {
  440. const cellValue = _xeUtils.default.isUndefined(val) ? _xeUtils.default.get(row, property) : val;
  441. rules.forEach(rule => {
  442. const {
  443. trigger,
  444. validator
  445. } = rule;
  446. if (validType === 'all' || !trigger || validType === trigger) {
  447. if (validator) {
  448. const validParams = {
  449. cellValue,
  450. rule,
  451. rules,
  452. row,
  453. rowIndex: this.getRowIndex(row),
  454. column,
  455. columnIndex: this.getColumnIndex(column),
  456. field: column.property,
  457. $table: this
  458. };
  459. let customValid;
  460. if (_xeUtils.default.isString(validator)) {
  461. const gvItem = validators.get(validator);
  462. if (gvItem) {
  463. const tcvMethod = gvItem.tableCellValidatorMethod || gvItem.cellValidatorMethod;
  464. if (tcvMethod) {
  465. customValid = tcvMethod(validParams);
  466. } else {
  467. (0, _log.errLog)('vxe.error.notValidators', [validator]);
  468. }
  469. } else {
  470. (0, _log.errLog)('vxe.error.notValidators', [validator]);
  471. }
  472. } else {
  473. customValid = validator(validParams);
  474. }
  475. if (customValid) {
  476. if (_xeUtils.default.isError(customValid)) {
  477. this.validRuleErr = true;
  478. errorRules.push(new Rule({
  479. type: 'custom',
  480. trigger,
  481. content: customValid.message,
  482. rule: new Rule(rule)
  483. }));
  484. } else if (customValid.catch) {
  485. // 如果为异步校验(注:异步校验是并发无序的)
  486. syncValidList.push(customValid.catch(e => {
  487. this.validRuleErr = true;
  488. errorRules.push(new Rule({
  489. type: 'custom',
  490. trigger,
  491. content: e && e.message ? e.message : rule.content || rule.message,
  492. rule: new Rule(rule)
  493. }));
  494. }));
  495. }
  496. }
  497. } else {
  498. if (!checkRuleStatus(rule, cellValue)) {
  499. this.validRuleErr = true;
  500. errorRules.push(new Rule(rule));
  501. }
  502. }
  503. }
  504. });
  505. }
  506. }
  507. return Promise.all(syncValidList).then(() => {
  508. if (errorRules.length) {
  509. const rest = {
  510. rules: errorRules,
  511. rule: errorRules[0]
  512. };
  513. return Promise.reject(rest);
  514. }
  515. });
  516. },
  517. _clearValidate(rows, fieldOrColumn) {
  518. const {
  519. validOpts,
  520. validErrorMaps
  521. } = this;
  522. const validTip = this.$refs.refValidTooltip;
  523. const rowList = _xeUtils.default.isArray(rows) ? rows : rows ? [rows] : [];
  524. const colList = (_xeUtils.default.isArray(fieldOrColumn) ? fieldOrColumn : fieldOrColumn ? [fieldOrColumn] : []).map(column => (0, _util.handleFieldOrColumn)(this, column));
  525. let validErrMaps = {};
  526. if (validTip && validTip.visible) {
  527. validTip.close();
  528. }
  529. // 如果是单个提示模式
  530. if (validOpts.msgMode === 'single') {
  531. this.validErrorMaps = {};
  532. return this.$nextTick();
  533. }
  534. if (rowList.length && colList.length) {
  535. validErrMaps = Object.assign({}, validErrorMaps);
  536. rowList.forEach(row => {
  537. colList.forEach(column => {
  538. const validKey = `${(0, _util.getRowid)(this, row)}:${column.id}`;
  539. if (validErrMaps[validKey]) {
  540. delete validErrMaps[validKey];
  541. }
  542. });
  543. });
  544. } else if (rowList.length) {
  545. const rowIdList = rowList.map(row => `${(0, _util.getRowid)(this, row)}`);
  546. _xeUtils.default.each(validErrorMaps, (item, key) => {
  547. if (rowIdList.indexOf(key.split(':')[0]) > -1) {
  548. validErrMaps[key] = item;
  549. }
  550. });
  551. } else if (colList.length) {
  552. const colidList = colList.map(column => `${column.id}`);
  553. _xeUtils.default.each(validErrorMaps, (item, key) => {
  554. if (colidList.indexOf(key.split(':')[1]) > -1) {
  555. validErrMaps[key] = item;
  556. }
  557. });
  558. }
  559. this.validErrorMaps = validErrMaps;
  560. return this.$nextTick();
  561. },
  562. /**
  563. * 触发校验
  564. */
  565. triggerValidate(type) {
  566. const {
  567. editConfig,
  568. editStore,
  569. editRules,
  570. editOpts,
  571. validOpts
  572. } = this;
  573. const {
  574. actived
  575. } = editStore;
  576. // 检查清除校验消息
  577. if (editRules && validOpts.msgMode === 'single') {
  578. this.validErrorMaps = {};
  579. }
  580. // 校验单元格
  581. if (editConfig && editRules && actived.row) {
  582. const {
  583. row,
  584. column,
  585. cell
  586. } = actived.args;
  587. if (this.hasCellRules(type, row, column)) {
  588. return this.validCellRules(type, row, column).then(() => {
  589. if (editOpts.mode === 'row') {
  590. this.clearValidate(row, column);
  591. }
  592. }).catch(({
  593. rule
  594. }) => {
  595. // 如果校验不通过与触发方式一致,则聚焦提示错误,否则跳过并不作任何处理
  596. if (!rule.trigger || type === rule.trigger) {
  597. const rest = {
  598. rule,
  599. row,
  600. column,
  601. cell
  602. };
  603. this.showValidTooltip(rest);
  604. return Promise.reject(rest);
  605. }
  606. return Promise.resolve();
  607. });
  608. }
  609. }
  610. return Promise.resolve();
  611. },
  612. /**
  613. * 弹出校验错误提示
  614. */
  615. showValidTooltip(params) {
  616. const $xeTable = this;
  617. const {
  618. $refs,
  619. height,
  620. validStore,
  621. validErrorMaps,
  622. tableData,
  623. validOpts
  624. } = this;
  625. const {
  626. rule,
  627. row,
  628. column,
  629. cell
  630. } = params;
  631. const validTip = $refs.refValidTooltip;
  632. const content = rule.content;
  633. validStore.visible = true;
  634. if (validOpts.msgMode === 'single') {
  635. this.validErrorMaps = {
  636. [`${(0, _util.getRowid)(this, row)}:${column.id}`]: {
  637. column,
  638. row,
  639. rule,
  640. content
  641. }
  642. };
  643. } else {
  644. this.validErrorMaps = Object.assign({}, validErrorMaps, {
  645. [`${(0, _util.getRowid)(this, row)}:${column.id}`]: {
  646. column,
  647. row,
  648. rule,
  649. content
  650. }
  651. });
  652. }
  653. $xeTable.dispatchEvent('valid-error', params, null);
  654. if (validTip) {
  655. if (validTip && (validOpts.message === 'tooltip' || validOpts.message === 'default' && !height && tableData.length < 2)) {
  656. return validTip.open(cell, content);
  657. }
  658. }
  659. return this.$nextTick();
  660. }
  661. }
  662. };