index.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <template>
  2. <view class="ss-onoff-button" :class="{ checked: isChecked }" @click="toggleSelect">
  3. <text class="button-label">{{ label }}</text>
  4. <view class="button-mark">
  5. <text
  6. class="form-icon"
  7. :class="isChecked ? 'form-icon-onoffbutton-checked' : 'form-icon-onoffbutton-unchecked'"
  8. ></text>
  9. </view>
  10. </view>
  11. </template>
  12. <script setup>
  13. import { computed, inject, nextTick } from 'vue'
  14. const props = defineProps({
  15. // 字段名称,用于表单校验
  16. name: {
  17. type: String,
  18. required: true
  19. },
  20. // 显示标签
  21. label: {
  22. type: String,
  23. required: true
  24. },
  25. // 按钮的值
  26. value: {
  27. type: [String, Number],
  28. required: true
  29. },
  30. // 宽度设置
  31. width: {
  32. type: String,
  33. default: ''
  34. },
  35. // v-model 绑定的值
  36. modelValue: {
  37. type: [String, Number, Array],
  38. default: ''
  39. },
  40. // 是否多选模式
  41. multiple: {
  42. type: Boolean,
  43. default: false
  44. }
  45. })
  46. const emit = defineEmits(['update:modelValue', 'change'])
  47. // 从父组件注入校验相关功能
  48. const validateField = inject('validateField', () => {})
  49. // 解析 modelValue,支持逗号分隔的字符串和数组
  50. const parseModelValue = (val) => {
  51. if (!val) return []
  52. // 如果是数组,直接返回字符串数组
  53. if (Array.isArray(val)) {
  54. return val.map(v => v.toString())
  55. }
  56. // 如果是字符串,按逗号分割
  57. const cleanValue = val.toString().replace(/^,+/, '') // 去掉开头的逗号
  58. if (cleanValue.includes('|')) {
  59. return cleanValue.split('|')
  60. }
  61. if (cleanValue.includes(',')) {
  62. return cleanValue.split(',')
  63. }
  64. return cleanValue ? [cleanValue] : []
  65. }
  66. // 判断当前按钮是否选中
  67. const isChecked = computed(() => {
  68. if (props.multiple) {
  69. const currentValue = parseModelValue(props.modelValue)
  70. return currentValue.includes(props.value.toString())
  71. }
  72. return props.modelValue === props.value
  73. })
  74. // 切换选中状态
  75. const toggleSelect = () => {
  76. if (props.multiple) {
  77. // 多选模式
  78. const currentValue = parseModelValue(props.modelValue)
  79. const index = currentValue.indexOf(props.value.toString())
  80. let newValue
  81. if (index === -1) {
  82. // 添加选项
  83. newValue = [...currentValue, props.value.toString()]
  84. } else {
  85. // 移除选项
  86. newValue = currentValue.filter(v => v !== props.value.toString())
  87. }
  88. // 发送更新事件,使用逗号分隔的字符串格式
  89. const emitValue = newValue.join(',')
  90. emit('update:modelValue', emitValue)
  91. emit('change', emitValue, newValue)
  92. } else {
  93. // 单选模式
  94. emit('update:modelValue', props.value)
  95. emit('change', props.value)
  96. }
  97. // 触发校验
  98. nextTick(() => {
  99. if (props.name) {
  100. validateField(props.name)
  101. }
  102. })
  103. }
  104. </script>
  105. <style lang="scss" scoped>
  106. /* 引入iconfont样式 */
  107. @import '../../static/iconfont/iconfont.css';
  108. .ss-onoff-button {
  109. position: relative;
  110. display: inline-flex;
  111. align-items: center;
  112. justify-content: center;
  113. padding: 10rpx 36rpx;
  114. border: 2rpx solid #BFC1C6;
  115. border-radius: 4rpx;
  116. background-color: #fff;
  117. color: #C3C6CA;
  118. font-size: 28rpx;
  119. cursor: pointer;
  120. transition: all 0.3s ease;
  121. // margin-right: 20rpx;
  122. // margin-bottom: 20rpx;
  123. // min-width: 120rpx;
  124. &.checked {
  125. color: #393D51;
  126. border-color: #393D51;
  127. // background-color: #F8F9FA;
  128. }
  129. .button-label {
  130. flex: 1;
  131. text-align: center;
  132. line-height: 1.2;
  133. }
  134. .button-mark {
  135. position: absolute;
  136. bottom: 0rpx;
  137. right: 0rpx;
  138. width: 36rpx;
  139. height: 36rpx;
  140. display: flex;
  141. align-items: center;
  142. justify-content: center;
  143. color: inherit;
  144. }
  145. }
  146. /* 表单组件icon样式 - 完全按照PC端实现 */
  147. .form-icon {
  148. font-size: 36rpx;
  149. font-family: "iconfont";
  150. }
  151. .form-icon-onoffbutton-checked::before {
  152. content: "\e62d";
  153. }
  154. .form-icon-onoffbutton-unchecked::before {
  155. content: "\e62b";
  156. }
  157. </style>