form-step2.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <template>
  2. <view class="page-container">
  3. <u-navbar title="新建团餐" :autoBack="true" :placeholder="true"></u-navbar>
  4. <u-form :model="form" :rules="rules" ref="formRef" :error-type="['toast', 'border-bottom']">
  5. <u-collapse ref="collapseRef"
  6. :value="activeNames"
  7. @change="onCollapseChange">
  8. <u-collapse-item
  9. :class="{
  10. active: activeNames == category.id
  11. }"
  12. v-for="(category, catIndex) in form.dishCategories"
  13. :key="category.id"
  14. :name="category.id"
  15. @click="onClick"
  16. >
  17. <view slot="title" class="collapse-title">
  18. <text>{{ category.name }}</text>
  19. <u-switch
  20. v-if="false"
  21. size="20"
  22. activeColor="#3c9cff"
  23. :value="category.isActive"
  24. @change="onSwitchChange($event, category.id)"
  25. ></u-switch>
  26. </view>
  27. <view class="collapse-content">
  28. <view class="dish-item" v-for="(dish, dishIndex) in category.items" :key="dish.id">
  29. <view class="dish-header">
  30. <view class="dish-title">
  31. <u-image width="32rpx" height="32rpx" src="/static/icon/icon_edit.png"></u-image>
  32. <text>{{ category.name }}{{ dishIndex + 1 }}</text>
  33. </view>
  34. <view class="dish-actions">
  35. <u-button v-if="dishIndex === category.items.length - 1"
  36. type="primary" size="mini"
  37. hover-class="none"
  38. :custom-style="{
  39. color: '#2D88F4',
  40. border: '1rpx solid #2D88F4',
  41. background: 'rgba(45,136,244,0.1)',
  42. }"
  43. @click="addDish(category.id)"
  44. >
  45. <view class="text">
  46. <u-image width="32rpx" height="32rpx" src="/static/icon/icon_plus.png"></u-image>
  47. <text>添加</text>
  48. </view>
  49. </u-button>
  50. <u-button v-if="category.items.length > 1"
  51. type="error" size="mini"
  52. hover-class="none"
  53. :custom-style="{
  54. color: '#FF6067',
  55. border: '1rpx solid #FF6067',
  56. background: 'rgba(255,96,103,0.1)',
  57. }"
  58. @click="removeDish(category.id, dish.id)"
  59. >
  60. <view class="text">
  61. <u-image width="32rpx" height="32rpx" src="/static/icon/icon_del.png"></u-image>
  62. <text>删除</text>
  63. </view>
  64. </u-button>
  65. </view>
  66. </view>
  67. <view class="dish-form">
  68. <u-form-item label="名称" :prop="`dishCategories.${catIndex}.items.${dishIndex}.name`" :required="isEdited(dish)">
  69. <u-input v-model="dish.name" height="41" placeholder="请输入" inputAlign="right"></u-input>
  70. </u-form-item>
  71. <u-form-item label="价格" :prop="`dishCategories.${catIndex}.items.${dishIndex}.price`" :required="isEdited(dish)">
  72. <u-input v-model="dish.price" type="digit" height="41" placeholder="输入金额" :clearable="false" inputAlign="right"></u-input>
  73. <text slot="right" style="color: #333333;">元</text>
  74. </u-form-item>
  75. <u-form-item label="描述" :prop="`dishCategories.${catIndex}.items.${dishIndex}.description`" :required="isEdited(dish)">
  76. <u-input v-model="dish.description" height="41" placeholder="请描述菜品的使用素材" inputAlign="right"></u-input>
  77. </u-form-item>
  78. </view>
  79. </view>
  80. </view>
  81. </u-collapse-item>
  82. </u-collapse>
  83. </u-form>
  84. <view class="footer-bar">
  85. <u-button
  86. :custom-style="{
  87. width: '205rpx',
  88. fontWeight: '400',
  89. fontSize: '28rpx',
  90. color: '#449AFF',
  91. border: '1rpx solid #449AFF',
  92. background: '#fff',
  93. marginRight: '24rpx'
  94. }"
  95. @click="goBack"
  96. >上一步</u-button>
  97. <u-button
  98. :custom-style="{
  99. fontWeight: '400',
  100. fontSize: '28rpx',
  101. color: '#FFFFFF',
  102. background: 'linear-gradient(90deg, #77B6FF 0%, #449AFF 100%)',
  103. }"
  104. @click="submit"
  105. >提交审核</u-button>
  106. </view>
  107. </view>
  108. </template>
  109. <script>
  110. let dishIdCounter = 100; // 简单的唯一ID生成器
  111. export default {
  112. data() {
  113. return {
  114. // 数据
  115. form: {
  116. dishCategories: [
  117. {
  118. id: 1,
  119. name: '荤菜',
  120. isActive: false,
  121. items: [{ id: dishIdCounter++, name: '', price: '', description: '' }]
  122. },
  123. {
  124. id: 2,
  125. name: '素菜',
  126. isActive: false,
  127. items: [{ id: dishIdCounter++, name: '', price: '', description: '' }]
  128. },
  129. {
  130. id: 3,
  131. name: '主食',
  132. isActive: false,
  133. items: [{ id: dishIdCounter++, name: '', price: '', description: '' }]
  134. },
  135. {
  136. id: 4,
  137. name: '汤',
  138. isActive: false,
  139. items: [{ id: dishIdCounter++, name: '', price: '', description: '' }]
  140. },
  141. {
  142. id: 5,
  143. name: '其他',
  144. isActive: false,
  145. items: [{ id: dishIdCounter++, name: '', price: '', description: '' }]
  146. }
  147. ],
  148. },
  149. rules: {},
  150. activeNames: [1], // 控制折叠面板展开的name数组
  151. };
  152. },
  153. watch: {
  154. 'form.dishCategories': {
  155. handler() {
  156. this.$nextTick(() => {
  157. this.updateValidationRules();
  158. });
  159. },
  160. deep: true
  161. }
  162. },
  163. mounted() {
  164. this.$nextTick(() => {
  165. this.$refs.formRef.setRules(this.rules);
  166. });
  167. },
  168. methods: {
  169. // 判断一个菜品是否被编辑过
  170. isEdited(dish) {
  171. return !!(dish.name || dish.price || dish.description);
  172. },
  173. updateValidationRules() {
  174. if (!this.$refs.formRef) return;
  175. const newRules = {};
  176. this.form.dishCategories.forEach((category, catIndex) => {
  177. category.items.forEach((dish, dishIndex) => {
  178. if (this.isEdited(dish)) {
  179. // 如果菜品的任一字段被填写,则所有字段都变为必填
  180. const nameProp = `dishCategories.${catIndex}.items.${dishIndex}.name`;
  181. const priceProp = `dishCategories.${catIndex}.items.${dishIndex}.price`;
  182. const descProp = `dishCategories.${catIndex}.items.${dishIndex}.description`;
  183. newRules[nameProp] = [{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }];
  184. newRules[priceProp] = [{ required: true, message: '请输入价格', trigger: ['blur', 'change'] }];
  185. newRules[descProp] = [{ required: true, message: '请输入描述', trigger: ['blur', 'change'] }];
  186. }
  187. });
  188. });
  189. this.rules = newRules;
  190. this.$refs.formRef.setRules(this.rules);
  191. },
  192. // Switch开关状态变化
  193. onSwitchChange(isActive, categoryId) {
  194. const index = this.activeNames.indexOf(categoryId);
  195. if (isActive) {
  196. if (index === -1) {
  197. this.activeNames.push(categoryId);
  198. }
  199. } else {
  200. if (index > -1) {
  201. this.activeNames.splice(index, 1);
  202. }
  203. }
  204. },
  205. // 折叠面板状态变化
  206. onCollapseChange(names) {
  207. console.log(names)
  208. this.activeNames = names;
  209. },
  210. // 添加菜品
  211. addDish(categoryId) {
  212. const category = this.form.dishCategories.find(c => c.id === categoryId);
  213. if (category) {
  214. category.items.push({
  215. id: dishIdCounter++,
  216. name: '',
  217. price: '',
  218. description: ''
  219. });
  220. }
  221. },
  222. // 删除菜品
  223. removeDish(categoryId, dishId) {
  224. const category = this.form.dishCategories.find(c => c.id === categoryId);
  225. if (category && category.items.length > 1) {
  226. const index = category.items.findIndex(d => d.id === dishId);
  227. if (index > -1) {
  228. category.items.splice(index, 1);
  229. }
  230. }
  231. },
  232. // 上一步
  233. goBack() {
  234. uni.navigateBack();
  235. },
  236. // 提交审核
  237. submit() {
  238. this.$refs.formRef.validate().then(res => {
  239. const activeCategories = this.form.dishCategories.filter(c => c.items.some(this.isDishEdited));
  240. console.log('表单校验成功, 提交的数据:', activeCategories);
  241. }).catch(errors => {
  242. console.log('校验失败', errors);
  243. });
  244. }
  245. }
  246. };
  247. </script>
  248. <style lang="scss" scoped>
  249. .page-container {
  250. min-height: 100vh;
  251. background-color: #f5f5f5;
  252. padding-bottom: 160rpx;
  253. ::v-deep .u-collapse-item {
  254. margin-bottom: 16rpx;
  255. .u-collapse-head {
  256. padding: 20rpx 32rpx;
  257. background-color: #fff;
  258. }
  259. &.active {
  260. .u-collapse-body {
  261. height: auto !important;
  262. }
  263. }
  264. }
  265. .collapse-title {
  266. font-weight: 500;
  267. font-size: 32rpx;
  268. color: #333333;
  269. display: flex;
  270. align-items: center;
  271. justify-content: space-between;
  272. }
  273. .collapse-content {
  274. .dish-item {
  275. margin-top: 16rpx;
  276. padding: 20rpx 32rpx;
  277. background: #FFFFFF;
  278. box-shadow: 0 2rpx 8rpx 0 rgba(214,225,237,0.1);
  279. border: 1rpx solid #F0F3F5;
  280. border-radius: 16rpx;
  281. .dish-header {
  282. margin-bottom: 20rpx;
  283. display: flex;
  284. align-items: center;
  285. justify-content: space-between;
  286. .dish-title {
  287. font-size: 28rpx;
  288. color: #333333;
  289. display: flex;
  290. align-items: center;
  291. }
  292. .dish-actions {
  293. display: flex;
  294. gap: 16rpx;
  295. ::v-deep .u-btn {
  296. font-weight: 400;
  297. font-size: 22rpx;
  298. }
  299. .text {
  300. display: flex;
  301. align-items: center;
  302. text {
  303. line-height: 1;
  304. margin-left: 4rpx;
  305. }
  306. }
  307. }
  308. }
  309. .dish-form {
  310. padding: 6rpx 16rpx;
  311. border-radius: 8rpx;
  312. background: #F7F8FC;
  313. }
  314. ::v-deep .u-form-item {
  315. padding: 16rpx 0;
  316. line-height: 1;
  317. .u-form-item--left__content {
  318. flex: none;
  319. }
  320. }
  321. }
  322. }
  323. }
  324. .footer-bar {
  325. ::v-deep .u-btn {
  326. width: 100%;
  327. height: 76rpx;
  328. border-radius: 54rpx;
  329. &::after {
  330. display: none;
  331. }
  332. }
  333. }
  334. </style>