box-form.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <template>
  2. <view class="page">
  3. <u-navbar :title="isEdit ? '编辑餐盒' : '新建餐盒'" :autoBack="true" :placeholder="true" :border-bottom="true"></u-navbar>
  4. <view class="section-head">规格1</view>
  5. <view class="form">
  6. <view class="row">
  7. <text class="label">餐盒名称</text>
  8. <input class="input" v-model="form.name" placeholder="请输入" placeholder-class="ph" />
  9. </view>
  10. <view class="row">
  11. <text class="label">餐盒价格</text>
  12. <input class="input" v-model="form.price" type="digit" placeholder="请输入金额 元" placeholder-class="ph" />
  13. </view>
  14. <view class="row link" @click="goBind">
  15. <text class="label">已绑商品</text>
  16. <view class="right">
  17. <text class="action">{{ form.bindCount ? `已绑${form.bindCount}个` : '去绑定' }}</text>
  18. <u-icon name="arrow-right" color="#C7C6CA" size="24"></u-icon>
  19. </view>
  20. </view>
  21. <view class="row link last" @click="pickImage">
  22. <text class="label">餐盒图片</text>
  23. <view class="right">
  24. <image v-if="form.image" class="thumb" :src="form.image" mode="aspectFill" @click.stop="previewImage"></image>
  25. <template v-else>
  26. <text class="action">去上传</text>
  27. <u-icon name="arrow-right" color="#C7C6CA" size="24"></u-icon>
  28. </template>
  29. </view>
  30. </view>
  31. </view>
  32. <view class="footer">
  33. <view :class="['primary-btn', saving ? 'disabled' : '']" @click="onSave">{{ saving ? '保存中...' : '保存' }}</view>
  34. </view>
  35. </view>
  36. </template>
  37. <script>
  38. import configService from '@/common/service/config.service';
  39. import merchantDishApi, { mapMealBoxItem } from '@/api/merchantDish.js';
  40. export default {
  41. data() {
  42. return {
  43. isEdit: false,
  44. saving: false,
  45. uploadAction: configService.apiUrl + '/sys/common/upload',
  46. imageHttp: configService.apiUrl + '/',
  47. form: {
  48. id: null,
  49. name: '',
  50. price: '',
  51. image: '',
  52. bindIds: [],
  53. bindCount: 0,
  54. status: 1,
  55. sort: 0
  56. }
  57. };
  58. },
  59. onLoad(query) {
  60. if (query.id) {
  61. this.isEdit = true;
  62. this.form.id = query.id;
  63. this.loadDetail(query.id);
  64. }
  65. },
  66. methods: {
  67. /** 供绑定页回写选中商品 */
  68. applyBindIds(ids) {
  69. this.form.bindIds = ids || [];
  70. this.form.bindCount = this.form.bindIds.length;
  71. },
  72. loadDetail(id) {
  73. uni.showLoading({ title: '加载中' });
  74. Promise.all([
  75. merchantDishApi.mealBoxDetail(id),
  76. merchantDishApi.mealBoxPackages(id)
  77. ])
  78. .then(([detailRes, pkgRes]) => {
  79. if (detailRes.data.code === 200 && detailRes.data.result) {
  80. const item = mapMealBoxItem(detailRes.data.result);
  81. this.form = {
  82. ...this.form,
  83. id: item.id,
  84. name: item.name || '',
  85. price: item.price != null && item.price !== '' ? String(item.price) : '',
  86. image: item.image || '',
  87. status: item.status != null ? item.status : 1,
  88. sort: item.sort || 0
  89. };
  90. }
  91. if (pkgRes.data.code === 200) {
  92. const pkgs = pkgRes.data.result || [];
  93. this.form.bindIds = pkgs.map((p) => p.packageId).filter(Boolean);
  94. this.form.bindCount = this.form.bindIds.length;
  95. }
  96. })
  97. .finally(() => uni.hideLoading());
  98. },
  99. pickImage() {
  100. uni.chooseImage({
  101. count: 1,
  102. sizeType: ['compressed'],
  103. sourceType: ['album', 'camera'],
  104. success: (res) => {
  105. const filePath = (res.tempFilePaths || [])[0];
  106. if (!filePath) return;
  107. uni.showLoading({ title: '上传中' });
  108. uni.uploadFile({
  109. url: this.uploadAction,
  110. filePath,
  111. name: 'file',
  112. formData: { biz: 'temp' },
  113. success: (uploadRes) => {
  114. try {
  115. const data = typeof uploadRes.data === 'string' ? JSON.parse(uploadRes.data) : uploadRes.data;
  116. if (data && data.success !== false && data.message) {
  117. this.form.image = this.imageHttp + data.message;
  118. } else if (data && data.url) {
  119. this.form.image = data.url;
  120. } else {
  121. this.$tip.toast((data && data.message) || '上传失败');
  122. }
  123. } catch (e) {
  124. this.$tip.toast('上传失败');
  125. }
  126. },
  127. fail: () => {
  128. this.$tip.toast('上传失败');
  129. },
  130. complete: () => uni.hideLoading()
  131. });
  132. }
  133. });
  134. },
  135. previewImage() {
  136. if (!this.form.image) return;
  137. uni.previewImage({ urls: [this.form.image] });
  138. },
  139. goBind() {
  140. const boxId = this.form.id || 'new';
  141. uni.navigateTo({
  142. url: `/pages/merchantDish/settings/box-bind?id=${boxId}&from=form`,
  143. success: (res) => {
  144. res.eventChannel.emit('initBind', {
  145. bindIds: this.form.bindIds || []
  146. });
  147. },
  148. events: {
  149. onBindConfirm: (data) => {
  150. this.applyBindIds((data && data.bindIds) || []);
  151. }
  152. }
  153. });
  154. },
  155. onSave() {
  156. if (!this.form.name) {
  157. uni.showToast({ title: '请输入餐盒名称', icon: 'none' });
  158. return;
  159. }
  160. if (this.saving) return;
  161. this.saving = true;
  162. const packageIds = this.form.bindIds || [];
  163. const base = {
  164. boxName: this.form.name,
  165. boxPrice: Number(this.form.price || 0),
  166. boxImage: this.form.image || '',
  167. status: this.form.status != null ? this.form.status : 1,
  168. sort: this.form.sort || 0
  169. };
  170. const done = (res) => {
  171. const ok = res.data.code === 200;
  172. this.$tip.toast(res.data.message || (ok ? '保存成功' : '保存失败'));
  173. if (ok) setTimeout(() => uni.navigateBack(), 400);
  174. };
  175. if (!this.isEdit) {
  176. merchantDishApi
  177. .mealBoxAdd({ ...base, packageIds })
  178. .then(done)
  179. .finally(() => {
  180. this.saving = false;
  181. });
  182. return;
  183. }
  184. merchantDishApi
  185. .mealBoxEdit({ id: this.form.id, ...base })
  186. .then((res) => {
  187. if (res.data.code !== 200) {
  188. done(res);
  189. return null;
  190. }
  191. return merchantDishApi.mealBoxBind(this.form.id, packageIds);
  192. })
  193. .then((res) => {
  194. if (res) done(res);
  195. })
  196. .finally(() => {
  197. this.saving = false;
  198. });
  199. }
  200. }
  201. };
  202. </script>
  203. <style lang="scss" scoped>
  204. .page {
  205. min-height: 100vh;
  206. background: #f7f8fa;
  207. padding-bottom: 160rpx;
  208. }
  209. .section-head {
  210. padding: 20rpx 24rpx;
  211. font-size: 30rpx;
  212. font-weight: 600;
  213. color: #333;
  214. background: #eef3f8;
  215. }
  216. .form {
  217. background: #fff;
  218. }
  219. .row {
  220. display: flex;
  221. align-items: center;
  222. min-height: 96rpx;
  223. padding: 0 24rpx;
  224. border-bottom: 1rpx solid #f0f0f0;
  225. &.last {
  226. border-bottom: none;
  227. }
  228. &.link:active {
  229. background: #fafafa;
  230. }
  231. .label {
  232. width: 180rpx;
  233. font-size: 28rpx;
  234. color: #333;
  235. flex-shrink: 0;
  236. }
  237. .input {
  238. flex: 1;
  239. text-align: right;
  240. font-size: 28rpx;
  241. color: #333;
  242. }
  243. .right {
  244. flex: 1;
  245. display: flex;
  246. align-items: center;
  247. justify-content: flex-end;
  248. }
  249. .action {
  250. font-size: 28rpx;
  251. color: #999;
  252. margin-right: 4rpx;
  253. }
  254. .thumb {
  255. width: 72rpx;
  256. height: 72rpx;
  257. border-radius: 8rpx;
  258. background: #eee;
  259. }
  260. }
  261. .ph {
  262. color: #c0c4cc;
  263. }
  264. .footer {
  265. position: fixed;
  266. left: 0;
  267. right: 0;
  268. bottom: 0;
  269. z-index: 20;
  270. padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
  271. background: #fff;
  272. }
  273. .primary-btn {
  274. height: 88rpx;
  275. line-height: 88rpx;
  276. text-align: center;
  277. color: #fff;
  278. font-size: 32rpx;
  279. border-radius: 44rpx;
  280. background: linear-gradient( 89deg, #10ABFE 0%, #2260F6 100%);
  281. &.disabled {
  282. opacity: 0.6;
  283. }
  284. }
  285. </style>