node-edit.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <template>
  2. <view class="container" :style="{ '--font-scale': fontScale }">
  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. import fontScaleMixin from '@/mixins/font-scale.js'
  56. export default {
  57. mixins: [fontScaleMixin],
  58. data() {
  59. return {
  60. nodeId: null,
  61. node: {
  62. title: '',
  63. description: '',
  64. points: 2,
  65. timeout_minutes: 0,
  66. max_loops: 0,
  67. executorType: 'child'
  68. },
  69. members: [],
  70. selectedMembers: [],
  71. executorTypes: ['child', 'member'],
  72. executorLabels: { child: '儿童', member: '家庭成员' },
  73. loading: false
  74. }
  75. },
  76. onLoad(options) {
  77. this.nodeId = options.id
  78. // 初始化节点数据(从页面参数或父组件传递)
  79. if (options.nodeData) {
  80. try {
  81. this.node = JSON.parse(decodeURIComponent(options.nodeData))
  82. } catch (e) {
  83. console.error('解析节点数据失败', e)
  84. }
  85. }
  86. this.loadMembers()
  87. },
  88. methods: {
  89. async loadMembers() {
  90. try {
  91. var res = await getFamilyMemberList({})
  92. this.members = res.data || []
  93. } catch (e) {
  94. console.error('加载成员列表失败', e)
  95. }
  96. },
  97. onExecutorChange(e) {
  98. var idx = e.detail.value
  99. this.node.executorType = this.executorTypes[idx]
  100. },
  101. onMemberChange(e) {
  102. this.selectedMembers = e.detail.value.map(Number)
  103. },
  104. handleSave() {
  105. // 返回上一页并传递节点数据
  106. var pages = getCurrentPages()
  107. if (pages.length > 1) {
  108. var prevPage = pages[pages.length - 2]
  109. prevPage.$vm.updateNode(this.nodeId, this.node)
  110. }
  111. uni.navigateBack()
  112. }
  113. }
  114. }
  115. </script>
  116. <style scoped lang="scss">
  117. .container {
  118. min-height: 100vh;
  119. background: $facade-fill-page;
  120. }
  121. .loading {
  122. text-align: center;
  123. padding: 100rpx;
  124. color: $app-text-subtle;
  125. }
  126. .form-container {
  127. padding: $uni-space-page;
  128. }
  129. .section {
  130. background: $app-surface;
  131. border-radius: 16rpx;
  132. padding: $uni-space-page;
  133. margin-bottom: 24rpx;
  134. }
  135. .section-title {
  136. font-size: calc(28rpx * var(--font-scale, 1));
  137. font-weight: bold;
  138. color: $uni-text-color;
  139. margin-bottom: 24rpx;
  140. }
  141. .form-item {
  142. margin-bottom: 24rpx;
  143. }
  144. .label {
  145. display: block;
  146. font-size: calc(26rpx * var(--font-scale, 1));
  147. color: $uni-text-color-secondary;
  148. margin-bottom: 12rpx;
  149. }
  150. .input {
  151. width: 100%;
  152. height: 80rpx;
  153. border: 2rpx solid $uni-border-color-light;
  154. border-radius: 16rpx;
  155. padding: 0 20rpx;
  156. font-size: calc(28rpx * var(--font-scale, 1));
  157. box-sizing: border-box;
  158. }
  159. .textarea {
  160. width: 100%;
  161. height: 160rpx;
  162. border: 2rpx solid $uni-border-color-light;
  163. border-radius: 16rpx;
  164. padding: 20rpx;
  165. font-size: calc(28rpx * var(--font-scale, 1));
  166. box-sizing: border-box;
  167. }
  168. .picker {
  169. width: 100%;
  170. height: 80rpx;
  171. border: 2rpx solid $uni-border-color-light;
  172. border-radius: 16rpx;
  173. padding: 0 20rpx;
  174. font-size: calc(28rpx * var(--font-scale, 1));
  175. line-height: 80rpx;
  176. }
  177. .hint {
  178. display: block;
  179. font-size: calc(22rpx * var(--font-scale, 1));
  180. color: $app-text-subtle;
  181. margin-top: 8rpx;
  182. }
  183. .checkbox-label {
  184. display: flex;
  185. align-items: center;
  186. padding: 16rpx 0;
  187. }
  188. .action-bar {
  189. padding: $uni-space-page;
  190. }
  191. .btn-save {
  192. width: 100%;
  193. height: 88rpx;
  194. line-height: 88rpx;
  195. border-radius: 24rpx;
  196. font-size: calc(30rpx * var(--font-scale, 1));
  197. font-weight: bold;
  198. background: linear-gradient(135deg, $brand-primary 0%, $brand-primary-dark 100%);
  199. color: $app-surface;
  200. border: none;
  201. }
  202. .btn-save::after { border: none; }
  203. </style>