GridBody.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. <template>
  2. <tr v-if="showTabGridTitleFields" height="40px" :style="{ 'background-color': warningColor }" :class="{ 'warning': isSelected }">
  3. <td rowspan="2" class="sticky-col" style="text-align: center;">{{ serialNumber }}</td>
  4. <td rowspan="2" class="text-center sticky-col" style="left:50px">
  5. <input v-model="isSelected" autocomplete="off" type="checkbox" @click="clickModelData(modelData)" />
  6. </td>
  7. <td v-if="isShowEdit == undefined || isShowEdit" rowspan="2" class="sticky-col" style="left:80px">
  8. <template v-if="showEditButton">
  9. <span class="operation-delete" aria-hidden="true" @click="editRecord">
  10. <i class="fa" :class="{ 'fa-pencil': !modelData.editMode, 'fa-check': modelData.editMode }" />
  11. </span>
  12. <span class="operation-edit" aria-hidden="true" @click="deleteRecord">
  13. <i class="fa fa-remove" />
  14. </span>
  15. </template>
  16. </td>
  17. <td :colspan="tabGridFields.length + 1" style="text-align: left; padding-left: 2rem;">
  18. <a-space align="center">
  19. <div v-for="tabGridTitleField in tabGridTitleFields" :key="tabGridTitleField.fieldName" @click="clickRecord">
  20. <a-space align="center">
  21. <span style="font-weight: normal;">{{ Language.getDisplayNameTrl($i18n.locale, tabGridTitleField) }}</span>
  22. <CellTextItem
  23. v-if="!modelData.editMode" :grid-field-item="tabGridTitleField" :model-data="modelData"
  24. :class-name="className" style="display: inline-block; font-weight: normal; padding-top: 3px;"
  25. @execute-callout="executeCallout(tabGridTitleField)"
  26. />
  27. <CellItem
  28. v-else :ref="'cellItem' + index" :scroll-object="scrollObject" :field-item="tabGridTitleField"
  29. :model-data="modelData" :parent-model-data="parentModelData" :class-name="className" :window-no="windowNo"
  30. :tab-index="tabIndex" style="display: inline-block; min-width: 200px;"
  31. :class="{ 'table-cell-searchwidget': fieldUtil.isSearchType(tabGridTitleField) }"
  32. @value-changed="valueChanged($event, tabGridTitleField)"
  33. @execute-callout="executeCallout(tabGridTitleField)"
  34. />
  35. </a-space>
  36. </div>
  37. </a-space>
  38. </td>
  39. </tr>
  40. <tr :style="{ 'background-color': warningColor }" :class="{ 'warning': isSelected }" height="40px">
  41. <template v-if="!showTabGridTitleFields">
  42. <td class="text-center sticky-col">{{ serialNumber }}</td>
  43. <td class="text-center sticky-col" style="left:50px">
  44. <input v-model="isSelected" autocomplete="off" type="checkbox" @click="clickModelData(modelData)" />
  45. </td>
  46. <td v-if="isShowEdit == undefined || isShowEdit" class="sticky-col" style="left:80px">
  47. <template v-if="showEditButton">
  48. <span class="operation-delete" aria-hidden="true" @click="editRecord">
  49. <i class="fa" :class="{ 'fa-pencil': !modelData.editMode, 'fa-check': modelData.editMode }" />
  50. </span>
  51. <span class="operation-edit" aria-hidden="true" @click="deleteRecord">
  52. <i class="fa fa-remove" />
  53. </span>
  54. </template>
  55. </td>
  56. </template>
  57. <template
  58. v-for="(gridFieldItem, index) in tabGridFields"
  59. :key="'TabGridField_' + gridFieldItem.fieldName + '_' + index"
  60. >
  61. <template
  62. v-if="gridFieldItem.groupNames == null || gridFieldItem.groupNames.length == 0 || (nowTab != null && (gridFieldItem.groupNames.indexOf(nowTab) >= 0))"
  63. >
  64. <td v-if="!modelData.editMode" v-show="gridFieldItem.visible" @click="clickRecord">
  65. <div>
  66. <CellTextItem
  67. ref="cellTextItem"
  68. :grid-field-item="gridFieldItem" :model-data="modelData" :class-name="className"
  69. :simple-filter-params="simpleFilterParams" :complex-filter-params="complexFilterParams"
  70. :model-datas="modelDatas" :js-url="jsUrl"
  71. :index="index" @execute-callout="executeCallout(gridFieldItem)" @delete-record="deleteRecord"
  72. @refresh-datas="refreshDatas"
  73. @edit-record="editRecord"
  74. @read-record="readRecord"
  75. />
  76. </div>
  77. </td>
  78. <td
  79. v-else v-show="gridFieldItem.visible"
  80. :class="{ 'table-cell-searchwidget': fieldUtil.isSearchType(gridFieldItem) }" @click="clickRecord"
  81. >
  82. <CellItem
  83. :ref="'cellItem' + index" :scroll-object="scrollObject" :field-item="gridFieldItem"
  84. :model-data="modelData" :parent-model-data="parentModelData" :class-name="className" :window-no="windowNo"
  85. :tab-index="tabIndex" @value-changed="valueChanged($event, gridFieldItem)"
  86. @execute-callout="executeCallout(gridFieldItem)"
  87. />
  88. </td>
  89. </template>
  90. </template>
  91. </tr>
  92. <Modal v-model:show="modal" :full="true">
  93. <ProcessReportResultPreview
  94. v-if="
  95. processReportResult != null &&
  96. (processReportResult.reportResults != null ||
  97. processReportResult.processResult != null)
  98. "
  99. :process-report-result="processReportResult"
  100. :pdf-only="false"
  101. :excel-only="false"
  102. />
  103. <template #header>
  104. {{ $t("lang.tabButton.executeResult") }}
  105. </template>
  106. </Modal>
  107. <Modal
  108. v-model:show="titleModal"
  109. :show-canel-button="false"
  110. :show-ok-button="false"
  111. >
  112. <template #header>
  113. {{ tabButtonModel.tipsTitle }}
  114. </template>
  115. {{ tabButtonModel.tipsContent }}
  116. <template #footer>
  117. <button type="button" class="btn btn-default" @click="titleModalClose">
  118. {{ $t("lang.tabButton.cancel") }}
  119. </button>
  120. <button type="primary" class="btn btn-default" @click="executeProcess">
  121. 确认
  122. </button>
  123. </template>
  124. </Modal>
  125. </template>
  126. <script>
  127. import WindowClientUtil from '../../resource/dictionary/WindowClientUtil.js';
  128. import FieldUtil from '../../resource/dictionary/FieldUtil.js';
  129. import CellItem from './CellItem.vue';
  130. import CellTextItem from './CellTextItem.vue';
  131. import Common from '../../common/Common.js';
  132. import Context from '../common/Context.js';
  133. import JsUtil from '../../common/JsUtil.js';
  134. import Language from '../../common/Language.js';
  135. import { Notify} from 'pc-component-v3';
  136. export default {
  137. components: {
  138. CellItem, CellTextItem,
  139. },
  140. props: {
  141. windowNo: {
  142. type: String,
  143. default: null,
  144. },
  145. tabIndex: {
  146. type: Number,
  147. default: null,
  148. },
  149. /** 表格标题字段 */
  150. tabGridTitleFields: {
  151. type: Array,
  152. default: function () {
  153. return null;
  154. },
  155. },
  156. /** 表格字段 */
  157. tabGridFields: {
  158. type: Array,
  159. default: function () {
  160. return null;
  161. },
  162. },
  163. modelData: {
  164. type: Object,
  165. default: function () {
  166. return null;
  167. },
  168. },
  169. parentModelData: {
  170. type: Object,
  171. default: function () {
  172. return null;
  173. },
  174. },
  175. className: {
  176. type: String,
  177. default: null,
  178. },
  179. isShowEdit: {
  180. type: Boolean,
  181. default: false,
  182. },
  183. scrollObject: {
  184. type: Object,
  185. default: null,
  186. },
  187. serialNumber: {
  188. type: Number,
  189. default: null,
  190. },
  191. jsUrl: {
  192. type: String,
  193. default: null,
  194. },
  195. simpleFilterParams: {
  196. type: String,
  197. default: null,
  198. },
  199. complexFilterParams: {
  200. type: Array,
  201. default: () => {
  202. return [];
  203. },
  204. },
  205. modelDatas: {
  206. type: Array,
  207. default: () => {
  208. return [];
  209. },
  210. },
  211. // 当前的页签
  212. nowTab: {
  213. type: String,
  214. default: '',
  215. },
  216. },
  217. emits: ['deleteRecord', 'editRecord', 'readRecord', 'clickModelData', 'executeCallout', 'valueChanged', 'refreshDatas'],
  218. data: function () {
  219. return {
  220. tabGridFields1: this.tabGridFields,
  221. lastClickTime: 0,
  222. fieldUtil: FieldUtil,
  223. Language: Language,
  224. processReportResult: {},
  225. modal: false,
  226. titleModal: false,
  227. tabButtonModel:{},
  228. };
  229. },
  230. computed: {
  231. modelDataCopy: function () {
  232. return JSON.parse(JSON.stringify(this.modelData));
  233. },
  234. isSelected: {
  235. get: function () {
  236. return this.modelData.select == true;
  237. },
  238. set: function () {
  239. },
  240. },
  241. /**
  242. * 获取报警颜色
  243. */
  244. warningColor: function () {
  245. if (this.modelData.data['warningColor'] != undefined && this.modelData.data['warningColor'].displayValue.length > 0) {
  246. return this.modelData.data['warningColor'].displayValue[0];
  247. } else {
  248. return 'none';
  249. }
  250. },
  251. /**
  252. * 是否显示编辑按钮
  253. */
  254. showEditButton: function () {
  255. if (this.isShowEdit == undefined || this.isShowEdit) {
  256. if (this.modelData != undefined
  257. && this.modelData.data != undefined
  258. && this.modelData.data['documentStatus'] != undefined
  259. && this.modelData.data['documentStatus'].displayValue != undefined
  260. && (this.modelData.data['documentStatus'].displayValue[0] == 'APPROVED'
  261. || this.modelData.data['documentStatus'].displayValue[0] == 'REJECTED'
  262. || this.modelData.data['documentStatus'].displayValue[0] == 'PROCESSING')) {
  263. return false;
  264. }
  265. return true;
  266. }
  267. return false;
  268. },
  269. /**
  270. * 是否显示标题行
  271. */
  272. showTabGridTitleFields: function () {
  273. if (this.tabGridTitleFields == null || this.tabGridTitleFields.length == 0) {
  274. return false;
  275. } else {
  276. return true;
  277. }
  278. },
  279. },
  280. watch: {
  281. 'parentModelData': {
  282. deep: true,
  283. handler(curVal, oldVal) {
  284. var _self = this;
  285. for (let index = 0; index < _self.tabGridFields.length; index++) {
  286. const tabGridField = _self.tabGridFields[index];
  287. let oldTabGridFieldString = JSON.stringify(tabGridField);
  288. const columnShowLogical = tabGridField.columnShowLogical;
  289. if (columnShowLogical != null && columnShowLogical.length > 0) {
  290. let functionName = tabGridField.fieldName.replace('.', '_') + '_showLogical';
  291. let executeFunction = function () {
  292. let parentCtx = new _self.getContext(_self.parentModelData);
  293. try {
  294. tabGridField.visible = _self[functionName](null, parentCtx.modelData);
  295. } catch (e) {
  296. console.error('js代码 %s 执行异常 %o', columnShowLogical, e);
  297. tabGridField.visible = true;
  298. }
  299. };
  300. if (_self[functionName] == null) {
  301. // 执行服务端的脚本
  302. const jsUrl = _self.jsUrl;
  303. if (jsUrl == null || jsUrl == undefined) {
  304. Notify.error('数据字典定义异常', '【' + tabGridField.fieldName + '】列显示逻辑的JS文件不存在,请联系管理员检查数据字典是否JS文件。', false);
  305. return;
  306. }
  307. let promise = JsUtil.dynamicLoadJsFunction(jsUrl, columnShowLogical);
  308. promise.then(dynamicFunction => {
  309. let targetFunction = dynamicFunction;
  310. if (targetFunction == null) {
  311. Notify.error('数据字典定义异常', '【' + tabGridField.fieldName + '】列显示逻辑定义异常,请联系管理员检查数据字典的定义。', false);
  312. return;
  313. }
  314. _self[functionName] = targetFunction;
  315. executeFunction();
  316. }, errorData => {
  317. console.error(errorData);
  318. });
  319. } else {
  320. executeFunction();
  321. }
  322. } else {
  323. tabGridField.visible = true;
  324. }
  325. let newTabGridFieldString = JSON.stringify(tabGridField);
  326. // bug fixed by jack 2022-01-17
  327. // 只有当数据发生改变以后,才重新设置数据,解决当子表数据量很打的时候,输入卡顿的问题。
  328. if (oldTabGridFieldString != newTabGridFieldString) {
  329. _self.tabGridFields[index] = tabGridField;
  330. }
  331. }
  332. console.log(_self.tabGridFields);
  333. },
  334. },
  335. },
  336. methods: {
  337. /**
  338. * 获取Context
  339. */
  340. getContext: Context,
  341. deleteRecord: function () {
  342. this.$emit('deleteRecord');
  343. },
  344. refreshDatas: function(){
  345. this.$emit('refreshDatas');
  346. },
  347. buttonClick: function(){
  348. Notify.error('提示', '暂不支持该组件', false);
  349. },
  350. // 改变表格行元素的编辑状态
  351. editRecord: function (modelData, index) {
  352. this.$emit('editRecord', modelData, index);
  353. },
  354. readRecord: function (isRead, modelData, index) {
  355. this.$emit('readRecord', isRead, modelData, index);
  356. },
  357. clickModelData: function () {
  358. this.$emit('clickModelData');
  359. this.$emit('readRecord', false);
  360. },
  361. // 值发生改变
  362. valueChanged: function (newFieldValue, gridFieldItem) {
  363. var valueObject = {
  364. newFieldValue: newFieldValue,
  365. gridFieldItem: gridFieldItem,
  366. };
  367. this.$emit('valueChanged', valueObject);
  368. this.refreshSelectField(this.modelData, gridFieldItem.fieldName);
  369. },
  370. // 刷新下拉选择框keyValues
  371. refreshSelectField: function (modelData, valueChangedFieldName) {
  372. var _self = this;
  373. if (_self.tabGridFields1) {
  374. _self.tabGridFields1.forEach(function (item) {
  375. if (item.displayType == 'ListBoxEditor') {
  376. var name1 = ':' + valueChangedFieldName;
  377. var showLogical = item.showLogical;
  378. if (showLogical != undefined && showLogical.indexOf(name1) >= 0) {
  379. refresh(item);
  380. }
  381. }
  382. });
  383. }
  384. function refresh(item) {
  385. var obj = {
  386. fieldId: item.id,
  387. modelData: modelData,
  388. };
  389. console.log('GridBody RefreshField');
  390. $.ajax({
  391. url: Common.getApiURL('FieldResource/refreshField'),
  392. async: false,
  393. dataType: 'json',
  394. type: 'post',
  395. data: JSON.stringify(obj),
  396. contentType: 'application/json',
  397. beforeSend: function (request) {
  398. Common.addTokenToRequest(request);
  399. },
  400. success: function (data) {
  401. if (data != undefined) {
  402. item.keyValues = data.keyValues;
  403. }
  404. },
  405. error: function (XMLHttpRequest, textStatus, errorThrown) {
  406. Common.processException(XMLHttpRequest, textStatus, errorThrown);
  407. },
  408. });
  409. }
  410. },
  411. performValidate: function () {
  412. var validateResults = [];
  413. for (var i = 0; i < this.tabGridFields1.length; i++) {
  414. let cellId = 'cellItem' + i;
  415. if (this.$refs[cellId] && this.$refs[cellId][0]) {
  416. var result = this.$refs[cellId][0].performValidate();
  417. validateResults.push(result);
  418. }
  419. }
  420. return validateResults;
  421. },
  422. clickRecord: function () {
  423. // 暂时取消双击打开
  424. var now = new Date().getTime();
  425. // if (now - this.lastClickTime < 500) {
  426. // this.readRecord();
  427. // }
  428. // this.lastClickTime = now;
  429. },
  430. /**
  431. * 执行Callout
  432. */
  433. executeCallout: function (gridFieldItem) {
  434. this.$emit('executeCallout', gridFieldItem);
  435. },
  436. /**
  437. * 获取字符串的哈希值
  438. * @param input
  439. */
  440. getHash: function (input) {
  441. let hash = 0;
  442. if (input.length === 0) {
  443. return hash;
  444. }
  445. for (let i = 0; i < input.length; i++) {
  446. const char = input.charCodeAt(i);
  447. hash = (hash << 5) - hash + char;
  448. hash = hash & hash; // 确保返回值是一个32位有符号整数
  449. }
  450. return Math.abs(hash).toString();
  451. },
  452. },
  453. };
  454. </script>
  455. <style scoped>
  456. .table-cell-text {
  457. overflow: hidden;
  458. text-overflow: ellipsis;
  459. white-space: nowrap;
  460. text-align: center;
  461. }
  462. /** 搜索框,自动提示表格, */
  463. .table-cell-searchwidget {
  464. position: relative;
  465. }
  466. .text-center {
  467. text-align: center;
  468. }
  469. .warning {
  470. background-color: #fcf8e3 !important;
  471. }
  472. .operation-delete {
  473. margin-left: 10px;
  474. margin-right: 5px;
  475. }
  476. .operation-edit {
  477. margin-left: 5px;
  478. margin-right: 10px;
  479. }
  480. /* 固定列 */
  481. .sticky-col {
  482. position: -webkit-sticky; /* Safari */
  483. position: sticky;
  484. left: 0;
  485. background: #fafafa;
  486. z-index: 1; /* 确保固定列在其他内容之上 */
  487. }
  488. </style>