kpi-alert-order-child-table.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <template>
  2. <div :class="$style.wrapper">
  3. <div v-if="showBtn" :class="[$style.caption]">
  4. <span class="child-table-title">{{ label }}</span>
  5. <div v-if="!readOnly" :class="$style.header">
  6. <!-- <a-button type="link" @click="add">
  7. <a-icon type="plus-circle" :theme="'filled'" />
  8. 添加
  9. </a-button> -->
  10. <a-button
  11. type="link"
  12. :disabled="selectedRowKeys.length === 0"
  13. @click="remove(selectedRowKeys)"
  14. >
  15. <a-icon type="minus-circle" :theme="'filled'" />
  16. 删除
  17. </a-button>
  18. </div>
  19. </div>
  20. <sd-table
  21. :key="sequenceColumn"
  22. size="middle"
  23. :columns="_columns"
  24. :sortable="false"
  25. :data-source="value"
  26. :row-key="(record, index) => index"
  27. :pagination="flagpage ? pagination : false"
  28. :row-selection="
  29. readOnly || !showBtn
  30. ? null
  31. : {
  32. getCheckboxProps(record) {
  33. return {
  34. props: { disabled: !showSelection(record) },
  35. }
  36. },
  37. selectedRowKeys: selectedRowKeys,
  38. onChange: onSelectChange,
  39. }
  40. "
  41. :custom-row="
  42. (record, index) => {
  43. return {
  44. on: {
  45. click: () => {
  46. edit(index)
  47. },
  48. },
  49. }
  50. }
  51. "
  52. @update:data-source="($event) => $emit('change', $event)"
  53. @change="onChange"
  54. >
  55. <template slot="index" slot-scope="text, record, index">
  56. {{ index + 1 }}
  57. </template>
  58. <template v-for="field in fields" :slot="'sd_' + field.name" slot-scope="text, record, index">
  59. <slot v-bind="{ field, text, record }" :name="field.name">
  60. <sd-attachment
  61. v-if="field.dataType === 'attachment'"
  62. :key="attkeys[index]"
  63. :group-id="JSON.parse(text).value"
  64. read-only
  65. />
  66. <sd-quill-reader v-else-if="field.dataType === 'tinymce'" :value="text" />
  67. <span v-else :key="field.name"> {{ getDisplayVaule(field, text) }} </span>
  68. </slot>
  69. </template>
  70. </sd-table>
  71. <a-modal
  72. v-model="visible"
  73. :title="label || '预警指标反馈'"
  74. destroy-on-close
  75. v-bind="{ width: modalWidth }"
  76. :body-style="modalProps"
  77. :mask-closable="false"
  78. :footer="readOnly ? null : undefined"
  79. @ok="save"
  80. @cancel="attkeys[action] = Math.random()"
  81. >
  82. <sdForm ref="form" :init-values="editRecord" :read-only="readOnly">
  83. <template v-slot="scope">
  84. <!--
  85. @slot 详情表单内容,8.0.4及以后版本支持
  86. @binding {object} model 整个表单的数据,可用于 v-model 绑定
  87. @binding {object} fields 后台返回的字段定义
  88. -->
  89. <slot v-bind="scope" name="form">
  90. <sd-form-item
  91. v-for="field in fields"
  92. :key="field.name"
  93. :hidden="field.dataType === 'id'"
  94. :name="field.name"
  95. />
  96. </slot>
  97. </template>
  98. </sdForm>
  99. </a-modal>
  100. </div>
  101. </template>
  102. <script>
  103. import axios from '@/common/services/axios-instance'
  104. import sdChildTable from '@/common/components/sd-child-table/sd-child-table'
  105. import components from './_import-components/kpi-alert-order-child-table-import'
  106. /**
  107. * 主子表、动态表格字段
  108. * @displayName SdChildTable 主子表
  109. */
  110. export default {
  111. name: 'KpiAlertOrderChildTable',
  112. components: {
  113. ...components,
  114. sdForm: () => import('@/common/components/sd-form'),
  115. },
  116. mixins: [sdChildTable],
  117. model: {
  118. prop: 'value',
  119. event: 'change',
  120. },
  121. props: {
  122. /**
  123. * 字表的值,{key:value}的格式
  124. */
  125. value: {
  126. type: Array,
  127. default: () => [],
  128. },
  129. /**
  130. * 字表的字段属性
  131. */
  132. fields: {
  133. type: Array,
  134. default: () => [],
  135. },
  136. /**
  137. * 是否只读
  138. */
  139. readOnly: {
  140. type: Boolean,
  141. default: false,
  142. },
  143. /**
  144. * 标签名
  145. */
  146. label: {
  147. type: String,
  148. default: '',
  149. },
  150. /**
  151. * 自定义子表列属性,如[{dataIndex:'shuxingmingzi15',width:'120px'}],8.0.4及以后版本支持
  152. */
  153. columns: {
  154. type: Array,
  155. default: () => [],
  156. },
  157. /**
  158. * 某行是否可选
  159. * @since 8.0.11
  160. */
  161. showSelection: {
  162. type: Function,
  163. default: () => true,
  164. },
  165. // 子组件接收函数
  166. handleBeforeAdd: {
  167. type: Function,
  168. },
  169. flagpage: {
  170. type: Boolean,
  171. default: false,
  172. },
  173. pages: {
  174. type: Number,
  175. default: 10,
  176. },
  177. modalWidth: {
  178. type: String,
  179. default: '1200px',
  180. },
  181. /**
  182. * 传给详情 modal 的 props,可以用于设置样式等
  183. */
  184. modalProps: {
  185. type: Object,
  186. default: undefined,
  187. },
  188. // 是否可以编辑行
  189. editRow: {
  190. type: Boolean,
  191. default: false,
  192. },
  193. // 是否显示操作按钮
  194. showBtn: {
  195. type: Boolean,
  196. default: false,
  197. },
  198. },
  199. data() {
  200. return {
  201. editRecord: [], // 待修改或编辑的记录
  202. pagination: {
  203. current: 1,
  204. total: 0,
  205. showTotal: (total) => {
  206. return `总共 ${total} 条`
  207. },
  208. onChange: (page) => {
  209. this.pagination.current = page
  210. this.pageOption.startPosition = this.pageOption.maxResults * (page - 1)
  211. this.cardIndex = null
  212. this.updatedata(page)
  213. },
  214. pageSize: 10,
  215. }, // 分页属性
  216. sorter: null, // 排序属性
  217. defaultValue: [], // 默认数据
  218. }
  219. },
  220. computed: {
  221. _columns() {
  222. const fields = JSON.parse(JSON.stringify(this.fields))
  223. return [
  224. {
  225. title: '序号',
  226. dataIndex: 'index',
  227. width: '50px',
  228. scopedSlots: { customRender: 'index' },
  229. sdHidden: this.sequenceColumn !== true,
  230. },
  231. ...fields
  232. .filter((item) => item.dataType !== 'id')
  233. .map((item) => {
  234. const t = {
  235. dataIndex: item.name,
  236. key: item.name,
  237. title: item.caption,
  238. scopedSlots: { customRender: 'sd_' + item.name },
  239. }
  240. const column = this.columns.find((c) => c.dataIndex === item.name)
  241. if (column) Object.assign(t, column)
  242. return t
  243. }),
  244. ].filter((item) => item.sdHidden !== true)
  245. },
  246. },
  247. mounted() {
  248. // 默认排序,赋值有用
  249. Object.assign(this.defaultValue, this.value)
  250. this.pagination.pageSize = this.pages
  251. // 增加默认排序
  252. const defaultSorters = this.columns
  253. .filter((col) => col.defaultSortOrder)
  254. .map((col) => ({
  255. field: col.dataIndex,
  256. order: col.defaultSortOrder,
  257. }))
  258. // 排序方法
  259. if (defaultSorters.length > 0) {
  260. this.sorter = { ...defaultSorters[0] }
  261. this.sorterFun(defaultSorters[0], true)
  262. }
  263. },
  264. methods: {
  265. // 排序点击之后,重新排序
  266. onChange(pagination, filters, sorter) {
  267. this.sorter = { ...sorter }
  268. this.sorterFun(this.sorter)
  269. },
  270. // 排序方法
  271. sorterFun(sorts, isDefault) {
  272. if (!sorts) {
  273. // 如果没有sorts,说明是父组件调用,则增加默认值
  274. Object.assign(this.defaultValue, this.value)
  275. sorts = this.sorter
  276. }
  277. // 没有排序,则使用默认排序
  278. if (!sorts?.order) {
  279. while (this.value.length > 0) {
  280. this.value.splice(0, 1)
  281. }
  282. this.defaultValue.forEach((item) => {
  283. this.value.push(item)
  284. })
  285. return
  286. }
  287. const sortValue = this.value.sort((a, b) => {
  288. if (sorts.order === 'ascend') {
  289. // 升序
  290. if (a[sorts.field] + '' < b[sorts.field] + '') {
  291. return -1
  292. }
  293. if (a[sorts.field] + '' === b[sorts.field] + '') {
  294. return 0
  295. }
  296. if (a[sorts.field] + '' > b[sorts.field] + '') {
  297. return 1
  298. }
  299. } else {
  300. // 降序
  301. if (a[sorts.field] + '' < b[sorts.field] + '') {
  302. return 1
  303. }
  304. if (a[sorts.field] + '' === b[sorts.field] + '') {
  305. return 0
  306. }
  307. if (a[sorts.field] + '' > b[sorts.field] + '') {
  308. return -1
  309. }
  310. }
  311. })
  312. this.$set(this, 'value', sortValue)
  313. },
  314. add() {
  315. if (this.handleBeforeAdd) {
  316. this.handleBeforeAdd(null, (res) => {
  317. if (!res) {
  318. this.addMethod()
  319. }
  320. })
  321. } else {
  322. this.addMethod()
  323. }
  324. },
  325. addMethod() {
  326. this.editRecord = this.fields
  327. this.action = 'add'
  328. const atts = this.editRecord.filter((item) => item.dataType === 'attachment')
  329. Promise.all(
  330. atts.map(() => {
  331. return axios('api/framework/v1/attachment-extend/attachments-create-group-code')
  332. })
  333. ).then((res) => {
  334. atts.forEach((att, index) => {
  335. att.value = JSON.stringify({
  336. value: res[index].data,
  337. })
  338. })
  339. this.visible = true
  340. })
  341. },
  342. edit(index) {
  343. if (!this.editRow) return
  344. this.editRecord = JSON.parse(JSON.stringify(this.fields))
  345. this.editRecord.forEach((item) => {
  346. // 把值放到字段信息fields里
  347. item.value = this.value[index][item.name]
  348. })
  349. this.visible = true
  350. this.action = index
  351. this.$emit('editchildrow', this.value[index])
  352. },
  353. save() {
  354. if (this.readOnly || !this.visible) {
  355. // 判断visable为false时,modal没有是为了防止快速重复点击确定
  356. this.visible = false
  357. return
  358. }
  359. this.$refs.form.validateFields().then(() => {
  360. const value = {}
  361. this.$refs.form.getBackendValues().forEach((item) => {
  362. value[item.name] = item.value
  363. })
  364. const data = this.value || [] // 兼容初始值是null的情况
  365. if (this.action === 'add') {
  366. // 新增
  367. data.push(value)
  368. }
  369. if (!isNaN(this.action)) {
  370. // 修改
  371. this.attkeys[this.action] = Math.random()
  372. data[this.action] = value
  373. }
  374. // 添加或修改之后,直接排序
  375. Object.assign(this.defaultValue, data)
  376. this.sorterFun(this.sorter)
  377. /**
  378. * 动态表格值变化时触发
  379. * @property {Object} data 子表的值
  380. */
  381. this.$emit('change', [...data])
  382. this.visible = false
  383. })
  384. },
  385. remove(keys) {
  386. // 用的序号作key,从大往小删,防止串了
  387. const removeKeys = keys.sort((a, b) => b - a)
  388. removeKeys.forEach((key) => {
  389. this.value.splice(key, 1)
  390. })
  391. this.selectedRowKeys = []
  392. this.$emit('change', this.value)
  393. },
  394. },
  395. }
  396. </script>
  397. <style module lang="scss">
  398. @use '@/common/design' as *;
  399. @use "@/common/components/sd-web-print.scss" as print;
  400. .header {
  401. position: absolute;
  402. top: 0;
  403. right: 0;
  404. :global .ant-input-search {
  405. width: 200px;
  406. }
  407. :global .ant-btn {
  408. margin: 5px;
  409. }
  410. }
  411. .caption {
  412. // position: relative;
  413. position: absolute;
  414. top: -5px;
  415. width: 100%;
  416. min-height: 40px;
  417. margin: 3px 0;
  418. text-align: center;
  419. }
  420. .wrapper {
  421. :global(.ant-table-tbody) {
  422. .clickable-cell {
  423. color: $primary-color;
  424. cursor: pointer;
  425. &:hover {
  426. color: $primary-5;
  427. }
  428. &:active {
  429. color: $primary-7;
  430. }
  431. }
  432. }
  433. }
  434. // 打印状态下 子表上选择列的按钮
  435. @include print.wrapper-for-printer {
  436. .wrapper {
  437. :global(.ant-table-thead .anticon) {
  438. display: none;
  439. }
  440. }
  441. }
  442. </style>