step.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <!-- 简化显示版 -->
  3. <view class="progress-steps simple">
  4. <!-- 动态渲染步骤 -->
  5. <template v-for="(step, index) in steps" >
  6. <!-- 步骤项 -->
  7. <view class="step-item" :class="{
  8. active: currentStep === step.stepNumber
  9. }">
  10. <!-- 圆圈部分 -->
  11. <view class="step-circle">
  12. <!-- 已完成的步骤显示完成图标 -->
  13. <image v-if="currentStep > step.stepNumber"
  14. class="icon"
  15. :src="step.doneIcon || '/static/image/icon_step_done.png'">
  16. </image>
  17. <!-- 当前和未完成的步骤显示数字 -->
  18. <text v-else>{{ step.stepNumber }}</text>
  19. </view>
  20. <!-- 步骤文本 -->
  21. <view class="step-text" :class="{
  22. active: currentStep === step.stepNumber
  23. }">
  24. {{ step.title }}
  25. </view>
  26. </view>
  27. <!-- 步骤之间的连线(最后一个步骤后不显示) -->
  28. <view v-if="index < steps.length - 1"
  29. class="step-line"
  30. :class="{ active: currentStep > step.stepNumber }">
  31. </view>
  32. </template>
  33. </view>
  34. </template>
  35. <script>
  36. export default {
  37. name: 'ProgressSteps',
  38. props: {
  39. // 当前步骤
  40. step: {
  41. type: Number,
  42. default: 1,
  43. validator: (value) => value > 0
  44. },
  45. // 步骤配置数据(支持数组或JSON字符串)
  46. stepsData: {
  47. type: [Array, String],
  48. default: () => [
  49. { id: 1, stepNumber: 1, title: '填写任务' },
  50. { id: 2, stepNumber: 2, title: '选择客户' },
  51. { id: 3, stepNumber: 3, title: '积分设置' }
  52. ]
  53. },
  54. // 完成图标(可覆盖单个步骤的配置)
  55. doneIcon: {
  56. type: String,
  57. default: '/static/image/icon_step_done.png'
  58. }
  59. },
  60. data() {
  61. return {
  62. currentStep: this.step,
  63. steps: []
  64. }
  65. },
  66. created() {
  67. this.initSteps()
  68. },
  69. watch: {
  70. current(newVal) {
  71. this.currentStep = newVal
  72. },
  73. stepsData: {
  74. handler(newVal) {
  75. this.initSteps()
  76. },
  77. deep: true,
  78. immediate: true
  79. }
  80. },
  81. methods: {
  82. // 初始化步骤数据
  83. initSteps() {
  84. let steps = []
  85. try {
  86. // 如果传入的是字符串,尝试解析为JSON
  87. if (typeof this.stepsData === 'string') {
  88. steps = JSON.parse(this.stepsData)
  89. } else if (Array.isArray(this.stepsData)) {
  90. steps = this.stepsData
  91. }
  92. // 验证和标准化数据
  93. steps = steps.map((item, index) => {
  94. return {
  95. id: item.id || index + 1,
  96. stepNumber: item.stepNumber || index + 1,
  97. title: item.title || `步骤${index + 1}`,
  98. doneIcon: item.doneIcon || this.doneIcon,
  99. // 可以添加更多自定义字段
  100. ...item
  101. }
  102. })
  103. // 按步骤号排序
  104. steps.sort((a, b) => a.stepNumber - b.stepNumber)
  105. this.steps = steps
  106. } catch (error) {
  107. console.error('解析步骤数据失败:', error)
  108. // 使用默认步骤
  109. this.steps = [
  110. { id: 1, stepNumber: 1, title: '填写任务' },
  111. { id: 2, stepNumber: 2, title: '选择客户' },
  112. { id: 3, stepNumber: 3, title: '积分设置' }
  113. ]
  114. }
  115. },
  116. // 跳转到指定步骤
  117. goToStep(stepNumber) {
  118. const targetStep = this.steps.find(step => step.stepNumber === stepNumber)
  119. if (targetStep && stepNumber >= 1 && stepNumber <= this.steps.length) {
  120. const oldStep = this.currentStep
  121. this.currentStep = stepNumber
  122. // 触发事件
  123. this.$emit('step-change', {
  124. stepNumber: this.currentStep,
  125. stepData: targetStep,
  126. oldStep: oldStep
  127. })
  128. // 点击步骤事件
  129. this.$emit('step-click', {
  130. stepNumber: stepNumber,
  131. stepData: targetStep
  132. })
  133. }
  134. },
  135. // 下一步
  136. goNext() {
  137. if (this.currentStep < this.steps.length) {
  138. this.goToStep(this.currentStep + 1)
  139. this.$emit('next', this.currentStep)
  140. }
  141. },
  142. // 上一步
  143. goPrev() {
  144. if (this.currentStep > 1) {
  145. this.goToStep(this.currentStep - 1)
  146. this.$emit('prev', this.currentStep)
  147. }
  148. },
  149. // 提交
  150. handleSubmit() {
  151. this.$emit('submit')
  152. },
  153. // 获取当前步骤数据
  154. getCurrentStep() {
  155. return this.steps.find(step => step.stepNumber === this.currentStep) || null
  156. },
  157. // 获取所有步骤
  158. getAllSteps() {
  159. return [...this.steps]
  160. },
  161. // 更新步骤数据(可用于动态修改标题等)
  162. updateStep(stepNumber, newData) {
  163. const index = this.steps.findIndex(step => step.stepNumber === stepNumber)
  164. if (index !== -1) {
  165. this.steps[index] = { ...this.steps[index], ...newData }
  166. this.$forceUpdate() // 强制更新视图
  167. }
  168. }
  169. }
  170. }
  171. </script>
  172. <style lang="scss" scoped>
  173. /* 简化版样式 - 确保圆圈和连线无缝连接 */
  174. .progress-steps.simple {
  175. display: flex;
  176. align-items: center;
  177. justify-content: center;
  178. padding: 16rpx 38rpx;
  179. position: relative;
  180. margin: 30rpx 48rpx;
  181. .step-item {
  182. display: flex;
  183. flex-direction: column;
  184. align-items: center;
  185. position: relative;
  186. z-index: 2;
  187. cursor: pointer;
  188. transition: all 0.3s ease;
  189. &:active {
  190. opacity: 0.7;
  191. }
  192. &.active {
  193. .step-circle {
  194. background: #388BFF;
  195. color: #FFFFFF;
  196. }
  197. }
  198. &:not(.active) {
  199. .step-circle {
  200. background: #FFFFFF;
  201. color: #C0C4CC;
  202. border: 2rpx solid #E4E7ED;
  203. }
  204. }
  205. }
  206. .step-circle {
  207. width: 48rpx;
  208. height: 48rpx;
  209. border-radius: 50%;
  210. display: flex;
  211. align-items: center;
  212. justify-content: center;
  213. font-size: 28rpx;
  214. font-weight: 500;
  215. margin-bottom: 8rpx;
  216. position: relative;
  217. z-index: 2;
  218. box-sizing: border-box;
  219. transition: all 0.3s ease;
  220. .icon {
  221. width: 100%;
  222. height: 100%;
  223. }
  224. }
  225. .step-text {
  226. font-size: 28rpx;
  227. color: #999999;
  228. white-space: nowrap;
  229. transition: all 0.3s ease;
  230. &.active {
  231. font-weight: 500;
  232. font-size: 28rpx;
  233. color: #333333;
  234. }
  235. }
  236. .step-line {
  237. flex: 1;
  238. height: 4rpx;
  239. background: #E4E7ED;
  240. margin: 0 -30rpx;
  241. /* 负边距让线延伸到圆圈边缘 */
  242. position: relative;
  243. top: -30rpx;
  244. max-width: 294rpx;
  245. /* 调整到圆圈中心位置 */
  246. z-index: 1;
  247. min-width: 60rpx;
  248. transition: all 0.3s ease;
  249. &.active {
  250. background: #388BFF;
  251. opacity: 0.2;
  252. }
  253. }
  254. }
  255. /* 响应式调整 */
  256. @media (max-width: 750px) {
  257. .progress-steps.simple {
  258. padding: 20rpx 15rpx;
  259. margin: 20rpx 30rpx;
  260. .step-circle {
  261. width: 50rpx;
  262. height: 50rpx;
  263. font-size: 24rpx;
  264. }
  265. .step-text {
  266. font-size: 24rpx;
  267. &.active {
  268. font-size: 24rpx;
  269. }
  270. }
  271. .step-line {
  272. margin: 0 -25rpx;
  273. top: -25rpx;
  274. min-width: 40rpx;
  275. }
  276. }
  277. }
  278. </style>