detail.vue 15 KB

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