meal-config.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <template>
  2. <view class="meal-config-page">
  3. <scroll-view class="content" scroll-y>
  4. <!-- 日期类型切换 -->
  5. <view class="date-tabs">
  6. <view class="tab-item" :class="{'tab-active': dateType === 'weekday'}" @tap="switchDateType('weekday')">
  7. <text>工作日</text>
  8. </view>
  9. <view class="tab-item" :class="{'tab-active': dateType === 'weekend'}" @tap="switchDateType('weekend')">
  10. <text>周末</text>
  11. </view>
  12. </view>
  13. <!-- 餐次配置 -->
  14. <view class="meal-section" v-for="meal in mealTypes" :key="meal.value">
  15. <view class="meal-header">
  16. <text class="meal-icon">{{ meal.icon }}</text>
  17. <text class="meal-name">{{ meal.label }}</text>
  18. </view>
  19. <view class="member-list">
  20. <view class="member-item" v-for="member in familyMembers" :key="member.id"
  21. :class="{'member-selected': isMemberSelected(meal.value, member.id)}"
  22. @tap="toggleMember(meal.value, member.id)">
  23. <text class="member-avatar">{{ member.nickname ? member.nickname.substring(0, 1) : '?' }}</text>
  24. <text class="member-name">{{ member.nickname || '未知' }}</text>
  25. </view>
  26. <view class="add-member-btn" @tap="addMember(meal.value)">
  27. <text>+</text>
  28. </view>
  29. </view>
  30. </view>
  31. <!-- 说明 -->
  32. <view class="tip-card">
  33. <text class="tip-text">💡 提示:选择与您在{{ dateType === 'weekday' ? '工作日' : '周末' }}共餐的家庭成员</text>
  34. </view>
  35. <!-- 保存按钮 -->
  36. <view class="save-area">
  37. <view class="save-btn" @tap="saveConfig">
  38. <text>{{ saving ? '保存中...' : '保存配置' }}</text>
  39. </view>
  40. </view>
  41. </scroll-view>
  42. </view>
  43. </template>
  44. <script>
  45. import config from '@/config.js'
  46. import { getMealConfig, saveMealConfig } from '@/utils/api.js'
  47. export default {
  48. data() {
  49. return {
  50. dateType: 'weekday',
  51. familyMembers: [],
  52. mealTypes: [
  53. { label: '早餐', value: 'breakfast', icon: '🌅' },
  54. { label: '午餐', value: 'lunch', icon: '☀️' },
  55. { label: '晚餐', value: 'dinner', icon: '🌙' }
  56. ],
  57. mealConfigs: {},
  58. saving: false
  59. }
  60. },
  61. onLoad: function() {
  62. this.loadMembers()
  63. this.loadConfigs()
  64. },
  65. methods: {
  66. goBack: function() {
  67. uni.navigateBack()
  68. },
  69. loadMembers: function() {
  70. var self = this
  71. var token = uni.getStorageSync('token')
  72. if (!token) return
  73. uni.request({
  74. url: this.getApiUrl('/api/family/member/list'),
  75. method: 'POST',
  76. data: {},
  77. header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
  78. success: function(res) {
  79. if (res.data && res.data.code === 200 && res.data.data) {
  80. self.familyMembers = res.data.data
  81. }
  82. }
  83. })
  84. },
  85. loadConfigs: function() {
  86. var self = this
  87. this.mealTypes.forEach(function(meal) {
  88. getMealConfig({ date_type: self.dateType, meal_type: meal.value }).then(function(config) {
  89. if (config) {
  90. self.mealConfigs[meal.value] = config
  91. }
  92. })
  93. })
  94. },
  95. switchDateType: function(type) {
  96. this.dateType = type
  97. this.loadConfigs()
  98. },
  99. isMemberSelected: function(mealType, memberId) {
  100. var config = this.mealConfigs[mealType]
  101. if (!config || !config.participantMemberIds) return false
  102. try {
  103. var ids = JSON.parse(config.participantMemberIds)
  104. return ids.indexOf(memberId) >= 0
  105. } catch (e) {
  106. return false
  107. }
  108. },
  109. toggleMember: function(mealType, memberId) {
  110. var config = this.mealConfigs[mealType] || {}
  111. var ids = []
  112. if (config.participantMemberIds) {
  113. try {
  114. ids = JSON.parse(config.participantMemberIds)
  115. } catch (e) {}
  116. }
  117. var index = ids.indexOf(memberId)
  118. if (index >= 0) {
  119. ids.splice(index, 1)
  120. } else {
  121. ids.push(memberId)
  122. }
  123. this.mealConfigs[mealType] = Object.assign({}, config, { participantMemberIds: JSON.stringify(ids) })
  124. },
  125. addMember: function(mealType) {
  126. // 简化的添加成员逻辑
  127. this.toggleMember(mealType, this.familyMembers[0] && this.familyMembers[0].id)
  128. },
  129. saveConfig: function() {
  130. var self = this
  131. this.saving = true
  132. var promises = this.mealTypes.map(function(meal) {
  133. var config = self.mealConfigs[meal.value] || {}
  134. return saveMealConfig({
  135. date_type: self.dateType,
  136. meal_type: meal.value,
  137. participant_member_ids: JSON.stringify(config.participantMemberIds || '[]')
  138. })
  139. })
  140. Promise.all(promises).then(function() {
  141. self.saving = false
  142. uni.showToast({ title: '保存成功', icon: 'success' })
  143. }).catch(function() {
  144. self.saving = false
  145. uni.showToast({ title: '保存失败', icon: 'none' })
  146. })
  147. },
  148. getApiUrl: function(path) {
  149. return config.api(path)
  150. }
  151. }
  152. }
  153. </script>
  154. <style scoped>
  155. .meal-config-page {
  156. min-height: 100vh;
  157. background-color: #F5F7FA;
  158. display: flex;
  159. flex-direction: column;
  160. }
  161. .nav-placeholder {
  162. width: 80rpx;
  163. }
  164. .content {
  165. flex: 1;
  166. padding: 30rpx;
  167. }
  168. .date-tabs {
  169. display: flex;
  170. gap: 20rpx;
  171. margin-bottom: 30rpx;
  172. }
  173. .tab-item {
  174. flex: 1;
  175. padding: 20rpx 0;
  176. border: 1rpx solid #ddd;
  177. border-radius: 30rpx;
  178. text-align: center;
  179. font-size: 26rpx;
  180. color: #666;
  181. background-color: #FFFFFF;
  182. }
  183. .tab-active {
  184. border-color: #FF8C42;
  185. color: #FF8C42;
  186. background-color: #FFF5EB;
  187. font-weight: 600;
  188. }
  189. .meal-section {
  190. background-color: #FFFFFF;
  191. border-radius: 20rpx;
  192. margin-bottom: 30rpx;
  193. overflow: hidden;
  194. box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
  195. }
  196. .meal-header {
  197. display: flex;
  198. align-items: center;
  199. padding: 24rpx 30rpx;
  200. border-bottom: 1rpx solid #f0f0f0;
  201. }
  202. .meal-icon {
  203. font-size: 36rpx;
  204. margin-right: 16rpx;
  205. }
  206. .meal-name {
  207. font-size: 28rpx;
  208. font-weight: 600;
  209. color: #333;
  210. }
  211. .member-list {
  212. display: flex;
  213. flex-wrap: wrap;
  214. gap: 16rpx;
  215. padding: 24rpx 30rpx;
  216. }
  217. .member-item {
  218. display: flex;
  219. flex-direction: column;
  220. align-items: center;
  221. padding: 16rpx 24rpx;
  222. border: 1rpx solid #ddd;
  223. border-radius: 16rpx;
  224. background-color: #FAFAFA;
  225. }
  226. .member-selected {
  227. border-color: #FF8C42;
  228. background-color: #FFF5EB;
  229. }
  230. .member-avatar {
  231. width: 60rpx;
  232. height: 60rpx;
  233. border-radius: 50%;
  234. background-color: #FF8C42;
  235. color: #FFFFFF;
  236. display: flex;
  237. align-items: center;
  238. justify-content: center;
  239. font-size: 24rpx;
  240. font-weight: 600;
  241. margin-bottom: 8rpx;
  242. }
  243. .member-name {
  244. font-size: 22rpx;
  245. color: #666;
  246. }
  247. .add-member-btn {
  248. width: 80rpx;
  249. height: 80rpx;
  250. border: 2rpx dashed #ddd;
  251. border-radius: 16rpx;
  252. display: flex;
  253. align-items: center;
  254. justify-content: center;
  255. font-size: 40rpx;
  256. color: #999;
  257. }
  258. .tip-card {
  259. background-color: #E3F2FD;
  260. border-radius: 16rpx;
  261. padding: 24rpx 30rpx;
  262. margin-bottom: 30rpx;
  263. }
  264. .tip-text {
  265. font-size: 24rpx;
  266. color: #1976D2;
  267. line-height: 1.6;
  268. }
  269. .save-area {
  270. padding: 40rpx 30rpx;
  271. }
  272. .save-btn {
  273. background-color: #FF8C42;
  274. color: #FFFFFF;
  275. text-align: center;
  276. padding: 28rpx 0;
  277. border-radius: 16rpx;
  278. font-size: 30rpx;
  279. font-weight: 600;
  280. }
  281. </style>