detail.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. <template>
  2. <!-- 代金券管理 - 详情页面 -->
  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. <!-- 基础信息模块 -->
  12. <div class="model">
  13. <h3 style="font-weight: bold">基础信息:</h3>
  14. <!-- 代金券名称 -->
  15. <div class="detail-item">
  16. <div class="detail-label">代金券名称</div>
  17. <div class="detail-value">
  18. {{ voucherModel.name || "--" }}
  19. </div>
  20. </div>
  21. <!-- 抵扣价格 -->
  22. <div class="detail-item">
  23. <div class="detail-label">抵扣价格</div>
  24. <div class="detail-value">
  25. {{ formatCurrency(voucherModel.price, 2, "¥") || "--" }}
  26. </div>
  27. </div>
  28. <!-- 售卖价格 -->
  29. <div class="detail-item">
  30. <div class="detail-label">售卖价格</div>
  31. <div class="detail-value">
  32. {{ formatCurrency(voucherModel.offprice, 2, "¥") || "--" }}
  33. </div>
  34. </div>
  35. <!-- 开始售卖时间 -->
  36. <div class="detail-item">
  37. <div class="detail-label">开始售卖时间</div>
  38. <div class="detail-value">
  39. {{ voucherModel.startDate ? formatDate(voucherModel.startDate) : "--" }}
  40. </div>
  41. </div>
  42. <!-- 结束售卖时间 -->
  43. <div class="detail-item">
  44. <div class="detail-label">结束售卖时间</div>
  45. <div class="detail-value">
  46. {{ voucherModel.endDate ? formatDate(voucherModel.endDate) : "--" }}
  47. </div>
  48. </div>
  49. </div>
  50. <!-- 购买须知模块 -->
  51. <div class="model">
  52. <h3 style="font-weight: bold">购买须知:</h3>
  53. <!-- 使用时间 -->
  54. <div class="detail-item">
  55. <div class="detail-label">使用时间</div>
  56. <div class="detail-value">
  57. {{ getUsageTimeText() }}
  58. </div>
  59. </div>
  60. <!-- 有效期 -->
  61. <div class="detail-item">
  62. <div class="detail-label">有效期</div>
  63. <div class="detail-value">
  64. {{ getExpirationText() }}
  65. </div>
  66. </div>
  67. <!-- 不可用日期 -->
  68. <div class="detail-item">
  69. <div class="detail-label">不可用日期</div>
  70. <div class="detail-value">
  71. {{ getUnavailableDateText() }}
  72. </div>
  73. </div>
  74. <!-- 库存 -->
  75. <div class="detail-item">
  76. <div class="detail-label">库存</div>
  77. <div class="detail-value">
  78. {{ voucherModel.singleQty + "张" || "--" }}
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. <!-- 右侧内容区域 -->
  84. <div class="contentRight">
  85. <!-- 使用规则模块 -->
  86. <div class="model">
  87. <h3 style="font-weight: bold">使用规则:</h3>
  88. <!-- 单次可用数量 -->
  89. <div class="detail-item">
  90. <div class="detail-label">单次可用数量</div>
  91. <div class="detail-value">
  92. {{ voucherModel.singleCanUse || "--" }}
  93. </div>
  94. </div>
  95. <!-- 限购数量 -->
  96. <div class="detail-item">
  97. <div class="detail-label">限购数量</div>
  98. <div class="detail-value">
  99. {{ voucherModel.purchaseLimitCode || "--" }}
  100. </div>
  101. </div>
  102. <!-- 适用范围 -->
  103. <div class="detail-item">
  104. <div class="detail-label">适用范围</div>
  105. <div class="detail-value">
  106. {{ getApplyScopeText() }}
  107. </div>
  108. </div>
  109. <!-- 补充说明 -->
  110. <div class="detail-item">
  111. <div class="detail-label">补充说明</div>
  112. <div class="detail-value" v-html="voucherModel.supplement || '--'" />
  113. </div>
  114. </div>
  115. </div>
  116. </div>
  117. </div>
  118. </template>
  119. <script setup lang="tsx" name="voucherManagementDetail">
  120. /**
  121. * 代金券管理 - 详情页面
  122. * 功能:显示代金券的详细信息
  123. */
  124. import { ref, onMounted } from "vue";
  125. import { useRouter, useRoute } from "vue-router";
  126. import { ElMessage } from "element-plus";
  127. import { getVoucherDetail, getHolidayList } from "@/api/modules/voucherManagement";
  128. import { formatCurrency } from "@/utils/formatCurrency";
  129. // ==================== 响应式数据定义 ====================
  130. // 路由相关
  131. const router = useRouter();
  132. const route = useRoute();
  133. // 页面ID参数
  134. const id = ref<string>("");
  135. // ==================== 代金券信息数据模型 ====================
  136. const voucherModel = ref<any>({
  137. // 代金券名称
  138. name: "",
  139. // 抵扣价格
  140. price: "",
  141. // 售卖价格
  142. offprice: "",
  143. // 开始售卖时间
  144. startDate: "",
  145. // 结束售卖时间
  146. endDate: "",
  147. // 使用时间 - 开始时间
  148. buyUseStartTime: "",
  149. // 使用时间 - 结束时间
  150. buyUseEndTime: "",
  151. // 有效期类型:1-指定天数,2-指定时间段内可用
  152. expirationType: "1",
  153. // 有效期天数(当expirationType为1时使用)
  154. expirationDate: 0,
  155. // 有效期时间段(当expirationType为2时使用)
  156. validityPeriod: [],
  157. // 不可用日期类型:1-全部日期可用,2-限制日期
  158. unusedType: "1",
  159. // 限制日期 - 星期选择(数组,存储选中的星期值)
  160. unavailableWeekdays: [],
  161. // 限制日期 - 节日选择(数组,存储选中的节日值)
  162. unavailableHolidays: [],
  163. // 自定义不可用日期列表(数组,每个元素是一个日期范围 [startDate, endDate])
  164. disableDateList: [],
  165. // 库存
  166. singleQty: "",
  167. // 单日可用数量
  168. singleCanUse: "",
  169. // 限购数量
  170. purchaseLimitCode: "",
  171. // 适用范围类型:0-全场通用,1-部分不可用
  172. applyType: "1",
  173. // 适用范围(当applyType为1时使用)
  174. applyDesc: "",
  175. // 补充说明
  176. supplement: ""
  177. });
  178. // 星期选项列表
  179. const weekdayList = ref([
  180. { name: "周一", id: "0", oName: "星期一" },
  181. { name: "周二", id: "1", oName: "星期二" },
  182. { name: "周三", id: "2", oName: "星期三" },
  183. { name: "周四", id: "3", oName: "星期四" },
  184. { name: "周五", id: "4", oName: "星期五" },
  185. { name: "周六", id: "5", oName: "星期六" },
  186. { name: "周日", id: "6", oName: "星期日" }
  187. ]);
  188. // 节日选项列表
  189. const holidayList: any = ref([]);
  190. // ==================== 生命周期钩子 ====================
  191. /**
  192. * 组件挂载时初始化
  193. * 从路由参数中获取ID并加载详情数据
  194. */
  195. onMounted(async () => {
  196. id.value = (route.query.id as string) || "";
  197. // 加载节日列表
  198. await loadHolidayList();
  199. if (id.value) {
  200. await loadDetailData();
  201. } else {
  202. ElMessage.warning("缺少代金券ID参数");
  203. }
  204. });
  205. // ==================== 事件处理函数 ====================
  206. /**
  207. * 返回上一页
  208. */
  209. const goBack = () => {
  210. router.go(-1);
  211. };
  212. // ==================== 数据加载函数 ====================
  213. /**
  214. * 加载节日列表
  215. */
  216. const loadHolidayList = async () => {
  217. try {
  218. let params = {
  219. year: new Date().getFullYear(),
  220. page: 1,
  221. size: 500,
  222. openFlag: 1,
  223. holidayName: ""
  224. };
  225. let res: any = await getHolidayList(params);
  226. if (res && res.code == 200) {
  227. holidayList.value = res.data.records || [];
  228. }
  229. } catch (error) {
  230. console.error("加载节日列表出错:", error);
  231. }
  232. };
  233. /**
  234. * 加载详情数据
  235. */
  236. const loadDetailData = async () => {
  237. try {
  238. const params = {
  239. id: id.value
  240. };
  241. const res: any = await getVoucherDetail(params);
  242. if (res && res.code == 200) {
  243. // 合并主数据
  244. voucherModel.value = { ...voucherModel.value, ...res.data };
  245. // 处理有效期时间段:将时间戳字符串转换为日期数组
  246. if (voucherModel.value.validityPeriod && voucherModel.value.expirationType == 2) {
  247. const periodArray = voucherModel.value.validityPeriod.split(",");
  248. voucherModel.value.validityPeriod = periodArray
  249. .map((item: string) => {
  250. const timestamp = Number(item.trim());
  251. if (!isNaN(timestamp)) {
  252. // 将时间戳转换为日期字符串 (YYYY-MM-DD)
  253. return new Date(timestamp).toISOString().split("T")[0];
  254. }
  255. return null;
  256. })
  257. .filter((item: any) => item !== null);
  258. } else {
  259. voucherModel.value.validityPeriod = [];
  260. }
  261. // 处理不可用日期
  262. if (voucherModel.value.unusedType == 2) {
  263. // 限制日期类型:格式为 "星期;节日"
  264. const listVal = voucherModel.value.unusedDate ? voucherModel.value.unusedDate.split(";") : [];
  265. voucherModel.value.unavailableWeekdays = listVal[0] ? listVal[0].split(",").filter((item: string) => item) : [];
  266. voucherModel.value.unavailableHolidays = listVal[1] ? listVal[1].split(",").filter((item: string) => item) : [];
  267. } else if (voucherModel.value.unusedType === 3) {
  268. // 自定义不可用日期类型:格式为 "开始日期,结束日期;开始日期,结束日期"
  269. if (voucherModel.value.unusedDate) {
  270. const dateRanges = voucherModel.value.unusedDate.split(";");
  271. voucherModel.value.disableDateList = dateRanges
  272. .map((range: string) => {
  273. const dates = range.split(",");
  274. if (dates.length === 2 && dates[0] && dates[1]) {
  275. return [dates[0].trim(), dates[1].trim()];
  276. }
  277. return null;
  278. })
  279. .filter((item: any) => item !== null);
  280. } else {
  281. voucherModel.value.disableDateList = [];
  282. }
  283. }
  284. } else {
  285. ElMessage.error("加载详情数据失败");
  286. }
  287. } catch (error) {
  288. console.error("加载详情数据出错:", error);
  289. ElMessage.error("加载详情数据出错");
  290. }
  291. };
  292. // ==================== 工具函数 ====================
  293. /**
  294. * 格式化日期
  295. * @param date 日期字符串 (YYYY-MM-DD)
  296. * @returns 格式化后的日期字符串 (YYYY.MM.DD)
  297. */
  298. const formatDate = (date: string) => {
  299. if (!date) return "--";
  300. return date.replace(/-/g, "/");
  301. };
  302. /**
  303. * 获取使用时间文本
  304. */
  305. const getUsageTimeText = () => {
  306. if (voucherModel.value.buyUseStartTime && voucherModel.value.buyUseEndTime) {
  307. const startHour = formatHour(voucherModel.value.buyUseStartTime);
  308. const endHour = formatHour(voucherModel.value.buyUseEndTime);
  309. return `${startHour}至${endHour}`;
  310. }
  311. return "--";
  312. };
  313. /**
  314. * 格式化小时
  315. * @param hour 小时值(字符串或数字)
  316. * @returns 格式化后的小时文本(如:7点)
  317. */
  318. const formatHour = (hour: string | number) => {
  319. if (!hour && hour !== 0) return "";
  320. const hourNum = typeof hour === "string" ? parseInt(hour) : hour;
  321. return `${hourNum}点`;
  322. };
  323. /**
  324. * 获取有效期文本
  325. */
  326. const getExpirationText = () => {
  327. if (voucherModel.value.expirationType == "1") {
  328. if (voucherModel.value.expirationDate) {
  329. return `购买后${voucherModel.value.expirationDate}天`;
  330. }
  331. return "--";
  332. } else if (voucherModel.value.expirationType == "2") {
  333. if (
  334. voucherModel.value.validityPeriod &&
  335. Array.isArray(voucherModel.value.validityPeriod) &&
  336. voucherModel.value.validityPeriod.length === 2
  337. ) {
  338. const startDate = formatDate(voucherModel.value.validityPeriod[0]);
  339. const endDate = formatDate(voucherModel.value.validityPeriod[1]);
  340. return `${startDate} 至 ${endDate}`;
  341. }
  342. return "--";
  343. }
  344. return "--";
  345. };
  346. /**
  347. * 获取不可用日期文本
  348. */
  349. const getUnavailableDateText = () => {
  350. if (voucherModel.value.unusedType == "1") {
  351. return "全部日期可用";
  352. } else if (voucherModel.value.unusedType === "2") {
  353. const weekdays: string[] = [];
  354. const holidays: string[] = [];
  355. // 处理星期
  356. if (voucherModel.value.unavailableWeekdays && voucherModel.value.unavailableWeekdays.length > 0) {
  357. voucherModel.value.unavailableWeekdays.forEach((day: string) => {
  358. const weekday = weekdayList.value.find(w => w.oName === day);
  359. if (weekday) {
  360. weekdays.push(weekday.name);
  361. }
  362. });
  363. }
  364. // 处理节日
  365. if (voucherModel.value.unavailableHolidays && voucherModel.value.unavailableHolidays.length > 0) {
  366. voucherModel.value.unavailableHolidays.forEach((holidayId: string) => {
  367. const holiday = holidayList.value.find((h: any) => h.id === holidayId || String(h.id) === String(holidayId));
  368. if (holiday) {
  369. holidays.push(holiday.festivalName);
  370. }
  371. });
  372. }
  373. const parts: string[] = [];
  374. if (weekdays.length > 0) {
  375. parts.push(weekdays.join("、"));
  376. }
  377. if (holidays.length > 0) {
  378. parts.push(holidays.join("、"));
  379. }
  380. return parts.length > 0 ? parts.join("、") : "--";
  381. } else if (voucherModel.value.unusedType == "3") {
  382. if (voucherModel.value.disableDateList && voucherModel.value.disableDateList.length > 0) {
  383. const dateStrings = voucherModel.value.disableDateList
  384. .filter((date: any) => date && Array.isArray(date) && date.length === 2)
  385. .map((date: any) => {
  386. const startDate = formatDate(date[0]);
  387. const endDate = formatDate(date[1]);
  388. return startDate === endDate ? startDate : `${startDate}-${endDate}`;
  389. });
  390. return dateStrings.length > 0 ? dateStrings.join("、") : "--";
  391. }
  392. return "--";
  393. }
  394. return "--";
  395. };
  396. /**
  397. * 获取适用范围文本
  398. */
  399. const getApplyScopeText = () => {
  400. if (voucherModel.value.applyType === "1" || voucherModel.value.applyType == 1) {
  401. return "全场通用";
  402. } else if (voucherModel.value.applyType === "2" || voucherModel.value.applyType == 2) {
  403. if (voucherModel.value.applyDesc) {
  404. return `除${voucherModel.value.applyDesc}外全场通用`;
  405. }
  406. return "全场通用";
  407. }
  408. return "--";
  409. };
  410. </script>
  411. <style scoped lang="scss">
  412. /* 页面容器 */
  413. .table-box {
  414. display: flex;
  415. flex-direction: column;
  416. height: auto !important;
  417. min-height: 100%;
  418. }
  419. /* 头部区域 */
  420. .header {
  421. display: flex;
  422. align-items: center;
  423. padding: 20px 24px;
  424. background-color: #ffffff;
  425. border-bottom: 1px solid #e4e7ed;
  426. box-shadow: 0 2px 4px rgb(0 0 0 / 2%);
  427. }
  428. .title {
  429. flex: 1;
  430. margin: 0;
  431. font-size: 18px;
  432. font-weight: 600;
  433. color: #303133;
  434. text-align: center;
  435. }
  436. /* 内容区域布局 */
  437. .content {
  438. display: flex;
  439. flex: 1;
  440. column-gap: 24px;
  441. width: 98%;
  442. padding: 0 12px;
  443. margin: 24px auto;
  444. /* 左侧内容区域 */
  445. .contentLeft {
  446. width: 50%;
  447. padding-right: 12px;
  448. }
  449. /* 右侧内容区域 */
  450. .contentRight {
  451. width: 50%;
  452. padding-left: 12px;
  453. }
  454. }
  455. /* 模块容器 */
  456. .model {
  457. margin-bottom: 50px;
  458. h3 {
  459. padding-bottom: 12px;
  460. margin: 0 0 20px;
  461. font-size: 16px;
  462. color: #303133;
  463. border-bottom: 2px solid #e4e7ed;
  464. }
  465. }
  466. /* 详情项样式 */
  467. .detail-item {
  468. display: flex;
  469. align-items: flex-start;
  470. min-height: 32px;
  471. margin-bottom: 24px;
  472. }
  473. .detail-label {
  474. flex-shrink: 0;
  475. min-width: 120px;
  476. font-size: 14px;
  477. font-weight: 500;
  478. line-height: 32px;
  479. color: #606266;
  480. }
  481. .detail-value {
  482. flex: 1;
  483. font-size: 14px;
  484. line-height: 32px;
  485. color: #303133;
  486. word-break: break-word;
  487. }
  488. .empty-text {
  489. color: #909399;
  490. }
  491. </style>