tm-treeSelect.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <template>
  2. <view class="tm-treeSelect fulled-height">
  3. <view class="tm-treeSelect-slide ">
  4. <scroll-view scroll-y
  5. :style="{
  6. height:activeHeight+'px'
  7. }"
  8. >
  9. <tm-listitem :disabled="item['disabled']?true:false"
  10. :black="black_tmeme" @click="activeIndex=index"
  11. :disabled-color="bgColor"
  12. v-for="(item,index) in formaData" :key='index'
  13. :color="activeIndex==index?(text?' text ' + color:'white'):bgColor"
  14. :show-right-icon="false"
  15. :margin="[0,0]"
  16. fontSize="26"
  17. :class-style="activeIndex==index?`border-${color_tmeme}-l-2`:'border-grey-lighten-5-l-2'"
  18. round="0" shadow="0"
  19. >
  20. <tm-badges color='red' :fllowTheme="false" :offset="[12,-10]" v-if="item.dot>0&&dot" :dot="false" :label="item.dot"></tm-badges>
  21. <view class="mr-10">
  22. <text :class="[`text-${activeIndex==index?color_tmeme:fontColor}`,item['disabled']?'opacity-3':'']">{{item[rangKey]}}</text>
  23. </view>
  24. </tm-listitem>
  25. </scroll-view>
  26. </view>
  27. <view class="tm-treeSelect-slideitem" >
  28. <scroll-view scroll-y
  29. :style="{
  30. height:activeHeight+'px'
  31. }"
  32. >
  33. <tm-groupcheckbox v-if="formaData[activeIndex]" :max="max" @change="changeOk">
  34. <block v-for="(item,index) in formaData[activeIndex].children" :key='index'>
  35. <tm-checkbox dense
  36. :disabled="item['disabled']||formaData[activeIndex]['disabled']?true:false"
  37. :color="color_tmeme" round="2" @change="itemChange"
  38. :name="item"
  39. v-model="item.checked">
  40. <tm-listitem fontSize="28" :black="black_tmeme" :padding="[24,24]" :margin="[0,0]" :border-bottom="true" round="0" shadow="0"
  41. :title="item[rangKey]" >
  42. <template #default>
  43. <text :class="[item.checked?'text-'+color_tmeme:'']">{{item[rangKey]}}</text>
  44. </template>
  45. <template v-slot:rightIcon>
  46. <view style="line-height: 0;vertical-align: middle;min-width: 40rpx;" class="fulled">
  47. <view v-if="item.checked" class="rounded flex-center" :class="[color_tmeme,`shadow-${color_tmeme}-4`]" style="width: 34rpx;height: 34rpx;">
  48. <text class="iconfont icon-check text-size-xs"></text>
  49. </view>
  50. <!-- <tm-icons dense v-if="item.checked" name="icon-check-circle"></tm-icons> -->
  51. <!-- <tm-button v-if="item.checked" icon="icon-check" fab size="xs"></tm-button> -->
  52. </view>
  53. </template>
  54. </tm-listitem>
  55. </tm-checkbox>
  56. </block>
  57. </tm-groupcheckbox>
  58. </scroll-view>
  59. </view>
  60. </view>
  61. </template>
  62. <script>
  63. /**
  64. * 分类选择
  65. * @property {Number|String} height = [] 默认0,单位upx,自动使用父组件的高度。
  66. * @property {Array} default-selected = [] 默认[],可以是id索引也可以是对象数组,可以混着来。[1]
  67. * @property {Array} list = [] 默认[],数据结构对象,必须需要含有唯一标识符id即可。[{title:'苏州',id:1}],每一个类目和子类可以含有disabled值,用来禁用类目或者选项。
  68. * @property {String} rang-key = [] 默认'title',数据结构对象中,需要展示标签的key
  69. * @property {Number} max = [] 默认999,每一项目最大选择数量,1可以实现单选。
  70. * @property {String} color = [] 默认 primary,主题色名
  71. * @property {String} bg-color = [] 默认 grey-lighten-5,主题色名未选择时的背景色。
  72. * @property {String} font-color = [] 默认 grey,未选中时的文字颜色
  73. * @property {Boolean} text = [] 默认 false,使用主题文本色为高亮色。
  74. * @property {Boolean} black = [] 默认 false,是否暗黑模式。
  75. * @property {Boolean} dot = [] 默认 false,是否显示角标数字,当前选中的数量。
  76. * @property {Function} children-change 选中单个选项时触发。返回选中的数据。
  77. * @property {Function} change 每次选择变化都会触发全局change,返回当前类目和所有类目选中的数据。
  78. * @example <tm-treeSelect color="green" ></tm-treeSelect>
  79. */
  80. import tmListitem from "@/tm-vuetify/components/tm-listitem/tm-listitem.vue"
  81. import tmGrouplist from "@/tm-vuetify/components/tm-grouplist/tm-grouplist.vue"
  82. import tmBadges from "@/tm-vuetify/components/tm-badges/tm-badges.vue"
  83. import tmGroupcheckbox from "@/tm-vuetify/components/tm-groupcheckbox/tm-groupcheckbox.vue"
  84. import tmCheckbox from "@/tm-vuetify/components/tm-checkbox/tm-checkbox.vue"
  85. export default {
  86. components:{tmListitem,tmGrouplist,tmBadges,tmGroupcheckbox,tmCheckbox},
  87. name:"tm-treeSelect",
  88. props:{
  89. // 高度,默认为0时,自动使用父组件的高度.
  90. height: {
  91. type: String | Number,
  92. default: 0
  93. },
  94. // 可以是id索引也可以是对象数组,可以混着来。
  95. defaultSelected:{
  96. type:Array,
  97. default:()=>{
  98. return []
  99. }
  100. },
  101. // 对象数组
  102. list:{
  103. type:Array,
  104. default:()=>{
  105. return [];
  106. }
  107. },
  108. // 显示标签的key.
  109. rangKey:{
  110. type:String,
  111. default:'title'
  112. },
  113. // 每一项目最大选择数量,1可以实现单选。
  114. max:{
  115. type:Number,
  116. default:999
  117. },
  118. // 主题色名
  119. color:{
  120. type:String,
  121. default:'primary'
  122. },
  123. // 未选择时的背景色。
  124. bgColor:{
  125. type:String,
  126. default:'grey-lighten-5'
  127. },
  128. //未选择时的文字色。
  129. fontColor:{
  130. type:String,
  131. default:'grey-darken-1'
  132. },
  133. // 使用主题文本色为高亮色。
  134. text:{
  135. type:Boolean,
  136. default:false
  137. },
  138. black:{
  139. type:Boolean,
  140. default:null
  141. },
  142. // 是否显示角标数字当前选中的数量。
  143. dot:{
  144. type:Boolean,
  145. default:false
  146. },
  147. // 跟随主题色的改变而改变。
  148. fllowTheme:{
  149. type:Boolean|String,
  150. default:true
  151. }
  152. },
  153. data() {
  154. return {
  155. activeHeight: 0,
  156. activeIndex:0,
  157. formaData:[]
  158. };
  159. },
  160. watch:{
  161. list:{
  162. deep:true,
  163. handler:function(){
  164. this.formaData = [...this.dataList];
  165. }
  166. },
  167. defaultSelected:{
  168. deep:true,
  169. handler:function(){
  170. this.clear()
  171. this.formaData = [...this.clear()]
  172. this.formaData = [...this.dataList];
  173. }
  174. }
  175. },
  176. computed:{
  177. black_tmeme: function() {
  178. if (this.black !== null) return this.black;
  179. return this.$tm.vx.state().tmVuetify.black;
  180. },
  181. color_tmeme:function(){
  182. if(this.$tm.vx.state().tmVuetify.color!==null&&this.$tm.vx.state().tmVuetify.color && this.fllowTheme){
  183. return this.$tm.vx.state().tmVuetify.color;
  184. }
  185. return this.color;
  186. },
  187. dataList(){
  188. let t = this;
  189. let p = this.$tm.deepClone(this.list)
  190. for(let j=0;j<p.length;j++){
  191. p[j]['dot'] = 0;
  192. if(p[j].children){
  193. let ic = p[j].children;
  194. for(let k=0;k<ic.length;k++){
  195. if(!ic[k].hasOwnProperty('checked')){
  196. ic[k]['checked'] = false;
  197. }
  198. for(let i=0 ;i < t.defaultSelected.length;i++){
  199. let lsitem = t.defaultSelected[i];
  200. if(typeof lsitem === 'object'){
  201. if(lsitem['id'] == ic[k]['id']){
  202. ic[k]['checked'] = true;
  203. }
  204. }else{
  205. if(lsitem == ic[k]['id']){
  206. ic[k]['checked'] = true;
  207. }
  208. }
  209. }
  210. if(ic[k]['checked']===true){
  211. p[j]['dot'] +=1
  212. }
  213. }
  214. }
  215. }
  216. return p;
  217. },
  218. },
  219. mounted() {
  220. let t = this;
  221. this.$nextTick(async function() {
  222. this.activeHeight = uni.upx2px(this.height);
  223. if (!this.activeHeight) {
  224. let wsz = await this.$Querey(".tm-treeSelect",this).catch(e=>{})
  225. this.activeHeight = wsz[0].height||250;
  226. }
  227. this.formaData = [...this.dataList];
  228. });
  229. },
  230. methods: {
  231. clear(){
  232. let t = this;
  233. let p = this.$tm.deepClone(this.list)
  234. for(let j=0;j<p.length;j++){
  235. p[j]['dot'] = 0;
  236. if(p[j].children){
  237. let ic = p[j].children;
  238. for(let k=0;k<ic.length;k++){
  239. if(!ic[k].hasOwnProperty('checked')){
  240. ic[k]['checked'] = false;
  241. }else{
  242. ic[k]['checked'] = false;
  243. }
  244. }
  245. }
  246. }
  247. return p;
  248. },
  249. // 所有选项。
  250. changeOk(e) {
  251. let p = [];
  252. function ts(obj){
  253. if(Array.isArray(obj)){
  254. obj.forEach(item=>{
  255. if(item.children){
  256. ts(item.children);
  257. }else{
  258. if(item.checked===true){
  259. p.push(item)
  260. }
  261. }
  262. })
  263. }
  264. }
  265. ts(this.dataList);
  266. this.$emit('change',{
  267. all:p,//所有选择项
  268. index:this.activeIndex,//当前数目索引
  269. children:e//当前类目的选中的选择项
  270. });
  271. if(this.formaData.length>0){
  272. this.$set(this.formaData[this.activeIndex],'dot',e.length)
  273. }
  274. },
  275. // 单个数组点击切换产生的选项。
  276. itemChange(e){
  277. let t = this;
  278. //返回的格式: {"index":4,"checked":true,"value":推荐的数据}
  279. this.$emit('children-change',e)
  280. }
  281. },
  282. }
  283. </script>
  284. <style lang="scss" scoped>
  285. .tm-treeSelect{
  286. height: 100%;
  287. display: flex;
  288. justify-content: flex-start;
  289. .tm-treeSelect-slide{
  290. width: 190upx;
  291. }
  292. .tm-treeSelect-slideitem{
  293. width: calc(100% - 190upx);
  294. }
  295. }
  296. </style>