friendCouponDetail.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <template>
  2. <!-- 好友优惠券 - 详情页面(数据由列表页 sessionStorage 传入,不调详情接口) -->
  3. <div class="table-box" style="width: 100%; min-height: 100%; background-color: white">
  4. <div class="header">
  5. <el-button @click="goBack"> 返回 </el-button>
  6. <h2 class="title">优惠券详情</h2>
  7. </div>
  8. <div class="content">
  9. <!-- 左侧内容区域 -->
  10. <div class="contentLeft">
  11. <div class="model">
  12. <h3 style="font-weight: bold">基础信息:</h3>
  13. <!-- 店铺名称 -->
  14. <div class="detail-item">
  15. <div class="detail-label">店铺名称</div>
  16. <div class="detail-value">
  17. {{ couponModel.storeName || "--" }}
  18. </div>
  19. </div>
  20. <!-- 类型 -->
  21. <div class="detail-item">
  22. <div class="detail-label">类型</div>
  23. <div class="detail-value">
  24. {{ getCouponTypeText(couponModel.couponType) }}
  25. </div>
  26. </div>
  27. <!-- 优惠券名称 -->
  28. <div class="detail-item">
  29. <div class="detail-label">优惠券名称</div>
  30. <div class="detail-value">
  31. {{ couponModel.couponName || couponModel.name || "--" }}
  32. </div>
  33. </div>
  34. <!-- 折扣券:折扣率;满减券等:面值 -->
  35. <div class="detail-item" v-if="Number(couponModel.couponType) === 2">
  36. <div class="detail-label">折扣率</div>
  37. <div class="detail-value">
  38. {{ formatDiscountRateDisplay(couponModel.discountRate) }}
  39. </div>
  40. </div>
  41. <div class="detail-item" v-else>
  42. <div class="detail-label">面值</div>
  43. <div class="detail-value">
  44. {{ formatCurrency(couponModel.nominalValue ?? couponModel.price, 2, "¥") }}
  45. </div>
  46. </div>
  47. <!-- 数量 -->
  48. <div class="detail-item">
  49. <div class="detail-label">数量</div>
  50. <div class="detail-value">
  51. {{ couponModel.ownedQuantity ?? couponModel.couponNum ?? couponModel.singleQty ?? "--" }}
  52. </div>
  53. </div>
  54. <!-- 有效期:longTermValid=1 长期有效;否则展示 expirationDate(天) -->
  55. <div class="detail-item">
  56. <div class="detail-label">有效期</div>
  57. <div class="detail-value">
  58. {{ getValidityDisplay() }}
  59. </div>
  60. </div>
  61. <!-- 最低消费金额 -->
  62. <div class="detail-item">
  63. <div class="detail-label">最低消费金额</div>
  64. <div class="detail-value">
  65. {{ formatCurrency(couponModel.minimumSpendingAmount, 2, "¥") }}
  66. </div>
  67. </div>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. </template>
  73. <script setup lang="tsx" name="friendCouponDetail">
  74. /**
  75. * 好友优惠券 - 详情页:数据来自列表行(sessionStorage),不请求详情接口
  76. */
  77. import { ref, onMounted } from "vue";
  78. import { useRouter } from "vue-router";
  79. import { ElMessage } from "element-plus";
  80. import { formatCurrency } from "@/utils/formatCurrency";
  81. const FRIEND_COUPON_DETAIL_ROW_KEY = "friendCoupon_detail_row";
  82. const router = useRouter();
  83. const couponModel = ref<any>({
  84. acName: "",
  85. couponName: "",
  86. couponNum: 0,
  87. deleteFlag: 0,
  88. endDate: "",
  89. expirationDate: "",
  90. longTermValid: undefined as number | undefined,
  91. id: 0,
  92. imgUrl: "",
  93. lifeDiscountCouponFriendRuleDetailVos: [],
  94. minimumSpendingAmount: undefined,
  95. moneyHigh: 0,
  96. moneyLow: 0,
  97. nominalValue: 0,
  98. discountRate: undefined,
  99. status: "",
  100. storeId: 0,
  101. storeName: ""
  102. });
  103. const goBack = () => {
  104. router.go(-1);
  105. };
  106. onMounted(() => {
  107. const raw = sessionStorage.getItem(FRIEND_COUPON_DETAIL_ROW_KEY);
  108. sessionStorage.removeItem(FRIEND_COUPON_DETAIL_ROW_KEY);
  109. if (!raw) {
  110. ElMessage.warning("缺少列表数据,请从列表重新进入");
  111. router.go(-1);
  112. return;
  113. }
  114. try {
  115. const row = JSON.parse(raw);
  116. couponModel.value = { ...couponModel.value, ...row };
  117. } catch {
  118. ElMessage.error("数据解析失败");
  119. router.go(-1);
  120. }
  121. });
  122. const getValidityDisplay = () => {
  123. const m = couponModel.value;
  124. const lt = m.longTermValid;
  125. if (lt === 1 || lt === "1" || lt === true) return "长期有效";
  126. const ed = m.expirationDate;
  127. if (ed === null || ed === undefined || ed === "") return "--";
  128. const s = String(ed).trim();
  129. if (/^\d{4}-\d{2}-\d{2}/.test(s) || s.includes("T")) {
  130. return s.includes("-") ? (s.replace(/-/g, "/").split(" ")[0] ?? s) : s;
  131. }
  132. return Number.isNaN(Number(s)) ? s : `${s}天`;
  133. };
  134. const formatDiscountRateDisplay = (rate: unknown) => {
  135. if (rate === null || rate === undefined || rate === "") return "--";
  136. const n = Number(rate);
  137. if (isNaN(n)) return `${rate}折`;
  138. // 与 newCoupon 一致:接口多为 1–100(如 85 表示 8.5 折),展示时除以 10
  139. const x = n / 10;
  140. const s = Number.isInteger(x) ? String(x) : String(Number(x.toFixed(1)));
  141. return `${s.replace(/\.0$/, "")}折`;
  142. };
  143. const getCouponTypeText = (t: number | string | undefined) => {
  144. const typeMap: Record<string, string> = { "1": "满减券", "2": "折扣券" };
  145. return t != null && t !== "" ? (typeMap[String(t)] ?? String(t)) : "--";
  146. };
  147. </script>
  148. <style scoped lang="scss">
  149. .table-box {
  150. display: flex;
  151. flex-direction: column;
  152. height: auto !important;
  153. min-height: 100%;
  154. }
  155. .header {
  156. display: flex;
  157. align-items: center;
  158. padding: 20px 24px;
  159. background-color: #ffffff;
  160. border-bottom: 1px solid #e4e7ed;
  161. box-shadow: 0 2px 4px rgb(0 0 0 / 2%);
  162. }
  163. .title {
  164. flex: 1;
  165. margin: 0;
  166. font-size: 18px;
  167. font-weight: 600;
  168. color: #303133;
  169. text-align: center;
  170. }
  171. .content {
  172. display: flex;
  173. flex: 1;
  174. column-gap: 24px;
  175. width: 98%;
  176. padding: 0 12px;
  177. margin: 24px auto;
  178. .contentLeft {
  179. width: 50%;
  180. padding-right: 12px;
  181. }
  182. .contentRight {
  183. width: 50%;
  184. padding-left: 12px;
  185. }
  186. }
  187. .model {
  188. margin-bottom: 50px;
  189. h3 {
  190. padding-bottom: 12px;
  191. margin: 0 0 20px;
  192. font-size: 16px;
  193. color: #303133;
  194. }
  195. }
  196. .detail-item {
  197. display: flex;
  198. align-items: flex-start;
  199. min-height: 32px;
  200. margin-bottom: 24px;
  201. }
  202. .detail-label {
  203. flex-shrink: 0;
  204. min-width: 200px;
  205. font-size: 14px;
  206. font-weight: 500;
  207. line-height: 32px;
  208. color: #606266;
  209. }
  210. .detail-value {
  211. flex: 1;
  212. font-size: 14px;
  213. line-height: 32px;
  214. color: #303133;
  215. word-break: break-word;
  216. }
  217. </style>