DishOrderDetail.vue 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  1. <template>
  2. <view class="dish-order-detail">
  3. <view class="order-header">
  4. <view class="room-info">
  5. <view class="room-name">{{ currentRoom.roomName || '-' }}</view>
  6. <view class="room-no">包房号:{{ currentRoom.roomNo || '-' }}</view>
  7. </view>
  8. <view class="header-actions">
  9. <button v-if="hasCurrentOrder" class="mini-btn success" @click="goAddDish">加菜</button>
  10. <button v-if="hasCurrentOrder" class="mini-btn warning" @click="openReturnDishDialog">退菜</button>
  11. <button class="mini-btn primary" @click="backRoomList">返回包房</button>
  12. </view>
  13. </view>
  14. <view class="order-main">
  15. <view v-if="!hasCurrentOrder" class="dish-panel">
  16. <view class="dish-search">
  17. <input class="search-input" v-model="dishSearchName" placeholder="搜索菜品名称" confirm-type="search"
  18. @confirm="searchDish" />
  19. <button class="search-btn" @click="searchDish">搜索</button>
  20. </view>
  21. <view class="dish-content">
  22. <scroll-view scroll-y class="category-list">
  23. <view v-for="item in categoryList" :key="item.id" class="category-item"
  24. :class="{ active: activeTypeId === item.id }" @click="changeType(item)">
  25. {{ item.name }}
  26. </view>
  27. </scroll-view>
  28. <scroll-view scroll-y class="dish-list">
  29. <view v-if="dishLoading" class="empty-box">加载中...</view>
  30. <view v-else-if="dishList.length === 0" class="empty-box">暂无菜品</view>
  31. <view v-else class="dish-grid">
  32. <view v-for="item in dishList" :key="item.id" class="dish-card">
  33. <image v-if="item.previewImageUrl" class="dish-image" :src="item.previewImageUrl"
  34. mode="aspectFill"></image>
  35. <view v-else class="dish-image dish-image-empty">暂无图片</view>
  36. <view class="dish-info">
  37. <view class="dish-name">{{ item.dishName }}</view>
  38. <view class="dish-desc">{{ item.spec || item.taste || item.remarks || '暂无说明' }}</view>
  39. <view class="dish-bottom">
  40. <text class="dish-price">¥{{ formatMoney(item.salePrice) }}</text>
  41. <button class="plain-btn" @click="addDish(item)">点菜</button>
  42. </view>
  43. </view>
  44. </view>
  45. </view>
  46. </scroll-view>
  47. </view>
  48. </view>
  49. <view class="ordered-panel" :class="{ full: hasCurrentOrder }">
  50. <view class="ordered-title">
  51. <text>已点菜</text>
  52. <view v-if="hasCurrentOrder" class="ordered-title-actions">
  53. <button class="text-btn" @click="openOrderInfoDialog">订单详情</button>
  54. <button class="text-btn danger" @click="confirmCancelOrder">取消订单</button>
  55. </view>
  56. </view>
  57. <scroll-view scroll-y class="ordered-list">
  58. <view v-if="orderLoading" class="ordered-empty">加载中...</view>
  59. <view v-else-if="orderedList.length === 0" class="ordered-empty">
  60. <view class="ordered-empty-title">暂未点菜</view>
  61. <view class="ordered-empty-sub">从左侧选择菜品后会显示在这里</view>
  62. </view>
  63. <view v-else>
  64. <view class="ordered-head">
  65. <text>菜品</text>
  66. <text>单价</text>
  67. <text>总价</text>
  68. <text>数量</text>
  69. </view>
  70. <view v-for="item in orderedList" :key="item.dishId" class="ordered-item"
  71. :class="{ pending: item.pendingQuantity !== 0 }">
  72. <view class="ordered-dish">
  73. <view class="ordered-dish-name">{{ item.dishName }}</view>
  74. </view>
  75. <view class="ordered-price">¥{{ formatMoney(item.salePrice) }}</view>
  76. <view class="ordered-total">¥{{ formatMoney(itemTotal(item)) }}</view>
  77. <view class="ordered-qty">
  78. <button v-if="!hasCurrentOrder" class="qty-btn" @click="reduceDish(item)">-</button>
  79. <text>{{ item.quantity }}</text>
  80. <button v-if="!hasCurrentOrder" class="qty-btn" @click="increaseOrderedDish(item)">+</button>
  81. </view>
  82. </view>
  83. </view>
  84. </scroll-view>
  85. <view class="ordered-footer">
  86. <view class="ordered-summary">
  87. <text>共 {{ orderedList.length }} 项,总价</text>
  88. <strong>¥{{ formatMoney(orderTotal) }}</strong>
  89. </view>
  90. <view class="ordered-actions" :class="{ one: hasCurrentOrder && !hasChangedDish }">
  91. <button v-if="hasCurrentOrder && !hasChangedDish" class="footer-btn success" :disabled="submitting"
  92. @click="openSettleDialog">结账</button>
  93. <button v-if="!hasCurrentOrder || hasChangedDish" class="footer-btn primary" :disabled="submitting"
  94. @click="confirmSubmitOrder">下单</button>
  95. </view>
  96. </view>
  97. </view>
  98. </view>
  99. <view v-if="settleDialogVisible" class="modal-mask">
  100. <view class="modal-card settle-card">
  101. <view class="modal-title">订单结账</view>
  102. <view class="settle-tabs">
  103. <view class="settle-tab" :class="{ active: settleForm.settleType === '0' }"
  104. @click="settleForm.settleType = '0'">结账</view>
  105. <view class="settle-tab" :class="{ active: settleForm.settleType === '1' }"
  106. @click="settleForm.settleType = '1'">折扣结账</view>
  107. <view class="settle-tab" :class="{ active: settleForm.settleType === '2' }"
  108. @click="settleForm.settleType = '2'">优惠结账</view>
  109. </view>
  110. <view v-if="settleForm.settleType === '0'" class="settle-normal">
  111. <text>订单金额</text>
  112. <strong>¥{{ formatMoney(orderTotal) }}</strong>
  113. </view>
  114. <view v-else class="settle-preview">
  115. <view>
  116. <text>账单原金额</text>
  117. <strong>¥{{ formatMoney(orderTotal) }}</strong>
  118. </view>
  119. <view v-if="settleForm.settleType === '1'">
  120. <text>折扣比例</text>
  121. <strong>{{ formatMoney(settleForm.discountRate) }}%</strong>
  122. </view>
  123. <view v-else>
  124. <text>优惠金额</text>
  125. <strong>¥{{ formatMoney(settleDiscountAmount) }}</strong>
  126. </view>
  127. <view>
  128. <text>应收金额</text>
  129. <strong class="danger-text">¥{{ formatMoney(settlePayableAmount) }}</strong>
  130. </view>
  131. </view>
  132. <view v-if="settleForm.settleType === '1'" class="form-row">
  133. <text>折扣比例</text>
  134. <input type="number" v-model="settleForm.discountRate" placeholder="请输入折扣比例" />
  135. </view>
  136. <view v-if="settleForm.settleType === '2'" class="form-row">
  137. <text>优惠金额</text>
  138. <input type="number" v-model="settleForm.discountAmount" placeholder="请输入优惠金额" />
  139. </view>
  140. <view class="modal-actions">
  141. <button class="modal-btn" @click="settleDialogVisible = false">取消</button>
  142. <button class="modal-btn primary" :disabled="submitting" @click="handleSettleConfirm">确认结账</button>
  143. </view>
  144. </view>
  145. </view>
  146. <view v-if="confirmOrderDialogVisible" class="modal-mask">
  147. <view class="modal-card">
  148. <view class="modal-title">确认订单</view>
  149. <view class="confirm-summary">
  150. <view><text>包间名</text><strong>{{ currentRoom.roomName || '-' }}</strong></view>
  151. <view><text>下单时间</text><strong>{{ confirmOrderTime || '-' }}</strong></view>
  152. </view>
  153. <view class="textarea-row">
  154. <text>备注</text>
  155. <textarea v-model="confirmOrderForm.tasteRemark" maxlength="500" placeholder="请输入备注"></textarea>
  156. </view>
  157. <view class="special-requirement-row">
  158. <text>特殊要求</text>
  159. <u-checkbox-group v-model="selectedSpecialRequirementValues" placement="column"
  160. @change="handleSpecialRequirementChange">
  161. <u-checkbox v-for="(item, index) in $dictUtils.getDictList('special_requirement')" :key="index"
  162. :customStyle="{ margin: '6rpx 24rpx 6rpx 0' }" :label="item.label || item.value"
  163. :name="String(item.value)"></u-checkbox>
  164. </u-checkbox-group>
  165. </view>
  166. <view class="modal-actions">
  167. <button class="modal-btn" @click="continueOrderDish">继续点菜</button>
  168. <button class="modal-btn primary" :disabled="submitting" @click="handleConfirmOrderSubmit">确认下单</button>
  169. </view>
  170. </view>
  171. </view>
  172. <view v-if="returnDishDialogVisible" class="modal-mask">
  173. <view class="modal-card return-card">
  174. <view class="modal-title">退菜</view>
  175. <view class="confirm-summary">
  176. <view><text>包房名</text><strong>{{ currentRoom.roomName || '-' }}</strong></view>
  177. <view><text>下单时间</text><strong>{{ currentOrder && currentOrder.createTime ? currentOrder.createTime : '-' }}</strong></view>
  178. </view>
  179. <scroll-view scroll-y class="return-list">
  180. <view class="return-head">
  181. <text>序号</text><text>菜品名</text><text>现有数量</text><text>退菜数量</text>
  182. </view>
  183. <view v-for="(item, index) in returnDishList" :key="item.dishId" class="return-row">
  184. <text>{{ index + 1 }}</text>
  185. <text class="return-name">{{ item.dishName }}</text>
  186. <text>{{ item.quantity }}</text>
  187. <input type="number" v-model="item.returnQuantity" @blur="normalizeReturnQuantity(item)" />
  188. </view>
  189. </scroll-view>
  190. <view class="textarea-row">
  191. <text>退菜原因</text>
  192. <textarea v-model="returnDishForm.reason" maxlength="500" placeholder="请输入退菜原因"></textarea>
  193. </view>
  194. <view class="modal-actions">
  195. <button class="modal-btn" @click="returnDishDialogVisible = false">取消</button>
  196. <button class="modal-btn primary" :disabled="submitting" @click="handleReturnDishConfirm">确认退菜</button>
  197. </view>
  198. </view>
  199. </view>
  200. <view v-if="orderInfoDialogVisible" class="modal-mask">
  201. <view class="modal-card info-card">
  202. <view class="modal-title">订单详情</view>
  203. <view class="info-summary">
  204. <view><text>订单编号</text><strong>{{ currentOrder.orderNo || '-' }}</strong></view>
  205. <view><text>结账方式</text><strong>{{ settleTypeName(currentOrder.settleType) }}</strong></view>
  206. <view><text>账单原金额</text><strong>¥{{ formatMoney(currentOrder.totalAmount) }}</strong></view>
  207. <view><text>实收金额</text><strong>¥{{ formatMoney(currentOrder.payableAmount) }}</strong></view>
  208. <view class="wide"><text>备注</text><strong>{{ currentOrder.tasteRemark || '-' }}</strong></view>
  209. </view>
  210. <view class="settle-tabs">
  211. <view class="settle-tab" :class="{ active: infoTab === 'actual' }" @click="infoTab = 'actual'">实际菜单</view>
  212. <view class="settle-tab" :class="{ active: infoTab === 'add' }" @click="infoTab = 'add'">加菜</view>
  213. <view class="settle-tab" :class="{ active: infoTab === 'reduce' }" @click="infoTab = 'reduce'">退菜</view>
  214. </view>
  215. <scroll-view scroll-y class="info-list">
  216. <view v-if="infoDetailList.length === 0" class="empty-box">暂无菜品</view>
  217. <view v-for="(item, index) in infoDetailList" :key="item.id || index" class="info-row">
  218. <view>
  219. <view class="info-dish-name">{{ item.dishName || '-' }}</view>
  220. <view class="info-dish-sub">单价 ¥{{ formatMoney(item.salePrice) }} / 数量 {{ displayQuantity(item) }}</view>
  221. <view v-if="infoTab === 'reduce' && item.reason" class="info-dish-sub">退菜原因:{{ item.reason }}</view>
  222. </view>
  223. <strong>¥{{ formatMoney(displayAmount(item)) }}</strong>
  224. </view>
  225. </scroll-view>
  226. <view class="info-total">共 {{ infoDetailList.length }} 项 / {{ infoQuantity }} 份,总价 ¥{{ formatMoney(infoAmount) }}</view>
  227. <view class="modal-actions">
  228. <button class="modal-btn primary" @click="orderInfoDialogVisible = false">关闭</button>
  229. </view>
  230. </view>
  231. </view>
  232. </view>
  233. </template>
  234. <script>
  235. import DishOrderService from '@/api/psi/DishOrderService'
  236. import OSSSerivce from '@/api/sys/OSSService'
  237. export default {
  238. data() {
  239. return {
  240. roomId: '',
  241. currentRoom: {},
  242. currentOrder: null,
  243. categoryList: [],
  244. activeTypeId: 'all',
  245. dishSearchName: '',
  246. dishList: [],
  247. orderedList: [],
  248. dishLoading: false,
  249. orderLoading: false,
  250. submitting: false,
  251. settleDialogVisible: false,
  252. confirmOrderDialogVisible: false,
  253. returnDishDialogVisible: false,
  254. orderInfoDialogVisible: false,
  255. infoTab: 'actual',
  256. pendingOrderSubmit: null,
  257. confirmOrderTime: '',
  258. confirmOrderForm: {
  259. tasteRemark: ''
  260. },
  261. selectedSpecialRequirementValues: [],
  262. lastSpecialRequirementValues: [],
  263. settleForm: {
  264. settleType: '0',
  265. discountRate: 100,
  266. discountAmount: 0
  267. },
  268. returnDishForm: {
  269. reason: ''
  270. },
  271. returnDishList: [],
  272. dishOrderService: null,
  273. ossService: null
  274. }
  275. },
  276. computed: {
  277. orderTotal() {
  278. return this.orderedList.reduce((total, item) => total + this.itemTotal(item), 0)
  279. },
  280. orderQuantity() {
  281. return this.orderedList.reduce((total, item) => total + Number(item.quantity || 0), 0)
  282. },
  283. changedDetailList() {
  284. return this.orderedList.filter(item => item.pendingQuantity !== 0)
  285. },
  286. hasChangedDish() {
  287. return this.changedDetailList.length > 0
  288. },
  289. hasCurrentOrder() {
  290. return !!(this.currentOrder && this.currentOrder.id)
  291. },
  292. submitDetailList() {
  293. return this.orderedList.map((item, index) => ({
  294. dishId: item.dishId,
  295. dishName: item.dishName,
  296. quantity: item.pendingQuantity,
  297. dishSort: index + 1
  298. }))
  299. },
  300. settleDiscountAmount() {
  301. if (this.settleForm.settleType === '1') {
  302. return this.orderTotal - this.settlePayableAmount
  303. }
  304. if (this.settleForm.settleType === '2') {
  305. return Math.min(Number(this.settleForm.discountAmount || 0), this.orderTotal)
  306. }
  307. return 0
  308. },
  309. settlePayableAmount() {
  310. if (this.settleForm.settleType === '1') {
  311. const rate = Math.min(Math.max(Number(this.settleForm.discountRate || 0), 0), 100)
  312. return this.orderTotal * rate / 100
  313. }
  314. if (this.settleForm.settleType === '2') {
  315. return Math.max(this.orderTotal - Number(this.settleForm.discountAmount || 0), 0)
  316. }
  317. return this.orderTotal
  318. },
  319. infoDetailList() {
  320. const order = this.currentOrder || {}
  321. if (this.infoTab === 'add') {
  322. return Array.isArray(order.addDishList) ? order.addDishList : []
  323. }
  324. if (this.infoTab === 'reduce') {
  325. return Array.isArray(order.reduceDishList) ? order.reduceDishList : []
  326. }
  327. return Array.isArray(order.detailList) ? order.detailList : []
  328. },
  329. infoQuantity() {
  330. return this.infoDetailList.reduce((total, item) => total + this.displayQuantity(item), 0)
  331. },
  332. infoAmount() {
  333. return this.infoDetailList.reduce((total, item) => total + this.displayAmount(item), 0)
  334. }
  335. },
  336. onLoad(option) {
  337. this.roomId = option && option.roomId ? option.roomId : ''
  338. uni.setNavigationBarTitle({ title: '点菜' })
  339. this.dishOrderService = new DishOrderService()
  340. this.ossService = new OSSSerivce()
  341. this.initPage()
  342. },
  343. onShow() {
  344. if (this.roomId && this.dishOrderService) {
  345. this.loadRoom(this.roomId)
  346. this.loadCurrentOrder(this.roomId)
  347. }
  348. },
  349. methods: {
  350. initPage() {
  351. if (!this.roomId) {
  352. this.backRoomList()
  353. return
  354. }
  355. this.loadRoom(this.roomId)
  356. this.loadCurrentOrder(this.roomId)
  357. this.activeTypeId = 'all'
  358. this.dishSearchName = ''
  359. this.loadCategoryList()
  360. this.loadDishList()
  361. },
  362. loadRoom(roomId) {
  363. this.dishOrderService.findRoomById(roomId).then((data) => {
  364. this.currentRoom = data || {}
  365. })
  366. },
  367. loadCurrentOrder(roomId) {
  368. this.orderLoading = true
  369. this.dishOrderService.currentOrder(roomId).then((data) => {
  370. this.currentOrder = data || null
  371. const detailList = data && data.detailList ? data.detailList : []
  372. const orderedDetailList = [...detailList].sort((a, b) => Number(a.dishSort || 999999) - Number(b.dishSort || 999999))
  373. this.orderedList = orderedDetailList.map(item => this.toOrderedItem(item, item.quantity || 0, 0))
  374. }).finally(() => {
  375. this.orderLoading = false
  376. })
  377. },
  378. loadCategoryList() {
  379. this.dishOrderService.typeList().then((data) => {
  380. this.categoryList = [
  381. { id: 'all', name: '所有菜品' },
  382. ...(Array.isArray(data) ? data.map(item => ({ id: item.id, name: item.name })) : [])
  383. ]
  384. })
  385. },
  386. changeType(item) {
  387. this.activeTypeId = item.id
  388. this.dishSearchName = ''
  389. this.loadDishList()
  390. },
  391. searchDish() {
  392. this.activeTypeId = 'all'
  393. this.loadDishList()
  394. },
  395. loadDishList() {
  396. this.dishLoading = true
  397. this.dishOrderService.dishList({
  398. typeId: this.activeTypeId === 'all' ? '' : this.activeTypeId,
  399. dishName: this.dishSearchName
  400. }).then((data) => {
  401. this.dishList = Array.isArray(data) ? data : []
  402. this.loadDishImagePreview()
  403. }).finally(() => {
  404. this.dishLoading = false
  405. })
  406. },
  407. addDish(dish) {
  408. const row = this.orderedList.find(item => item.dishId === dish.id)
  409. if (row) {
  410. row.quantity += 1
  411. row.pendingQuantity += 1
  412. return
  413. }
  414. this.orderedList.push(this.toOrderedItem(dish, 1, 1))
  415. },
  416. increaseOrderedDish(item) {
  417. item.quantity += 1
  418. item.pendingQuantity += 1
  419. },
  420. reduceDish(item) {
  421. if (item.quantity <= 0) {
  422. return
  423. }
  424. item.quantity -= 1
  425. item.pendingQuantity -= 1
  426. if (item.quantity === 0 && item.baseQuantity === 0) {
  427. this.orderedList = this.orderedList.filter(row => row.dishId !== item.dishId)
  428. }
  429. },
  430. openSettleDialog() {
  431. if (!this.hasCurrentOrder && this.changedDetailList.length === 0) {
  432. uni.showToast({ title: '请先选择菜品', icon: 'none' })
  433. return
  434. }
  435. this.settleForm = {
  436. settleType: '0',
  437. discountRate: 100,
  438. discountAmount: 0
  439. }
  440. this.settleDialogVisible = true
  441. },
  442. handleSettleConfirm() {
  443. const settleInfo = {
  444. settleType: this.settleForm.settleType,
  445. discountRate: this.settleForm.settleType === '1' ? this.settleForm.discountRate : 100,
  446. discountAmount: this.settleForm.settleType === '2' ? this.settleForm.discountAmount : this.settleDiscountAmount
  447. }
  448. if (!this.hasCurrentOrder) {
  449. this.settleDialogVisible = false
  450. this.openConfirmOrderDialog('1', settleInfo, false)
  451. return
  452. }
  453. this.submitOrder('1', settleInfo, false)
  454. },
  455. confirmSubmitOrder() {
  456. if (this.changedDetailList.length === 0) {
  457. uni.showToast({ title: '请先选择菜品', icon: 'none' })
  458. return
  459. }
  460. if (!this.hasCurrentOrder) {
  461. this.openConfirmOrderDialog('0', {}, true)
  462. return
  463. }
  464. uni.showModal({
  465. title: '提示',
  466. content: '是否确认下单?',
  467. success: (res) => {
  468. if (res.confirm) {
  469. this.submitOrder('0', {}, true)
  470. }
  471. }
  472. })
  473. },
  474. openConfirmOrderDialog(settleFlag, settleInfo = {}, requireChanged = false) {
  475. this.confirmOrderTime = this.formatDateTime(new Date())
  476. this.confirmOrderForm = {
  477. tasteRemark: ''
  478. }
  479. this.selectedSpecialRequirementValues = []
  480. this.lastSpecialRequirementValues = []
  481. this.pendingOrderSubmit = { settleFlag, settleInfo, requireChanged }
  482. this.confirmOrderDialogVisible = true
  483. },
  484. continueOrderDish() {
  485. this.confirmOrderDialogVisible = false
  486. },
  487. handleConfirmOrderSubmit() {
  488. const pending = this.pendingOrderSubmit || {}
  489. this.submitOrder(pending.settleFlag || '0', pending.settleInfo || {}, pending.requireChanged, {
  490. tasteRemark: this.confirmOrderForm.tasteRemark,
  491. specialRequirements: ''
  492. })
  493. },
  494. handleSpecialRequirementChange(value) {
  495. const current = Array.isArray(value) ? value.map(item => String(item)) : []
  496. const previous = this.lastSpecialRequirementValues || []
  497. current.filter(item => !previous.includes(item)).forEach(item => this.appendRemarkText(this.getSpecialRequirementLabel(item)))
  498. previous.filter(item => !current.includes(item)).forEach(item => this.removeRemarkText(this.getSpecialRequirementLabel(item)))
  499. this.lastSpecialRequirementValues = [...current]
  500. },
  501. getSpecialRequirementLabel(value) {
  502. const list = this.$dictUtils.getDictList('special_requirement') || []
  503. const row = list.find(item => String(item.value) === String(value))
  504. return row ? (row.label || row.value) : value
  505. },
  506. appendRemarkText(text) {
  507. if (!text) {
  508. return
  509. }
  510. const items = this.getRemarkItems()
  511. if (!items.includes(text)) {
  512. items.push(text)
  513. }
  514. this.confirmOrderForm.tasteRemark = items.join(',')
  515. },
  516. removeRemarkText(text) {
  517. if (!text) {
  518. return
  519. }
  520. let value = this.confirmOrderForm.tasteRemark || ''
  521. if (value.indexOf(`,${text}`) > -1) {
  522. value = value.split(`,${text}`).join('')
  523. } else if (value.indexOf(`${text},`) > -1) {
  524. value = value.split(`${text},`).join('')
  525. } else {
  526. value = value.split(text).join('')
  527. }
  528. this.confirmOrderForm.tasteRemark = this.cleanupRemarkText(value)
  529. },
  530. cleanupRemarkText(value) {
  531. return (value || '').split(',').map(item => item.trim()).filter(item => item).join(',')
  532. },
  533. getRemarkItems() {
  534. return (this.confirmOrderForm.tasteRemark || '').split(',').map(item => item.trim()).filter(item => item)
  535. },
  536. openReturnDishDialog() {
  537. if (!this.hasCurrentOrder) {
  538. return
  539. }
  540. this.returnDishForm = { reason: '' }
  541. this.returnDishList = this.orderedList.map(item => ({
  542. dishId: item.dishId,
  543. dishName: item.dishName,
  544. quantity: item.quantity,
  545. dishSort: item.dishSort,
  546. returnQuantity: 0
  547. }))
  548. this.returnDishDialogVisible = true
  549. },
  550. normalizeReturnQuantity(item) {
  551. let value = Number(item.returnQuantity || 0)
  552. value = Math.max(0, Math.min(value, Number(item.quantity || 0)))
  553. item.returnQuantity = Math.floor(value)
  554. },
  555. handleReturnDishConfirm() {
  556. const detailList = this.returnDishList
  557. .filter(item => Number(item.returnQuantity || 0) > 0)
  558. .map(item => ({
  559. dishId: item.dishId,
  560. dishName: item.dishName,
  561. quantity: -Number(item.returnQuantity || 0),
  562. dishSort: item.dishSort,
  563. reason: this.returnDishForm.reason
  564. }))
  565. if (detailList.length === 0) {
  566. uni.showToast({ title: '请填写退菜数量', icon: 'none' })
  567. return
  568. }
  569. this.submitting = true
  570. this.dishOrderService.submit({
  571. id: this.currentOrder.id,
  572. roomId: this.roomId,
  573. settleFlag: '0',
  574. detailList
  575. }).then((data) => {
  576. uni.showToast({ title: data || '退菜成功', icon: 'none' })
  577. this.returnDishDialogVisible = false
  578. this.loadRoom(this.roomId)
  579. this.loadCurrentOrder(this.roomId)
  580. }).finally(() => {
  581. this.submitting = false
  582. })
  583. },
  584. confirmCancelOrder() {
  585. if (!this.hasCurrentOrder) {
  586. uni.showToast({ title: '暂无可取消的订单', icon: 'none' })
  587. return
  588. }
  589. uni.showModal({
  590. title: '提示',
  591. content: '确定取消当前订单吗?取消后包房将释放。',
  592. success: (res) => {
  593. if (!res.confirm) {
  594. return
  595. }
  596. this.submitting = true
  597. this.dishOrderService.cancelOrder(this.currentOrder.id).then((data) => {
  598. uni.showToast({ title: data || '取消成功', icon: 'none' })
  599. this.backRoomList()
  600. }).finally(() => {
  601. this.submitting = false
  602. })
  603. }
  604. })
  605. },
  606. submitOrder(settleFlag, settleInfo = {}, requireChanged = false, orderInfo = {}) {
  607. const changedList = this.changedDetailList
  608. const detailList = settleFlag === '1' && changedList.length === 0 ? [] : this.submitDetailList
  609. if (requireChanged && changedList.length === 0) {
  610. uni.showToast({ title: '请先选择菜品', icon: 'none' })
  611. return
  612. }
  613. if (settleFlag === '1' && !this.hasCurrentOrder && changedList.length === 0) {
  614. uni.showToast({ title: '请先选择菜品', icon: 'none' })
  615. return
  616. }
  617. if (settleFlag !== '1' && detailList.length === 0) {
  618. uni.showToast({ title: '请先选择菜品', icon: 'none' })
  619. return
  620. }
  621. this.submitting = true
  622. this.dishOrderService.submit({
  623. id: this.hasCurrentOrder ? this.currentOrder.id : '',
  624. roomId: this.roomId,
  625. settleFlag,
  626. ...settleInfo,
  627. ...orderInfo,
  628. detailList
  629. }).then((data) => {
  630. uni.showToast({ title: data || '操作成功', icon: 'none' })
  631. this.settleDialogVisible = false
  632. this.confirmOrderDialogVisible = false
  633. this.loadRoom(this.roomId)
  634. this.loadCurrentOrder(this.roomId)
  635. }).finally(() => {
  636. this.submitting = false
  637. })
  638. },
  639. goAddDish() {
  640. if (!this.hasCurrentOrder) {
  641. return
  642. }
  643. uni.navigateTo({
  644. url: `/pages/psiManagement/dishManage/DishOrderAdd?roomId=${this.roomId}&orderId=${this.currentOrder.id}`
  645. })
  646. },
  647. openOrderInfoDialog() {
  648. this.infoTab = 'actual'
  649. this.orderInfoDialogVisible = true
  650. },
  651. backRoomList() {
  652. uni.navigateBack({
  653. fail: () => {
  654. uni.redirectTo({ url: '/pages/psiManagement/dishManage/DishOrder' })
  655. }
  656. })
  657. },
  658. toOrderedItem(item, quantity, pendingQuantity) {
  659. const salePrice = Number(item.salePrice || 0)
  660. return {
  661. dishId: item.dishId || item.id,
  662. dishName: item.dishName,
  663. salePrice,
  664. quantity,
  665. baseQuantity: quantity - pendingQuantity,
  666. pendingQuantity,
  667. dishSort: item.dishSort || 0
  668. }
  669. },
  670. itemTotal(item) {
  671. return Number(item.salePrice || 0) * Number(item.quantity || 0)
  672. },
  673. loadDishImagePreview() {
  674. this.dishList.forEach((row) => {
  675. const urls = this.getImageUrls(row.imageUrl)
  676. if (urls.length === 0) {
  677. this.$set ? this.$set(row, 'previewImageUrl', '') : row.previewImageUrl = ''
  678. this.$set ? this.$set(row, 'previewImageList', []) : row.previewImageList = []
  679. return
  680. }
  681. this.$set ? this.$set(row, 'previewImageUrl', urls[0]) : row.previewImageUrl = urls[0]
  682. this.$set ? this.$set(row, 'previewImageList', urls) : row.previewImageList = urls
  683. Promise.all(urls.map(url => this.getPreviewUrl(url))).then((list) => {
  684. row.previewImageUrl = list[0]
  685. row.previewImageList = list
  686. })
  687. })
  688. },
  689. getImageUrls(value) {
  690. if (!value) {
  691. return []
  692. }
  693. return value.split(',').map(item => item.trim()).filter(item => item)
  694. },
  695. getPreviewUrl(url) {
  696. if (!url) {
  697. return Promise.resolve('')
  698. }
  699. if (url.indexOf('http') === 0) {
  700. return Promise.resolve(url)
  701. }
  702. return this.ossService.getTemporaryUrl(url).catch(() => url)
  703. },
  704. formatMoney(value) {
  705. return Number(value || 0).toFixed(2)
  706. },
  707. formatDateTime(value) {
  708. const date = value ? new Date(value) : new Date()
  709. const pad = (num) => String(num).padStart(2, '0')
  710. return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
  711. },
  712. settleTypeName(value) {
  713. if (value === '1') return '折扣结账'
  714. if (value === '2') return '优惠结账'
  715. if (value === '0') return '结账'
  716. return '-'
  717. },
  718. displayQuantity(item) {
  719. return Math.abs(Number(item.quantity || 0))
  720. },
  721. displayAmount(item) {
  722. return Math.abs(Number(item.amount || 0))
  723. }
  724. }
  725. }
  726. </script>
  727. <style scoped>
  728. .dish-order-detail {
  729. height: calc(100vh - var(--window-top, 0px));
  730. box-sizing: border-box;
  731. display: flex;
  732. flex-direction: column;
  733. overflow: hidden;
  734. padding: 20rpx;
  735. background: #f5f6fa;
  736. }
  737. .order-header {
  738. height: 92rpx;
  739. display: flex;
  740. align-items: center;
  741. justify-content: space-between;
  742. padding: 0 8rpx 18rpx;
  743. box-sizing: border-box;
  744. border-bottom: 1rpx solid #ebeef5;
  745. }
  746. .room-name {
  747. font-size: 34rpx;
  748. font-weight: 700;
  749. color: #303133;
  750. line-height: 44rpx;
  751. }
  752. .room-no {
  753. font-size: 24rpx;
  754. color: #909399;
  755. line-height: 34rpx;
  756. }
  757. .header-actions {
  758. display: flex;
  759. gap: 12rpx;
  760. }
  761. .mini-btn,
  762. .plain-btn,
  763. .text-btn,
  764. .footer-btn,
  765. .modal-btn {
  766. margin: 0;
  767. padding: 0;
  768. border-radius: 8rpx;
  769. font-size: 26rpx;
  770. line-height: 64rpx;
  771. }
  772. .mini-btn {
  773. width: 150rpx;
  774. height: 64rpx;
  775. color: #fff;
  776. }
  777. .primary,
  778. .footer-btn.primary,
  779. .modal-btn.primary {
  780. color: #fff;
  781. background: #2979ff;
  782. }
  783. .success,
  784. .footer-btn.success {
  785. color: #fff;
  786. background: #19be6b;
  787. }
  788. .warning {
  789. color: #fff;
  790. background: #f9ae3d;
  791. }
  792. .order-main {
  793. flex: 1;
  794. min-height: 0;
  795. display: grid;
  796. grid-template-columns: minmax(0, 1fr) 720rpx;
  797. gap: 18rpx;
  798. padding-top: 18rpx;
  799. }
  800. .dish-panel,
  801. .ordered-panel {
  802. min-height: 0;
  803. display: flex;
  804. flex-direction: column;
  805. border: 1rpx solid #ebeef5;
  806. border-radius: 10rpx;
  807. background: #fff;
  808. overflow: hidden;
  809. }
  810. .ordered-panel.full {
  811. grid-column: 1 / -1;
  812. }
  813. .dish-search {
  814. display: grid;
  815. grid-template-columns: minmax(0, 1fr) 120rpx;
  816. gap: 12rpx;
  817. padding: 16rpx;
  818. border-bottom: 1rpx solid #ebeef5;
  819. }
  820. .search-input,
  821. .form-row input,
  822. .return-row input {
  823. height: 64rpx;
  824. box-sizing: border-box;
  825. padding: 0 18rpx;
  826. border: 1rpx solid #dcdfe6;
  827. border-radius: 8rpx;
  828. font-size: 26rpx;
  829. color: #303133;
  830. }
  831. .search-btn {
  832. height: 64rpx;
  833. margin: 0;
  834. padding: 0;
  835. border-radius: 8rpx;
  836. font-size: 26rpx;
  837. line-height: 64rpx;
  838. color: #fff;
  839. background: #2979ff;
  840. }
  841. .dish-content {
  842. flex: 1;
  843. min-height: 0;
  844. display: grid;
  845. grid-template-columns: 150rpx minmax(0, 1fr);
  846. }
  847. .category-list {
  848. height: 100%;
  849. background: #f7f9fb;
  850. border-right: 1rpx solid #ebeef5;
  851. }
  852. .category-item {
  853. min-height: 78rpx;
  854. display: flex;
  855. align-items: center;
  856. box-sizing: border-box;
  857. padding: 0 16rpx;
  858. border-left: 5rpx solid transparent;
  859. font-size: 26rpx;
  860. color: #606266;
  861. }
  862. .category-item.active {
  863. color: #2979ff;
  864. background: #fff;
  865. border-left-color: #2979ff;
  866. font-weight: 700;
  867. }
  868. .dish-list {
  869. height: 100%;
  870. box-sizing: border-box;
  871. padding: 16rpx;
  872. }
  873. .dish-grid {
  874. display: grid;
  875. grid-template-columns: repeat(4, minmax(0, 1fr));
  876. gap: 16rpx;
  877. }
  878. .dish-card {
  879. min-width: 0;
  880. border: 1rpx solid #ebeef5;
  881. border-radius: 10rpx;
  882. overflow: hidden;
  883. background: #fff;
  884. }
  885. .dish-image {
  886. width: 100%;
  887. height: 200rpx;
  888. background: #f2f3f5;
  889. }
  890. .dish-image-empty {
  891. display: flex;
  892. align-items: center;
  893. justify-content: center;
  894. font-size: 24rpx;
  895. color: #909399;
  896. }
  897. .dish-info {
  898. padding: 12rpx;
  899. }
  900. .dish-name,
  901. .ordered-dish-name,
  902. .info-dish-name {
  903. overflow: hidden;
  904. white-space: nowrap;
  905. text-overflow: ellipsis;
  906. }
  907. .dish-name {
  908. font-size: 28rpx;
  909. font-weight: 700;
  910. color: #303133;
  911. line-height: 38rpx;
  912. }
  913. .dish-desc {
  914. height: 42rpx;
  915. margin-top: 8rpx;
  916. font-size: 24rpx;
  917. color: #606266;
  918. line-height: 32rpx;
  919. overflow: hidden;
  920. }
  921. .dish-bottom {
  922. display: flex;
  923. align-items: center;
  924. justify-content: space-between;
  925. gap: 10rpx;
  926. margin-top: 12rpx;
  927. }
  928. .dish-price,
  929. .ordered-total,
  930. .danger-text {
  931. color: #fa3534;
  932. }
  933. .dish-price {
  934. font-size: 30rpx;
  935. font-weight: 700;
  936. }
  937. .plain-btn {
  938. width: 96rpx;
  939. height: 54rpx;
  940. line-height: 54rpx;
  941. color: #2979ff;
  942. background: #ecf5ff;
  943. }
  944. .ordered-title {
  945. height: 76rpx;
  946. display: flex;
  947. align-items: center;
  948. justify-content: space-between;
  949. box-sizing: border-box;
  950. padding: 0 18rpx;
  951. border-bottom: 1rpx solid #ebeef5;
  952. font-size: 30rpx;
  953. font-weight: 700;
  954. color: #303133;
  955. }
  956. .ordered-title-actions {
  957. display: flex;
  958. gap: 10rpx;
  959. }
  960. .text-btn {
  961. width: 140rpx;
  962. height: 52rpx;
  963. line-height: 52rpx;
  964. color: #2979ff;
  965. background: #ecf5ff;
  966. }
  967. .text-btn.danger {
  968. color: #fa3534;
  969. background: #fff1f0;
  970. }
  971. .ordered-list {
  972. flex: 1;
  973. min-height: 0;
  974. box-sizing: border-box;
  975. padding: 14rpx;
  976. }
  977. .ordered-head,
  978. .ordered-item {
  979. display: grid;
  980. grid-template-columns: minmax(180rpx, 1fr) 110rpx 120rpx 150rpx;
  981. gap: 10rpx;
  982. align-items: center;
  983. text-align: center;
  984. }
  985. .ordered-head {
  986. height: 50rpx;
  987. padding: 0 10rpx;
  988. border-radius: 8rpx;
  989. background: #f7f9fb;
  990. font-size: 24rpx;
  991. color: #909399;
  992. }
  993. .ordered-item {
  994. min-height: 86rpx;
  995. box-sizing: border-box;
  996. padding: 12rpx 10rpx;
  997. border-bottom: 1rpx solid #ebeef5;
  998. font-size: 24rpx;
  999. color: #606266;
  1000. }
  1001. .ordered-item.pending {
  1002. background: rgba(45, 140, 240, 0.12);
  1003. }
  1004. .ordered-dish-name {
  1005. font-size: 26rpx;
  1006. font-weight: 700;
  1007. color: #303133;
  1008. }
  1009. .ordered-total {
  1010. font-weight: 700;
  1011. }
  1012. .ordered-qty {
  1013. display: flex;
  1014. align-items: center;
  1015. justify-content: center;
  1016. gap: 8rpx;
  1017. }
  1018. .qty-btn {
  1019. width: 42rpx;
  1020. height: 42rpx;
  1021. margin: 0;
  1022. padding: 0;
  1023. border-radius: 50%;
  1024. font-size: 28rpx;
  1025. line-height: 42rpx;
  1026. color: #2979ff;
  1027. background: #ecf5ff;
  1028. }
  1029. .ordered-footer {
  1030. padding: 16rpx;
  1031. border-top: 1rpx solid #ebeef5;
  1032. }
  1033. .ordered-summary {
  1034. display: flex;
  1035. align-items: center;
  1036. justify-content: space-between;
  1037. margin-bottom: 14rpx;
  1038. font-size: 26rpx;
  1039. color: #606266;
  1040. }
  1041. .ordered-summary strong {
  1042. font-size: 36rpx;
  1043. color: #fa3534;
  1044. }
  1045. .ordered-actions {
  1046. display: flex;
  1047. justify-content: flex-end;
  1048. }
  1049. .ordered-actions.one {
  1050. justify-content: flex-end;
  1051. }
  1052. .footer-btn {
  1053. width: 220rpx;
  1054. height: 72rpx;
  1055. line-height: 72rpx;
  1056. }
  1057. .ordered-empty,
  1058. .empty-box {
  1059. min-height: 240rpx;
  1060. display: flex;
  1061. flex-direction: column;
  1062. align-items: center;
  1063. justify-content: center;
  1064. color: #909399;
  1065. font-size: 26rpx;
  1066. }
  1067. .ordered-empty-title {
  1068. font-size: 30rpx;
  1069. color: #606266;
  1070. }
  1071. .ordered-empty-sub {
  1072. margin-top: 8rpx;
  1073. font-size: 24rpx;
  1074. }
  1075. .modal-mask {
  1076. position: fixed;
  1077. left: 0;
  1078. right: 0;
  1079. top: 0;
  1080. bottom: 0;
  1081. z-index: 999;
  1082. display: flex;
  1083. align-items: center;
  1084. justify-content: center;
  1085. padding: 36rpx;
  1086. box-sizing: border-box;
  1087. background: rgba(0, 0, 0, 0.45);
  1088. }
  1089. .modal-card {
  1090. width: 680rpx;
  1091. max-height: 90vh;
  1092. box-sizing: border-box;
  1093. padding: 26rpx;
  1094. border-radius: 12rpx;
  1095. background: #fff;
  1096. overflow: auto;
  1097. }
  1098. .settle-card {
  1099. width: 760rpx;
  1100. }
  1101. .return-card,
  1102. .info-card {
  1103. width: 920rpx;
  1104. }
  1105. .modal-title {
  1106. margin-bottom: 20rpx;
  1107. font-size: 34rpx;
  1108. font-weight: 700;
  1109. color: #303133;
  1110. }
  1111. .settle-tabs {
  1112. display: flex;
  1113. gap: 10rpx;
  1114. margin-bottom: 18rpx;
  1115. padding: 8rpx;
  1116. border-radius: 10rpx;
  1117. background: #f7f8fa;
  1118. }
  1119. .settle-tab {
  1120. flex: 1;
  1121. height: 58rpx;
  1122. border-radius: 8rpx;
  1123. text-align: center;
  1124. font-size: 26rpx;
  1125. line-height: 58rpx;
  1126. color: #606266;
  1127. }
  1128. .settle-tab.active {
  1129. color: #fff;
  1130. background: #2979ff;
  1131. font-weight: 700;
  1132. }
  1133. .settle-normal,
  1134. .settle-preview,
  1135. .confirm-summary,
  1136. .info-summary {
  1137. display: grid;
  1138. grid-template-columns: repeat(2, minmax(0, 1fr));
  1139. gap: 12rpx;
  1140. margin-bottom: 18rpx;
  1141. padding: 16rpx;
  1142. border-radius: 8rpx;
  1143. background: #f7f8fa;
  1144. }
  1145. .settle-normal {
  1146. grid-template-columns: 1fr 1fr;
  1147. align-items: center;
  1148. }
  1149. .settle-normal strong {
  1150. text-align: right;
  1151. font-size: 40rpx;
  1152. color: #fa3534;
  1153. }
  1154. .settle-preview text,
  1155. .confirm-summary text,
  1156. .info-summary text {
  1157. display: block;
  1158. font-size: 24rpx;
  1159. color: #909399;
  1160. line-height: 32rpx;
  1161. }
  1162. .settle-preview strong,
  1163. .confirm-summary strong,
  1164. .info-summary strong {
  1165. display: block;
  1166. margin-top: 6rpx;
  1167. font-size: 28rpx;
  1168. color: #303133;
  1169. line-height: 38rpx;
  1170. word-break: break-all;
  1171. }
  1172. .info-summary {
  1173. grid-template-columns: repeat(4, minmax(0, 1fr));
  1174. }
  1175. .info-summary .wide {
  1176. grid-column: span 4;
  1177. }
  1178. .form-row,
  1179. .textarea-row,
  1180. .special-requirement-row {
  1181. margin-bottom: 16rpx;
  1182. }
  1183. .form-row text,
  1184. .textarea-row text,
  1185. .special-requirement-row text {
  1186. display: block;
  1187. margin-bottom: 8rpx;
  1188. font-size: 26rpx;
  1189. color: #606266;
  1190. }
  1191. .form-row input {
  1192. width: 100%;
  1193. }
  1194. .textarea-row textarea {
  1195. width: 100%;
  1196. height: 130rpx;
  1197. box-sizing: border-box;
  1198. padding: 14rpx;
  1199. border: 1rpx solid #dcdfe6;
  1200. border-radius: 8rpx;
  1201. font-size: 26rpx;
  1202. color: #303133;
  1203. }
  1204. .special-requirement-row u-checkbox-group {
  1205. display: block;
  1206. box-sizing: border-box;
  1207. padding: 12rpx 14rpx;
  1208. border: 1rpx solid #dcdfe6;
  1209. border-radius: 8rpx;
  1210. }
  1211. .modal-actions {
  1212. display: grid;
  1213. grid-template-columns: 1fr 1fr;
  1214. gap: 14rpx;
  1215. margin-top: 18rpx;
  1216. }
  1217. .modal-btn {
  1218. height: 72rpx;
  1219. line-height: 72rpx;
  1220. color: #606266;
  1221. background: #f7f8fa;
  1222. }
  1223. .return-list,
  1224. .info-list {
  1225. max-height: 380rpx;
  1226. }
  1227. .return-head,
  1228. .return-row {
  1229. display: grid;
  1230. grid-template-columns: 80rpx minmax(0, 1fr) 120rpx 140rpx;
  1231. gap: 10rpx;
  1232. align-items: center;
  1233. }
  1234. .return-head {
  1235. height: 52rpx;
  1236. padding: 0 12rpx;
  1237. border-radius: 8rpx;
  1238. background: #f7f9fb;
  1239. font-size: 24rpx;
  1240. color: #909399;
  1241. }
  1242. .return-row {
  1243. min-height: 70rpx;
  1244. padding: 10rpx 12rpx;
  1245. border-bottom: 1rpx solid #ebeef5;
  1246. font-size: 26rpx;
  1247. color: #606266;
  1248. }
  1249. .return-name {
  1250. overflow: hidden;
  1251. white-space: nowrap;
  1252. text-overflow: ellipsis;
  1253. font-weight: 700;
  1254. color: #303133;
  1255. }
  1256. .info-row {
  1257. display: flex;
  1258. align-items: center;
  1259. justify-content: space-between;
  1260. gap: 18rpx;
  1261. min-height: 88rpx;
  1262. padding: 12rpx 0;
  1263. border-bottom: 1rpx solid #ebeef5;
  1264. }
  1265. .info-row strong {
  1266. font-size: 28rpx;
  1267. color: #fa3534;
  1268. }
  1269. .info-dish-name {
  1270. font-size: 28rpx;
  1271. font-weight: 700;
  1272. color: #303133;
  1273. }
  1274. .info-dish-sub {
  1275. margin-top: 6rpx;
  1276. font-size: 24rpx;
  1277. color: #909399;
  1278. }
  1279. .info-total {
  1280. margin-top: 14rpx;
  1281. text-align: right;
  1282. font-size: 26rpx;
  1283. color: #606266;
  1284. }
  1285. </style>