index.vue 4.2 KB

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