TableColumn.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. <template>
  2. <el-table-column
  3. :prop="itemCol.prop"
  4. :show-overflow-tooltip="itemCol.tooltip"
  5. :align="handleAlign(itemCol)"
  6. :label="itemCol.label"
  7. :fixed="itemCol.fixed ? itemCol.fixed : false"
  8. :width="itemCol.width"
  9. :formatter="itemCol.formatter"
  10. :min-width="itemCol.minWidth"
  11. >
  12. <!-- 单列中嵌套多列数据 -->
  13. <template v-if="itemCol.type === 'innerColumn' && itemCol.children && itemCol.children.length">
  14. <TableColumn
  15. v-for="(innerItem, index) in itemCol.children"
  16. :key="index"
  17. :align="innerItem && innerItem.align"
  18. :itemCol="innerItem">
  19. <!-- 传递插槽 -->
  20. <template v-for="(slot, name) in $scopedSlots" :slot="name" slot-scope="slotScope">
  21. {{slot}}
  22. <slot :name="name" v-bind="slotScope"></slot>
  23. </template>
  24. </TableColumn>
  25. </template>
  26. <!-- 自定义表头 -->
  27. <template slot="header" slot-scope="scope">
  28. <div v-if="itemCol.headerIcon" style="display: flex;align-items: center;justify-content: center;">
  29. {{ itemCol.label }}<i @click="delColumn((scope.$index))" style="font-size: 25px;" :class="itemCol.headerIcon"></i>
  30. </div>
  31. <div v-else style="display: flex;align-items: center;justify-content: center;">
  32. {{ itemCol.label }}
  33. </div>
  34. </template>
  35. <template slot-scope="scope">
  36. <!-- 操作列 -->
  37. <div v-if="itemCol.prop == 'operation'">
  38. <template v-if="!itemCol.type">
  39. <span v-for="oper in itemCol.children" :key="oper.key">
  40. <el-button
  41. v-if="!oper.popover && scope.row[oper.key] != 'hide' && !oper.hide"
  42. class="btn-item"
  43. :class="{ 'danger': oper.key === 'delete'}"
  44. type="text"
  45. size="small"
  46. @click="operClick(scope, oper.click)"
  47. >
  48. {{ oper.name }}
  49. </el-button>
  50. <el-button
  51. v-if="scope.row[oper.key] == 'hide' || oper.hide"
  52. class="btn-item"
  53. type="text"
  54. size="small"
  55. >
  56. </el-button>
  57. <el-popover
  58. v-if="
  59. oper.popover &&
  60. scope.row[oper.key] != 'hide' &&
  61. oper.hide !== 'hide'
  62. "
  63. placement="top"
  64. width="160"
  65. v-model="oper.visible"
  66. >
  67. <p>{{ oper.content }}</p>
  68. <div style="text-align: right; margin: 0">
  69. <el-button size="mini" type="text" @click="oper.visible = false">取消</el-button>
  70. <el-button type="primary" size="mini" @click="operClick(scope, oper.click); oper.visible = false">确定</el-button>
  71. </div>
  72. <el-button
  73. slot="reference"
  74. class="btn-item"
  75. type="text"
  76. :class="{ 'danger': oper.key === 'delete' || oper.key === 'obsolete' }"
  77. size="small"
  78. >{{ oper.name }}</el-button
  79. >
  80. </el-popover>
  81. </span>
  82. </template>
  83. <template v-if="itemCol.type == 'custom'">
  84. <slot name="operation" v-bind="scope.row"></slot>
  85. </template>
  86. </div>
  87. <!-- 序号列 -->
  88. <div v-else-if="itemCol.prop == 'seq'">
  89. <span>
  90. {{itemCol.getIndex ? itemCol.getIndex(scope.$index) : scope.$index + 1 }}
  91. </span>
  92. </div>
  93. <!-- 小图预览 -->
  94. <div v-else-if="itemCol.type == 'imagePreview'">
  95. <el-image
  96. style="width: 50px; height: 50px"
  97. :src="scope.row[itemCol.prop]"
  98. @click="onPreview(scope.row, itemCol)"
  99. v-if="scope.row[itemCol.prop]"
  100. fit="cover"
  101. />
  102. <span v-else>--</span>
  103. </div>
  104. <!-- 字段合并 -->
  105. <div v-else-if="itemCol.type == 'merge'">
  106. <span>{{scope.row[itemCol.prop.split(',')[0]] || '--'}}{{scope.row[itemCol.prop.split(',')[1]]}}</span>
  107. </div>
  108. <!-- 表格可编辑列 -->
  109. <div v-else-if="itemCol.type == 'editItem'" style="display: flex;align-items: center;">
  110. <el-input
  111. @change="editItemChange(scope.row, itemCol)"
  112. v-if="itemCol.tag == 'input'"
  113. v-model="scope.row[itemCol.prop]"
  114. :disabled="isDisabled"
  115. placeholder="请输入"
  116. clearable
  117. size="small"
  118. />
  119. <el-input-number
  120. @change="editItemNumChange(scope.row, itemCol)"
  121. v-if="itemCol.tag == 'inputNumber'"
  122. v-model="scope.row[itemCol.prop]"
  123. :disabled="isDisabled"
  124. placeholder="请输入"
  125. size="small"
  126. :precision="itemCol.precision"
  127. :step="itemCol.step"
  128. clearable
  129. />
  130. <el-select v-if="itemCol.tag == 'select'" v-model="scope.row[itemCol.prop]" :disabled="isDisabled" clearable filterable placeholder="" size="small">
  131. <el-option v-for="(ele, idx) in itemCol.oprion" :key="idx" :label="ele[itemCol.optionLabel]" :value="ele[itemCol.optionValue]" />
  132. </el-select>
  133. <i @click="delRow(scope.$index)" v-if="itemCol.icon" style="font-size: 25px;" :class="itemCol.icon"></i>
  134. </div>
  135. <!-- 链接跳转 -->
  136. <div v-else-if="itemCol.type == 'link'">
  137. <el-button
  138. type="text"
  139. size="small"
  140. class="linkBtn"
  141. v-if="!scope.row['disabled'] && scope.row[itemCol.prop] !== '无'"
  142. @click="operClick(scope, itemCol.click)"
  143. >
  144. <!-- 除0外,其他空值(null、undefined)转为'--'-->
  145. {{
  146. scope.row[itemCol.prop] === ""
  147. ? "--"
  148. : (scope.row[itemCol.prop] !== null && scope.row[itemCol.prop] !== undefined) ? scope.row[itemCol.prop] : "--"
  149. }}
  150. </el-button>
  151. <span class="text-disable linkBtn" v-else>{{
  152. scope.row[itemCol.prop] === ""
  153. ? "--"
  154. : (scope.row[itemCol.prop] !== null && scope.row[itemCol.prop] !== undefined) ? scope.row[itemCol.prop] : "--"
  155. }}</span>
  156. </div>
  157. <!-- 单选/多选 -->
  158. <div v-else-if="itemCol.type == 'choice'">
  159. <div v-if="scope.row[itemCol.prop]">
  160. <div
  161. v-if="
  162. scope.row[itemCol.isRadioChecked] == '0' ||
  163. (scope.row[itemCol.prop] && scope.row[itemCol.prop][0] && scope.row[itemCol.prop][0][itemCol.isRadioChecked] == '0')
  164. "
  165. >
  166. <el-radio-group
  167. :value="scope.row[itemCol.model].join()"
  168. class="ml-4"
  169. >
  170. <el-radio
  171. v-for="radioItem in scope.row[itemCol.prop]"
  172. :label="radioItem.argumentsCode"
  173. size="large"
  174. :key="radioItem.argumentsName"
  175. >{{ radioItem.argumentsName }}</el-radio
  176. >
  177. </el-radio-group>
  178. </div>
  179. <div v-else>
  180. <el-checkbox-group :value="scope.row[itemCol.model]">
  181. <el-checkbox
  182. v-for="checkboxItem in scope.row[itemCol.prop]"
  183. :label="checkboxItem.argumentsCode"
  184. size="large"
  185. :key="checkboxItem.argumentsCode"
  186. >{{ checkboxItem.argumentsName }}</el-checkbox
  187. >
  188. </el-checkbox-group>
  189. </div>
  190. </div>
  191. </div>
  192. <!-- 需要遍历查找 -->
  193. <div v-else-if="itemCol.type == 'find'">
  194. <el-tooltip
  195. v-if="Boolean(scope.row[itemCol.customTip])"
  196. effect="dark"
  197. :content="scope.row[itemCol.customTip]"
  198. placement="top"
  199. >
  200. <span>{{
  201. toLabel(
  202. itemCol.option,
  203. scope.row[itemCol.prop],
  204. itemCol.optionValue,
  205. itemCol.optionLabel,
  206. itemCol.isNum
  207. )
  208. }}</span>
  209. </el-tooltip>
  210. <span v-else>
  211. {{
  212. toLabel(
  213. itemCol.option,
  214. scope.row[itemCol.prop],
  215. itemCol.optionValue,
  216. itemCol.optionLabel,
  217. itemCol.isNum
  218. )
  219. }}</span>
  220. </div>
  221. <div v-else-if="itemCol.type == 'tooltips'">
  222. <el-tooltip placement="top">
  223. <template slot="content" v-if="!itemCol.customTip">
  224. <p style="max-width: 15vw">
  225. {{ scope.row[itemCol.prop] }}
  226. </p>
  227. </template>
  228. <template slot="content" v-if="itemCol.customTip" >
  229. <p style="max-width: 15vw">
  230. {{ scope.row[itemCol.customTip] }}
  231. </p>
  232. </template>
  233. <div
  234. class="table-ellipsis"
  235. :style="`color:${itemCol.color}`"
  236. style="
  237. text-overflow: ellipsis;
  238. white-space: nowrap;
  239. overflow: hidden;
  240. "
  241. >
  242. {{ scope.row[itemCol.prop] }}
  243. </div>
  244. </el-tooltip>
  245. </div>
  246. <!-- 转化数据字典,支持显示多选结果 -->
  247. <span v-else-if="itemCol.type == 'dict2Name'">
  248. <template v-if="scope.row[itemCol.prop]">
  249. {{ parseDictValue2Name(scope.row[itemCol.prop], itemCol.dictList, itemCol) }}
  250. </template>
  251. <template v-else>--</template>
  252. </span>
  253. <!-- 自定义 -->
  254. <span v-else-if="itemCol.type == 'diy'">
  255. <slot :name="itemCol.prop" v-bind="scope.row"></slot>
  256. <span v-if="!$scopedSlots[itemCol.prop]">{{ scope.row[itemCol.prop] === '' ? '--' : scope.row[itemCol.prop] }}</span>
  257. </span>
  258. <span :style="`color:${itemCol.color}`" v-else>
  259. {{ scope.row[itemCol.prop] === '' ? '--' : scope.row[itemCol.prop] }}
  260. </span>
  261. </template>
  262. </el-table-column>
  263. </template>
  264. <script>
  265. import TableColumn from "./TableColumn.vue";
  266. export default {
  267. name: "TableColumn",
  268. components: {
  269. TableColumn,
  270. },
  271. props: {
  272. // 编辑时可传
  273. isDisabled: {
  274. type: Boolean,
  275. default: false,
  276. },
  277. itemCol: {
  278. type: Object,
  279. default: function() {
  280. return {};
  281. },
  282. },
  283. selection: {
  284. type: Boolean,
  285. default: true,
  286. },
  287. },
  288. data() {
  289. return {
  290. showViewer: false,
  291. };
  292. },
  293. computed: {
  294. customSlots() {
  295. return {
  296. ...this.$slots,
  297. };
  298. },
  299. },
  300. methods: {
  301. toLabel(
  302. option = [],
  303. val,
  304. value = "value",
  305. label = "label",
  306. isNum = false
  307. ) {
  308. // 该代码以后删掉
  309. if (isNum) {
  310. val = Number(val);
  311. }
  312. let labelName = option.find((item) => item[value] == val);
  313. return labelName ? labelName[label] : "--";
  314. },
  315. handleSelection(row) {
  316. return !row.selectionDisabled;
  317. },
  318. handleAlign(item) {
  319. if (item.align == "center") {
  320. return "center";
  321. }
  322. if (item.type == "innerColumn") {
  323. return "center";
  324. }
  325. if (item.prop == "seq") {
  326. return "center";
  327. } else {
  328. return "left";
  329. }
  330. // return item.align
  331. },
  332. operClick(scope, funName) {
  333. if (typeof funName === 'function') {
  334. funName(scope.row, scope.column, scope.$index);
  335. }
  336. },
  337. // 根据字典中的value转为name
  338. parseDictValue2Name(val, list, item) {
  339. if (!item || !item.multiple) {
  340. let name = '';
  341. list.map((e) => {
  342. if (e.value == val) {
  343. name = e.label;
  344. }
  345. });
  346. return name || val || '--';
  347. } else {
  348. let str = '';
  349. let arr = val.split(',');
  350. list.map((e) => {
  351. if (arr.includes(e.value)) {
  352. str += e.label + '、';
  353. }
  354. });
  355. return str ? str.slice(0, str.length - 1) : '--';
  356. }
  357. },
  358. // 图片展示弹框显示
  359. onPreview(row, itemCol) {
  360. // 判断该图片展示是否需要接口查询 (处理字段为base64的情况)
  361. if (itemCol && itemCol.imgQuery) {
  362. let axios = itemCol.imgQuery.queryFn;
  363. if (axios) {
  364. axios({
  365. id: row[itemCol.imgQuery.primaryKey],
  366. }).then((res) => {
  367. if (res.code == 200) {
  368. if (res.data[[itemCol.prop]].indexOf("/file") === -1) {
  369. // 使用base64时,拼接标识处理
  370. this.$emit("imgPreview", [
  371. `data:image/png;base64, ${res.data[[itemCol.prop]]}`,
  372. ]);
  373. } else {
  374. // 使用链接时直接反回
  375. this.$emit("imgPreview", [res.data[itemCol.prop]]);
  376. }
  377. }
  378. });
  379. }
  380. } else {
  381. this.$emit("imgPreview", row[itemCol.prop] ? row[itemCol.prop].split(",") : []);
  382. }
  383. },
  384. editItemChange(val, itemCol) {
  385. this.$emit("editItemChange", val, itemCol);
  386. },
  387. editItemNumChange(val, itemCol) {
  388. this.$emit("editItemNumChange", val, itemCol);
  389. },
  390. delRow(index){
  391. this.$emit("delRow", index);
  392. },
  393. delColumn(index){
  394. this.$emit("delColumn", index);
  395. }
  396. },
  397. };
  398. </script>
  399. <style lang="scss" scoped>
  400. .btn-item {
  401. margin: 0 10px;
  402. }
  403. .btn-item:nth-child(1) {
  404. margin-left: 0;
  405. }
  406. // 2.1.9版本之后新增此样式
  407. .btn-item.el-button--small:nth-child(1) {
  408. padding-left: 0 !important;
  409. }
  410. .danger {
  411. color: #f56455 !important;
  412. }
  413. .linkBtn {
  414. font-size: 14px;
  415. white-space: normal;
  416. line-height: 16px;
  417. text-align: left;
  418. height: auto;
  419. }
  420. // ::v-deep(.linkBtn) {
  421. // padding: 0;
  422. // >span {
  423. // overflow: hidden;
  424. // text-overflow: ellipsis;
  425. // white-space: nowrap;
  426. // width: 80px;
  427. // display: block;
  428. // }
  429. // }
  430. .text-disable {
  431. // padding-left: 12px;
  432. }
  433. //有自带的border
  434. // .el-input,.el-input-number,.el-select{
  435. // width: 100%;
  436. // border: 1px solid #dedede;
  437. // }
  438. </style>