node-edit.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <template>
  2. <view class="container">
  3. <view v-if="!loading" class="form-container">
  4. <view class="section">
  5. <view class="section-title">节点配置</view>
  6. <view class="form-item">
  7. <text class="label">任务标题</text>
  8. <input v-model="node.title" class="input" placeholder="请输入任务标题" />
  9. </view>
  10. <view class="form-item">
  11. <text class="label">任务描述</text>
  12. <textarea v-model="node.description" class="textarea" placeholder="可选,任务描述" />
  13. </view>
  14. <view class="form-item">
  15. <text class="label">积分</text>
  16. <input v-model="node.points" class="input" type="number" placeholder="完成此任务获得的积分" />
  17. </view>
  18. <view class="form-item">
  19. <text class="label">超时时间(分钟)</text>
  20. <input v-model="node.timeout_minutes" class="input" type="number" placeholder="0表示不限制" />
  21. <text class="hint">超过此时长未完成任务将触发超时兜底</text>
  22. </view>
  23. <view class="form-item">
  24. <text class="label">最大循环次数</text>
  25. <input v-model="node.max_loops" class="input" type="number" placeholder="0表示不限制" />
  26. <text class="hint">达到上限后节点自动终止</text>
  27. </view>
  28. <view class="form-item">
  29. <text class="label">执行人</text>
  30. <picker :range="executorTypes" @change="onExecutorChange">
  31. <view class="picker">{{ executorLabels[node.executorType] || '请选择' }}</view>
  32. </picker>
  33. </view>
  34. <view class="form-item" v-if="node.executorType === 'member'">
  35. <text class="label">分配给</text>
  36. <checkbox-group @change="onMemberChange">
  37. <label v-for="m in members" :key="m.id" class="checkbox-label">
  38. <checkbox :value="String(m.id)" :checked="selectedMembers.indexOf(m.id) >= 0" />
  39. <text>{{ m.nickname }}</text>
  40. </label>
  41. </checkbox-group>
  42. </view>
  43. </view>
  44. <view class="action-bar">
  45. <button class="btn-save" @click="handleSave">保存</button>
  46. </view>
  47. </view>
  48. <view v-if="loading" class="loading">
  49. <text>加载中...</text>
  50. </view>
  51. </view>
  52. </template>
  53. <script>
  54. import { getFamilyMemberList } from '../../utils/api.js'
  55. export default {
  56. data() {
  57. return {
  58. nodeId: null,
  59. node: {
  60. title: '',
  61. description: '',
  62. points: 2,
  63. timeout_minutes: 0,
  64. max_loops: 0,
  65. executorType: 'child'
  66. },
  67. members: [],
  68. selectedMembers: [],
  69. executorTypes: ['child', 'member'],
  70. executorLabels: { child: '孩子', member: '家庭成员' },
  71. loading: false
  72. }
  73. },
  74. onLoad(options) {
  75. this.nodeId = options.id
  76. // 初始化节点数据(从页面参数或父组件传递)
  77. if (options.nodeData) {
  78. try {
  79. this.node = JSON.parse(decodeURIComponent(options.nodeData))
  80. } catch (e) {
  81. console.error('解析节点数据失败', e)
  82. }
  83. }
  84. this.loadMembers()
  85. },
  86. methods: {
  87. async loadMembers() {
  88. try {
  89. var res = await getFamilyMemberList({})
  90. this.members = res.data || []
  91. } catch (e) {
  92. console.error('加载成员列表失败', e)
  93. }
  94. },
  95. onExecutorChange(e) {
  96. var idx = e.detail.value
  97. this.node.executorType = this.executorTypes[idx]
  98. },
  99. onMemberChange(e) {
  100. this.selectedMembers = e.detail.value.map(Number)
  101. },
  102. handleSave() {
  103. // 返回上一页并传递节点数据
  104. var pages = getCurrentPages()
  105. if (pages.length > 1) {
  106. var prevPage = pages[pages.length - 2]
  107. prevPage.$vm.updateNode(this.nodeId, this.node)
  108. }
  109. uni.navigateBack()
  110. }
  111. }
  112. }
  113. </script>
  114. <style scoped>
  115. .container {
  116. min-height: 100vh;
  117. background: #F8F8F8;
  118. }
  119. .loading {
  120. text-align: center;
  121. padding: 100rpx;
  122. color: #94A3B8;
  123. }
  124. .form-container {
  125. padding: 30rpx;
  126. }
  127. .section {
  128. background: #fff;
  129. border-radius: 16rpx;
  130. padding: 30rpx;
  131. margin-bottom: 24rpx;
  132. }
  133. .section-title {
  134. font-size: 28rpx;
  135. font-weight: bold;
  136. color: #1E293B;
  137. margin-bottom: 24rpx;
  138. }
  139. .form-item {
  140. margin-bottom: 24rpx;
  141. }
  142. .label {
  143. display: block;
  144. font-size: 26rpx;
  145. color: #64748B;
  146. margin-bottom: 12rpx;
  147. }
  148. .input {
  149. width: 100%;
  150. height: 80rpx;
  151. border: 2rpx solid #E2E8F0;
  152. border-radius: 12rpx;
  153. padding: 0 20rpx;
  154. font-size: 28rpx;
  155. box-sizing: border-box;
  156. }
  157. .textarea {
  158. width: 100%;
  159. height: 160rpx;
  160. border: 2rpx solid #E2E8F0;
  161. border-radius: 12rpx;
  162. padding: 20rpx;
  163. font-size: 28rpx;
  164. box-sizing: border-box;
  165. }
  166. .picker {
  167. width: 100%;
  168. height: 80rpx;
  169. border: 2rpx solid #E2E8F0;
  170. border-radius: 12rpx;
  171. padding: 0 20rpx;
  172. font-size: 28rpx;
  173. line-height: 80rpx;
  174. }
  175. .hint {
  176. display: block;
  177. font-size: 22rpx;
  178. color: #94A3B8;
  179. margin-top: 8rpx;
  180. }
  181. .checkbox-label {
  182. display: flex;
  183. align-items: center;
  184. padding: 16rpx 0;
  185. }
  186. .action-bar {
  187. padding: 30rpx;
  188. }
  189. .btn-save {
  190. width: 100%;
  191. height: 88rpx;
  192. line-height: 88rpx;
  193. border-radius: 44rpx;
  194. font-size: 30rpx;
  195. font-weight: bold;
  196. background: linear-gradient(135deg, #F97316 0%, #EA580C 100%);
  197. color: #fff;
  198. border: none;
  199. }
  200. .btn-save::after { border: none; }
  201. </style>