uni-th.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <template>
  2. <!-- #ifdef H5 -->
  3. <th
  4. :rowspan="rowspan"
  5. :colspan="colspan"
  6. class="uni-table-th"
  7. :class="{ 'table--border': border }"
  8. :style="{ width: customWidth + 'px', 'text-align': align }"
  9. >
  10. <view class="uni-table-th-row">
  11. <view
  12. class="uni-table-th-content"
  13. :style="{ 'justify-content': contentAlign }"
  14. @click="sort"
  15. >
  16. <slot></slot>
  17. <view v-if="sortable" class="arrow-box">
  18. <text
  19. class="arrow up"
  20. :class="{ active: ascending }"
  21. @click.stop="ascendingFn"
  22. ></text>
  23. <text
  24. class="arrow down"
  25. :class="{ active: descending }"
  26. @click.stop="descendingFn"
  27. ></text>
  28. </view>
  29. </view>
  30. <dropdown
  31. v-if="filterType || filterData.length"
  32. :filterDefaultValue="filterDefaultValue"
  33. :filterData="filterData"
  34. :filterType="filterType"
  35. @change="ondropdown"
  36. ></dropdown>
  37. </view>
  38. </th>
  39. <!-- #endif -->
  40. <!-- #ifndef H5 -->
  41. <view
  42. class="uni-table-th"
  43. :class="{ 'table--border': border }"
  44. :style="{ width: customWidth + 'px', 'text-align': align }"
  45. ><slot></slot
  46. ></view>
  47. <!-- #endif -->
  48. </template>
  49. <script>
  50. // #ifdef H5
  51. import dropdown from './filter-dropdown.vue'
  52. // #endif
  53. /**
  54. * Th 表头
  55. * @description 表格内的表头单元格组件
  56. * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
  57. * @property {Number | String} width 单元格宽度(支持纯数字、携带单位px或rpx)
  58. * @property {Boolean} sortable 是否启用排序
  59. * @property {Number} align = [left|center|right] 单元格对齐方式
  60. * @value left 单元格文字左侧对齐
  61. * @value center 单元格文字居中
  62. * @value right 单元格文字右侧对齐
  63. * @property {Array} filterData 筛选数据
  64. * @property {String} filterType [search|select] 筛选类型
  65. * @value search 关键字搜素
  66. * @value select 条件选择
  67. * @event {Function} sort-change 排序触发事件
  68. */
  69. export default {
  70. name: 'uniTh',
  71. options: {
  72. virtualHost: true
  73. },
  74. components: {
  75. // #ifdef H5
  76. dropdown
  77. // #endif
  78. },
  79. emits: ['sort-change', 'filter-change'],
  80. props: {
  81. width: {
  82. type: [String, Number],
  83. default: ''
  84. },
  85. align: {
  86. type: String,
  87. default: 'left'
  88. },
  89. rowspan: {
  90. type: [Number, String],
  91. default: 1
  92. },
  93. colspan: {
  94. type: [Number, String],
  95. default: 1
  96. },
  97. sortable: {
  98. type: Boolean,
  99. default: false
  100. },
  101. filterType: {
  102. type: String,
  103. default: ''
  104. },
  105. filterData: {
  106. type: Array,
  107. default () {
  108. return []
  109. }
  110. },
  111. filterDefaultValue: {
  112. type: [Array, String],
  113. default () {
  114. return ''
  115. }
  116. }
  117. },
  118. data () {
  119. return {
  120. border: false,
  121. ascending: false,
  122. descending: false
  123. }
  124. },
  125. computed: {
  126. // 根据props中的width属性 自动匹配当前th的宽度(px)
  127. customWidth () {
  128. if (typeof this.width === 'number') {
  129. return this.width
  130. } else if (typeof this.width === 'string') {
  131. let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g)
  132. let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g)
  133. let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g)
  134. if (this.width.match(regexHaveUnitPx) !== null) {
  135. // 携带了 px
  136. return this.width.replace('px', '')
  137. } else if (this.width.match(regexHaveUnitRpx) !== null) {
  138. // 携带了 rpx
  139. let numberRpx = Number(this.width.replace('rpx', ''))
  140. let widthCoe = uni.getSystemInfoSync().screenWidth / 750
  141. return Math.round(numberRpx * widthCoe)
  142. } else if (this.width.match(regexHaveNotUnit) !== null) {
  143. // 未携带 rpx或px 的纯数字 String
  144. return this.width
  145. } else {
  146. // 不符合格式
  147. return ''
  148. }
  149. } else {
  150. return ''
  151. }
  152. },
  153. contentAlign () {
  154. let align = 'left'
  155. switch (this.align) {
  156. case 'left':
  157. align = 'flex-start'
  158. break
  159. case 'center':
  160. align = 'center'
  161. break
  162. case 'right':
  163. align = 'flex-end'
  164. break
  165. }
  166. return align
  167. }
  168. },
  169. created () {
  170. this.root = this.getTable('uniTable')
  171. this.rootTr = this.getTable('uniTr')
  172. this.rootTr.minWidthUpdate(this.customWidth ? this.customWidth : 140)
  173. this.border = this.root.border
  174. this.root.thChildren.push(this)
  175. },
  176. methods: {
  177. sort () {
  178. if (!this.sortable) return
  179. this.clearOther()
  180. if (!this.ascending && !this.descending) {
  181. this.ascending = true
  182. this.$emit('sort-change', { order: 'ascending' })
  183. return
  184. }
  185. if (this.ascending && !this.descending) {
  186. this.ascending = false
  187. this.descending = true
  188. this.$emit('sort-change', { order: 'descending' })
  189. return
  190. }
  191. if (!this.ascending && this.descending) {
  192. this.ascending = false
  193. this.descending = false
  194. this.$emit('sort-change', { order: null })
  195. }
  196. },
  197. ascendingFn () {
  198. this.clearOther()
  199. this.ascending = !this.ascending
  200. this.descending = false
  201. this.$emit('sort-change', { order: this.ascending ? 'ascending' : null })
  202. },
  203. descendingFn () {
  204. this.clearOther()
  205. this.descending = !this.descending
  206. this.ascending = false
  207. this.$emit('sort-change', {
  208. order: this.descending ? 'descending' : null
  209. })
  210. },
  211. clearOther () {
  212. this.root.thChildren.map(item => {
  213. if (item !== this) {
  214. item.ascending = false
  215. item.descending = false
  216. }
  217. return item
  218. })
  219. },
  220. ondropdown (e) {
  221. this.$emit('filter-change', e)
  222. },
  223. /**
  224. * 获取父元素实例
  225. */
  226. getTable (name) {
  227. let parent = this.$parent
  228. let parentName = parent.$options.name
  229. while (parentName !== name) {
  230. parent = parent.$parent
  231. if (!parent) return false
  232. parentName = parent.$options.name
  233. }
  234. return parent
  235. }
  236. }
  237. }
  238. </script>
  239. <style lang="scss">
  240. $border-color: #ebeef5;
  241. $uni-primary: #007aff !default;
  242. .uni-table-th {
  243. padding: 12px 10px;
  244. /* #ifndef APP-NVUE */
  245. display: table-cell;
  246. box-sizing: border-box;
  247. /* #endif */
  248. font-size: 32rpx;
  249. font-weight: bold;
  250. color: #909399;
  251. border-bottom: 1px $border-color solid;
  252. }
  253. .uni-table-th-row {
  254. /* #ifndef APP-NVUE */
  255. display: flex;
  256. /* #endif */
  257. flex-direction: row;
  258. }
  259. .table--border {
  260. border-right: 1px $border-color solid;
  261. }
  262. .uni-table-th-content {
  263. display: flex;
  264. align-items: center;
  265. flex: 1;
  266. }
  267. .arrow-box {
  268. }
  269. .arrow {
  270. display: block;
  271. position: relative;
  272. width: 10px;
  273. height: 8px;
  274. // border: 1px red solid;
  275. left: 5px;
  276. overflow: hidden;
  277. cursor: pointer;
  278. }
  279. .down {
  280. top: 3px;
  281. ::after {
  282. content: '';
  283. width: 8px;
  284. height: 8px;
  285. position: absolute;
  286. left: 2px;
  287. top: -5px;
  288. transform: rotate(45deg);
  289. background-color: #ccc;
  290. }
  291. &.active {
  292. ::after {
  293. background-color: $uni-primary;
  294. }
  295. }
  296. }
  297. .up {
  298. ::after {
  299. content: '';
  300. width: 8px;
  301. height: 8px;
  302. position: absolute;
  303. left: 2px;
  304. top: 5px;
  305. transform: rotate(45deg);
  306. background-color: #ccc;
  307. }
  308. &.active {
  309. ::after {
  310. background-color: $uni-primary;
  311. }
  312. }
  313. }
  314. </style>