newVoucher.vue 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387
  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">{{ type == "add" ? "新建" : "编辑" }}代金券</h2>
  7. </div>
  8. <el-form :model="voucherModel" ref="ruleFormRef" :rules="rules" label-width="130px" class="formBox">
  9. <div class="content">
  10. <!-- 左侧内容区域 -->
  11. <div class="contentLeft">
  12. <!-- 基础信息模块 -->
  13. <div class="model">
  14. <h3 style="font-weight: bold">基础信息:</h3>
  15. <!-- 代金券名称 -->
  16. <el-form-item label="代金券名称" prop="name">
  17. <el-input maxlength="20" v-model="voucherModel.name" placeholder="请输入" clearable />
  18. </el-form-item>
  19. <!-- 抵扣价格 -->
  20. <el-form-item label="抵扣价格(¥)" prop="price">
  21. <el-input v-model="voucherModel.price" maxlength="15" placeholder="请输入" clearable />
  22. </el-form-item>
  23. <!-- 售卖价格 -->
  24. <el-form-item label="售卖价格(¥)" prop="offprice">
  25. <el-input v-model="voucherModel.offprice" maxlength="15" placeholder="请输入" clearable />
  26. </el-form-item>
  27. <!-- 开始售卖时间 -->
  28. <el-form-item label="开始售卖时间" prop="startDate">
  29. <el-date-picker
  30. v-model="voucherModel.startDate"
  31. format="YYYY/MM/DD"
  32. value-format="YYYY-MM-DD"
  33. placeholder="请选择开始售卖时间"
  34. :disabled-date="disabledStartDate"
  35. />
  36. </el-form-item>
  37. <!-- 结束售卖时间 -->
  38. <el-form-item label="结束售卖时间" prop="endDate">
  39. <el-date-picker
  40. v-model="voucherModel.endDate"
  41. format="YYYY/MM/DD"
  42. value-format="YYYY-MM-DD"
  43. placeholder="请选择结束售卖时间"
  44. :disabled-date="disabledEndDate"
  45. />
  46. </el-form-item>
  47. </div>
  48. <!-- 购买须知模块 -->
  49. <div class="model">
  50. <h3 style="font-weight: bold">购买须知:</h3>
  51. <!-- 使用时间 -->
  52. <el-form-item label="使用时间" prop="usageTime">
  53. <div class="time-range-container">
  54. <el-select v-model="voucherModel.buyUseStartTime" placeholder="开始时间" class="time-picker">
  55. <el-option v-for="hour in hourOptions" :key="hour.value" :label="hour.label" :value="hour.value" />
  56. </el-select>
  57. <span class="time-separator">至</span>
  58. <el-select v-model="voucherModel.buyUseEndTime" placeholder="结束时间" class="time-picker">
  59. <el-option v-for="hour in hourOptions" :key="hour.value" :label="hour.label" :value="hour.value" />
  60. </el-select>
  61. </div>
  62. </el-form-item>
  63. <!-- 有效期 -->
  64. <el-form-item label="有效期" prop="expirationType">
  65. <el-radio-group v-model="voucherModel.expirationType" class="ml-4">
  66. <el-radio v-for="status in validityPeriodList" :value="status.value" :key="status.value">
  67. {{ status.label }}
  68. </el-radio>
  69. </el-radio-group>
  70. </el-form-item>
  71. <el-form-item label="" prop="expirationDate" v-if="voucherModel.expirationType == 1">
  72. <div class="expiration-date-container">
  73. <span class="expiration-label">用户购买</span>
  74. <el-input-number
  75. v-model="voucherModel.expirationDate"
  76. placeholder="请输入"
  77. :min="0"
  78. :max="10000"
  79. class="expiration-input"
  80. />
  81. <span class="expiration-label">天内有效</span>
  82. </div>
  83. </el-form-item>
  84. <el-form-item label="" prop="validityPeriod" v-else>
  85. <el-date-picker
  86. v-model="voucherModel.validityPeriod"
  87. type="daterange"
  88. value-format="x"
  89. range-separator="-"
  90. start-placeholder="开始时间"
  91. end-placeholder="结束时间"
  92. :disabled-date="disabledValidityDate"
  93. />
  94. </el-form-item>
  95. <!-- 不可用日期 -->
  96. <el-form-item label="不可用日期" prop="unusedType">
  97. <el-radio-group v-model="voucherModel.unusedType" class="ml-4">
  98. <el-radio v-for="status in unavailableDateTypeList" :value="status.value" :key="status.value">
  99. {{ status.label }}
  100. </el-radio>
  101. </el-radio-group>
  102. </el-form-item>
  103. <template v-if="voucherModel.unusedType == 2">
  104. <el-form-item label="" prop="unavailableWeekdays">
  105. <div class="unavailable-dates-container">
  106. <!-- 星期选择 -->
  107. <div class="date-select-section">
  108. <div class="section-title">星期</div>
  109. <div class="button-group">
  110. <el-button
  111. v-for="weekday in weekdayList"
  112. :key="weekday.oName"
  113. :type="voucherModel.unavailableWeekdays?.includes(weekday.oName) ? 'primary' : ''"
  114. class="date-select-btn"
  115. @click="toggleWeekday(weekday.oName)"
  116. >
  117. {{ weekday.name }}
  118. </el-button>
  119. </div>
  120. </div>
  121. </div>
  122. </el-form-item>
  123. <el-form-item label="" prop="unavailableHolidays">
  124. <div class="unavailable-dates-container">
  125. <!-- 节日选择 -->
  126. <div class="date-select-section">
  127. <div class="section-title">节日</div>
  128. <div class="button-group">
  129. <el-button
  130. v-for="holiday in holidayList"
  131. :key="holiday.id"
  132. :type="voucherModel.unavailableHolidays?.includes(String(holiday.id)) ? 'primary' : ''"
  133. class="date-select-btn"
  134. @click="toggleHoliday(holiday.id)"
  135. >
  136. {{ holiday.festivalName }}
  137. </el-button>
  138. </div>
  139. </div>
  140. </div>
  141. </el-form-item>
  142. </template>
  143. <el-form-item label="" prop="customUnavailableDates" v-else-if="voucherModel.unusedType == 3">
  144. <div class="date-picker-container">
  145. <el-button :icon="Plus" class="add-date-btn" type="primary" @click="addDate"> 添加日期 </el-button>
  146. <div v-for="(item, index) in voucherModel.disableDateList" :key="index" class="date-item">
  147. <el-date-picker
  148. v-model="voucherModel.disableDateList[index]"
  149. type="daterange"
  150. value-format="YYYY-MM-DD"
  151. range-separator="-"
  152. start-placeholder="开始时间"
  153. end-placeholder="结束时间"
  154. class="date-picker"
  155. :disabled-date="disabledCustomUnavailableDate"
  156. />
  157. <el-button
  158. :icon="Delete"
  159. type="danger"
  160. circle
  161. size="small"
  162. class="delete-btn"
  163. @click="removeDate(index)"
  164. v-show="voucherModel.disableDateList.length > 1"
  165. />
  166. </div>
  167. </div>
  168. </el-form-item>
  169. </div>
  170. </div>
  171. <!-- 右侧内容区域 -->
  172. <div class="contentRight">
  173. <!-- 库存模块 -->
  174. <div class="model">
  175. <h3 style="font-weight: bold">库存:</h3>
  176. <el-form-item label="库存(张)" prop="singleQty">
  177. <el-input v-model="voucherModel.singleQty" maxlength="15" placeholder="请输入" clearable />
  178. </el-form-item>
  179. </div>
  180. <!-- 使用规则模块 -->
  181. <div class="model">
  182. <h3 style="font-weight: bold">使用规则:</h3>
  183. <!-- 单日可用数量 -->
  184. <el-form-item label="单日可用数量(张)" prop="singleCanUseType">
  185. <el-radio-group v-model="voucherModel.singleCanUseType" class="ml-4">
  186. <el-radio v-for="status in dailyUseLimitList" :value="status.value" :key="status.value">
  187. {{ status.label }}
  188. </el-radio>
  189. </el-radio-group>
  190. </el-form-item>
  191. <el-form-item label="" prop="singleCanUse" v-if="voucherModel.singleCanUseType == 2">
  192. <el-input v-model="voucherModel.singleCanUse" maxlength="15" placeholder="请输入" clearable />
  193. </el-form-item>
  194. <!-- 限购数量 -->
  195. <el-form-item label="限购数量(张)" prop="purchaseLimitType">
  196. <el-radio-group v-model="voucherModel.purchaseLimitType" class="ml-4">
  197. <el-radio v-for="status in purchaseLimitList" :value="status.value" :key="status.value">
  198. {{ status.label }}
  199. </el-radio>
  200. </el-radio-group>
  201. </el-form-item>
  202. <el-form-item label="" prop="purchaseLimitCode" v-if="voucherModel.purchaseLimitType == 2">
  203. <el-input v-model="voucherModel.purchaseLimitCode" maxlength="15" placeholder="请输入" clearable />
  204. </el-form-item>
  205. <!-- 适用范围 -->
  206. <el-form-item label="适用范围" prop="applyType">
  207. <el-radio-group v-model="voucherModel.applyType" class="ml-4">
  208. <el-radio v-for="status in applicableScopeList" :value="status.value" :key="status.value">
  209. {{ status.label }}
  210. </el-radio>
  211. </el-radio-group>
  212. </el-form-item>
  213. <el-form-item label="" prop="applyDesc" v-if="voucherModel.applyType == 2">
  214. <el-input
  215. maxlength="50"
  216. v-model="voucherModel.applyDesc"
  217. :rows="3"
  218. type="textarea"
  219. placeholder="请输入"
  220. show-word-limit
  221. />
  222. </el-form-item>
  223. </div>
  224. <!-- 补充说明模块 -->
  225. <div class="model">
  226. <h3 style="font-weight: bold">补充说明:</h3>
  227. <el-form-item label="补充说明" prop="supplement">
  228. <el-input
  229. maxlength="300"
  230. v-model="voucherModel.supplement"
  231. :rows="4"
  232. type="textarea"
  233. placeholder="请输入"
  234. show-word-limit
  235. />
  236. </el-form-item>
  237. </div>
  238. </div>
  239. </div>
  240. </el-form>
  241. <!-- 底部按钮区域 -->
  242. <div class="button-container">
  243. <el-button @click="handleSubmit('draft')"> 存草稿 </el-button>
  244. <el-button type="primary" @click="handleSubmit()"> 提交 </el-button>
  245. </div>
  246. </div>
  247. </template>
  248. <script setup lang="tsx" name="newVoucher">
  249. /**
  250. * 代金券管理 - 新增/编辑页面
  251. * 功能:支持代金券的新增和编辑操作
  252. */
  253. import { ref, reactive, watch, nextTick, onMounted } from "vue";
  254. import { ElMessage } from "element-plus";
  255. import { Plus, Delete } from "@element-plus/icons-vue";
  256. import { useRoute, useRouter } from "vue-router";
  257. import type { FormInstance } from "element-plus";
  258. import { getVoucherDetail, getHolidayList, addOrUpdateCoupon } from "@/api/modules/voucherManagement";
  259. import {
  260. validatePositiveNumber,
  261. validatePositiveInteger,
  262. validateDateRange,
  263. validateDateRangeArray,
  264. validateConditionalRequired,
  265. validateArrayMinLength,
  266. validateDateListArray,
  267. validatePriceFormat,
  268. validatePriceComparison
  269. } from "@/utils/eleValidate";
  270. import { localGet } from "@/utils";
  271. // ==================== 响应式数据定义 ====================
  272. // 路由相关
  273. const router = useRouter();
  274. const route = useRoute();
  275. // 页面状态
  276. const type = ref<string>(""); // 页面类型:add-新增, edit-编辑
  277. const id = ref<string>(""); // 页面ID参数
  278. // ==================== 表单验证规则 ====================
  279. const rules = reactive({
  280. name: [{ required: true, message: "请输入代金券名称" }],
  281. price: [
  282. { required: true, message: "请输入抵扣价格" },
  283. {
  284. validator: validatePositiveNumber("抵扣价格必须为正数"),
  285. trigger: "blur"
  286. },
  287. {
  288. validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
  289. trigger: "blur"
  290. },
  291. {
  292. validator: validatePriceComparison(
  293. () => voucherModel.value.price,
  294. () => voucherModel.value.offprice,
  295. "抵扣价格不能低于售卖价格"
  296. ),
  297. trigger: "blur"
  298. }
  299. ],
  300. offprice: [
  301. { required: true, message: "请输入售卖价格" },
  302. {
  303. validator: validatePositiveNumber("售卖价格必须为正数"),
  304. trigger: "blur"
  305. },
  306. {
  307. validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
  308. trigger: "blur"
  309. },
  310. {
  311. validator: validatePriceComparison(
  312. () => voucherModel.value.price,
  313. () => voucherModel.value.offprice,
  314. "抵扣价格不能低于售卖价格"
  315. ),
  316. trigger: "blur"
  317. }
  318. ],
  319. startDate: [
  320. { required: true, message: "请选择开始售卖时间" },
  321. {
  322. validator: (rule: any, value: any, callback: any) => {
  323. if (!value) {
  324. callback();
  325. return;
  326. }
  327. const selectedDate = new Date(value);
  328. const today = new Date();
  329. today.setHours(0, 0, 0, 0);
  330. // 验证不能早于今天
  331. if (selectedDate < today) {
  332. callback(new Error("开始售卖时间不能早于当前时间"));
  333. return;
  334. }
  335. // 验证开始时间不能晚于结束时间
  336. const endDate = voucherModel.value.endDate;
  337. if (endDate) {
  338. const end = new Date(endDate);
  339. if (selectedDate > end) {
  340. callback(new Error("开始售卖时间不能晚于结束售卖时间"));
  341. return;
  342. }
  343. }
  344. callback();
  345. },
  346. trigger: "change"
  347. }
  348. ],
  349. endDate: [
  350. { required: true, message: "请选择结束售卖时间" },
  351. {
  352. validator: (rule: any, value: any, callback: any) => {
  353. if (!value) {
  354. callback();
  355. return;
  356. }
  357. const selectedDate = new Date(value);
  358. const today = new Date();
  359. today.setHours(0, 0, 0, 0);
  360. // 验证不能早于今天
  361. if (selectedDate < today) {
  362. callback(new Error("结束售卖时间不能早于当前时间"));
  363. return;
  364. }
  365. // 验证结束时间不能早于开始时间
  366. const startDate = voucherModel.value.startDate;
  367. if (startDate) {
  368. const start = new Date(startDate);
  369. if (selectedDate < start) {
  370. callback(new Error("结束售卖时间不能早于开始售卖时间"));
  371. return;
  372. }
  373. }
  374. callback();
  375. },
  376. trigger: "change"
  377. }
  378. ],
  379. usageTime: [
  380. {
  381. required: true,
  382. validator: (rule: any, value: any, callback: any) => {
  383. if (!voucherModel.value.buyUseStartTime || !voucherModel.value.buyUseEndTime) {
  384. callback(new Error("请选择使用时间"));
  385. return;
  386. }
  387. callback();
  388. },
  389. trigger: []
  390. }
  391. ],
  392. expirationType: [{ required: true, message: "请选择有效期类型" }],
  393. expirationDate: [
  394. {
  395. required: true,
  396. validator: (rule: any, value: any, callback: any) => {
  397. if (voucherModel.value.expirationType == 1) {
  398. if (value === null || value === undefined || value === "") {
  399. callback(new Error("请输入用户购买天数"));
  400. return;
  401. }
  402. // 先验证是否为正整数
  403. validatePositiveInteger("用户购买天数必须为正整数", { required: false, checkLeadingZero: false })(
  404. rule,
  405. value,
  406. (error: any) => {
  407. if (error) {
  408. callback(error);
  409. return;
  410. }
  411. // 验证最大值
  412. const numValue = Number(value);
  413. if (!isNaN(numValue) && numValue > 10000) {
  414. callback(new Error("有效期不得大于10000"));
  415. return;
  416. }
  417. callback();
  418. }
  419. );
  420. } else {
  421. callback();
  422. }
  423. },
  424. trigger: "blur"
  425. }
  426. ],
  427. validityPeriod: [
  428. {
  429. required: true,
  430. validator: (rule: any, value: any, callback: any) => {
  431. if (voucherModel.value.expirationType == 2) {
  432. if (!value || value.length !== 2) {
  433. callback(new Error("请选择指定时间段"));
  434. return;
  435. }
  436. validateDateRangeArray("开始时间必须早于结束时间", true, "时间不能早于当前时间")(rule, value, callback);
  437. } else {
  438. callback();
  439. }
  440. },
  441. trigger: "change"
  442. }
  443. ],
  444. unusedType: [{ required: true, message: "请选择不可用日期类型" }],
  445. unavailableWeekdays: [
  446. {
  447. validator: (rule: any, value: any, callback: any) => {
  448. if (voucherModel.value.unusedType == 2) {
  449. const weekdays = voucherModel.value.unavailableWeekdays || [];
  450. const holidays = voucherModel.value.unavailableHolidays || [];
  451. if (weekdays.length === 0 && holidays.length === 0) {
  452. callback(new Error("至少需要选择一个星期或节日"));
  453. return;
  454. }
  455. }
  456. callback();
  457. },
  458. trigger: "change"
  459. }
  460. ],
  461. unavailableHolidays: [
  462. {
  463. validator: (rule: any, value: any, callback: any) => {
  464. if (voucherModel.value.unusedType == 2) {
  465. const weekdays = voucherModel.value.unavailableWeekdays || [];
  466. const holidays = voucherModel.value.unavailableHolidays || [];
  467. if (weekdays.length === 0 && holidays.length === 0) {
  468. callback(new Error("至少需要选择一个星期或节日"));
  469. return;
  470. }
  471. }
  472. callback();
  473. },
  474. trigger: "change"
  475. }
  476. ],
  477. customUnavailableDates: [
  478. {
  479. required: true,
  480. validator: (rule: any, value: any, callback: any) => {
  481. if (voucherModel.value.unusedType == 3) {
  482. if (!voucherModel.value.disableDateList || voucherModel.value.disableDateList.length === 0) {
  483. callback(new Error("至少需要添加一个自定义不可用日期"));
  484. return;
  485. }
  486. validateDateListArray(
  487. () => voucherModel.value.disableDateList,
  488. "日期项未完整填写",
  489. "开始时间必须早于结束时间",
  490. true
  491. )(rule, value, callback);
  492. } else {
  493. callback();
  494. }
  495. },
  496. trigger: "change"
  497. }
  498. ],
  499. singleQty: [
  500. { required: true, message: "请输入库存数量" },
  501. {
  502. validator: (rule: any, value: any, callback: any) => {
  503. // 先验证是否为正整数
  504. validatePositiveInteger("库存必须为正整数", { required: false })(rule, value, (error: any) => {
  505. if (error) {
  506. callback(error);
  507. return;
  508. }
  509. // 验证最大值
  510. if (value) {
  511. const numValue = Number(value);
  512. if (!isNaN(numValue) && numValue > 10000) {
  513. callback(new Error("库存不得大于10000"));
  514. return;
  515. }
  516. }
  517. callback();
  518. });
  519. },
  520. trigger: "blur"
  521. }
  522. ],
  523. singleCanUseType: [{ required: true, message: "请选择单日可用数量类型" }],
  524. singleCanUse: [
  525. {
  526. required: true,
  527. validator: (rule: any, value: any, callback: any) => {
  528. if (voucherModel.value.singleCanUseType == 2) {
  529. if (!value || value.toString().trim() === "") {
  530. callback(new Error("请输入单日可用数量"));
  531. return;
  532. }
  533. // 验证是否为正整数
  534. validatePositiveInteger("单日可用数量必须为正整数", { required: false })(rule, value, (error: any) => {
  535. if (error) {
  536. callback(error);
  537. return;
  538. }
  539. const stock = voucherModel.value.singleQty;
  540. // 如果库存为空,不进行验证(由库存的验证规则处理)
  541. if (!stock || stock.toString().trim() === "") {
  542. callback();
  543. return;
  544. }
  545. const useNum = Number(value);
  546. const stockNum = Number(stock);
  547. // 如果转换失败,不进行验证(由其他验证规则处理)
  548. if (isNaN(useNum) || isNaN(stockNum)) {
  549. callback();
  550. return;
  551. }
  552. // 验证单日可用数量不能多于库存
  553. if (useNum > stockNum) {
  554. callback(new Error("单日可用数量不能多于库存"));
  555. return;
  556. }
  557. callback();
  558. });
  559. } else {
  560. callback();
  561. }
  562. },
  563. trigger: "blur"
  564. }
  565. ],
  566. purchaseLimitType: [{ required: true, message: "请选择限购数量类型" }],
  567. purchaseLimitCode: [
  568. {
  569. required: true,
  570. validator: (rule: any, value: any, callback: any) => {
  571. if (voucherModel.value.purchaseLimitType == 2) {
  572. if (!value || value.toString().trim() === "") {
  573. callback(new Error("请输入限购数量"));
  574. return;
  575. }
  576. // 验证是否为正整数
  577. validatePositiveInteger("限购数量必须为正整数", { required: false })(rule, value, (error: any) => {
  578. if (error) {
  579. callback(error);
  580. return;
  581. }
  582. const stock = voucherModel.value.singleQty;
  583. // 如果库存为空,不进行验证(由库存的验证规则处理)
  584. if (!stock || stock.toString().trim() === "") {
  585. callback();
  586. return;
  587. }
  588. const limitNum = Number(value);
  589. const stockNum = Number(stock);
  590. // 如果转换失败,不进行验证(由其他验证规则处理)
  591. if (isNaN(limitNum) || isNaN(stockNum)) {
  592. callback();
  593. return;
  594. }
  595. // 验证限购数量不能多于库存
  596. if (limitNum > stockNum) {
  597. callback(new Error("限购数量不能多于库存"));
  598. return;
  599. }
  600. callback();
  601. });
  602. } else {
  603. callback();
  604. }
  605. },
  606. trigger: "blur"
  607. }
  608. ],
  609. applyDesc: [
  610. {
  611. required: true,
  612. validator: (rule: any, value: any, callback: any) => {
  613. if (voucherModel.value.applyType == 2) {
  614. if (!value || value.toString().trim() === "") {
  615. callback(new Error("请输入适用范围"));
  616. return;
  617. }
  618. }
  619. callback();
  620. },
  621. trigger: "blur"
  622. }
  623. ]
  624. });
  625. // ==================== 代金券信息数据模型 ====================
  626. const voucherModel = ref<any>({
  627. // 代金券名称
  628. name: "",
  629. // 抵扣价格
  630. price: "",
  631. // 售卖价格
  632. offprice: "",
  633. // 开始售卖时间
  634. startDate: "",
  635. // 结束售卖时间
  636. endDate: "",
  637. // 使用时间 - 开始时间
  638. buyUseStartTime: "",
  639. // 使用时间 - 结束时间
  640. buyUseEndTime: "",
  641. // 使用时间(虚拟字段,用于表单验证)
  642. usageTime: null,
  643. // 有效期类型:1-指定天数,2-指定时间段内可用
  644. expirationType: "1",
  645. // 有效期天数(当expirationType为1时使用)
  646. expirationDate: 0,
  647. // 有效期时间段(当expirationType为2时使用)
  648. validityPeriod: [],
  649. // 不可用日期类型:1-全部日期可用,2-限制日期
  650. unusedType: "1",
  651. // 限制日期 - 星期选择(数组,存储选中的星期值)
  652. unavailableWeekdays: [],
  653. // 限制日期 - 节日选择(数组,存储选中的节日值)
  654. unavailableHolidays: [],
  655. // 自定义不可用日期列表(数组,每个元素是一个日期范围 [startDate, endDate])
  656. disableDateList: [],
  657. // 库存
  658. singleQty: "",
  659. // 单日可用数量类型:1-不限制,2-限制
  660. singleCanUseType: "1",
  661. // 单日可用数量
  662. singleCanUse: "",
  663. // 限购数量类型:1-不限制,2-限制
  664. purchaseLimitType: "1",
  665. // 限购数量
  666. purchaseLimitCode: "",
  667. // 适用范围类型:0-全场通用,1-部分不可用
  668. applyType: "1",
  669. // 适用范围(当applyType为1时使用)
  670. applyDesc: "",
  671. // 补充说明
  672. supplement: ""
  673. });
  674. // ==================== 下拉选项数据 ====================
  675. // 小时选项列表(0-24点)
  676. const hourOptions = ref(
  677. Array.from({ length: 25 }, (_, i) => ({
  678. value: String(i),
  679. label: `${i}点`
  680. }))
  681. );
  682. // 有效期类型列表
  683. const validityPeriodList = ref([
  684. { value: "1", label: "指定天数" },
  685. { value: "2", label: "指定时间段内可用" }
  686. ]);
  687. // 不可用日期类型列表
  688. const unavailableDateTypeList = ref([
  689. { value: "1", label: "全部日期可用" },
  690. { value: "2", label: "限制日期" }
  691. // { value: "3", label: "自定义不可用日期" }
  692. ]);
  693. // 适用范围类型列表
  694. const applicableScopeList = ref([
  695. { value: "1", label: "全场通用" },
  696. { value: "2", label: "部分不可用" }
  697. ]);
  698. // 单日可用数量类型列表
  699. const dailyUseLimitList = ref([
  700. { value: "1", label: "不限制" },
  701. { value: "2", label: "限制" }
  702. ]);
  703. // 限购数量类型列表
  704. const purchaseLimitList = ref([
  705. { value: "1", label: "不限制" },
  706. { value: "2", label: "限制" }
  707. ]);
  708. // 星期选项列表
  709. const weekdayList = ref([
  710. { name: "周一", id: "0", oName: "星期一" },
  711. { name: "周二", id: "1", oName: "星期二" },
  712. { name: "周三", id: "2", oName: "星期三" },
  713. { name: "周四", id: "3", oName: "星期四" },
  714. { name: "周五", id: "4", oName: "星期五" },
  715. { name: "周六", id: "5", oName: "星期六" },
  716. { name: "周日", id: "6", oName: "星期日" }
  717. ]);
  718. // 节日选项列表
  719. const holidayList: any = ref([]);
  720. // ==================== 监听器 ====================
  721. // 初始化标志,用于防止初始化时触发验证
  722. const isInitializing = ref(true);
  723. /**
  724. * 监听开始售卖时间变化
  725. * 当开始时间改变时,重新验证结束时间
  726. */
  727. watch(
  728. () => voucherModel.value.startDate,
  729. () => {
  730. if (isInitializing.value) return;
  731. if (voucherModel.value.endDate) {
  732. nextTick(() => {
  733. ruleFormRef.value?.validateField("endDate");
  734. });
  735. }
  736. }
  737. );
  738. /**
  739. * 监听结束售卖时间变化
  740. * 当结束时间改变时,重新验证开始时间
  741. */
  742. watch(
  743. () => voucherModel.value.endDate,
  744. () => {
  745. if (isInitializing.value) return;
  746. if (voucherModel.value.startDate) {
  747. nextTick(() => {
  748. ruleFormRef.value?.validateField("startDate");
  749. });
  750. }
  751. }
  752. );
  753. /**
  754. * 监听抵扣价格变化
  755. * 当抵扣价格改变时,重新验证售卖价格
  756. */
  757. watch(
  758. () => voucherModel.value.price,
  759. () => {
  760. if (isInitializing.value) return;
  761. if (voucherModel.value.offprice) {
  762. nextTick(() => {
  763. ruleFormRef.value?.validateField("offprice");
  764. });
  765. }
  766. }
  767. );
  768. /**
  769. * 监听售卖价格变化
  770. * 当售卖价格改变时,重新验证抵扣价格
  771. */
  772. watch(
  773. () => voucherModel.value.offprice,
  774. () => {
  775. if (isInitializing.value) return;
  776. if (voucherModel.value.price) {
  777. nextTick(() => {
  778. ruleFormRef.value?.validateField("price");
  779. });
  780. }
  781. }
  782. );
  783. /**
  784. * 监听库存变化
  785. * 当库存改变时,重新验证单日可用数量和限购数量
  786. */
  787. watch(
  788. () => voucherModel.value.singleQty,
  789. () => {
  790. if (isInitializing.value) return;
  791. if (voucherModel.value.singleCanUseType == 2 && voucherModel.value.singleCanUse) {
  792. nextTick(() => {
  793. ruleFormRef.value?.validateField("singleCanUse");
  794. });
  795. }
  796. if (voucherModel.value.purchaseLimitType == 2 && voucherModel.value.purchaseLimitCode) {
  797. nextTick(() => {
  798. ruleFormRef.value?.validateField("purchaseLimitCode");
  799. });
  800. }
  801. }
  802. );
  803. /**
  804. * 监听单日可用数量类型变化
  805. * 当切换到"不限制"时,清空输入框的值
  806. */
  807. watch(
  808. () => voucherModel.value.singleCanUseType,
  809. newVal => {
  810. if (newVal == 1) {
  811. voucherModel.value.singleCanUse = "";
  812. nextTick(() => {
  813. ruleFormRef.value?.clearValidate("singleCanUse");
  814. });
  815. }
  816. }
  817. );
  818. /**
  819. * 监听限购数量类型变化
  820. * 当切换到"不限制"时,清空输入框的值
  821. */
  822. watch(
  823. () => voucherModel.value.purchaseLimitType,
  824. newVal => {
  825. if (newVal == 1) {
  826. voucherModel.value.purchaseLimitCode = "";
  827. nextTick(() => {
  828. ruleFormRef.value?.clearValidate("purchaseLimitCode");
  829. });
  830. }
  831. }
  832. );
  833. /**
  834. * 监听适用范围类型变化
  835. * 当切换到"全场通用"时,清空输入框的值
  836. */
  837. watch(
  838. () => voucherModel.value.applyType,
  839. newVal => {
  840. if (newVal == 1) {
  841. voucherModel.value.applyDesc = "";
  842. nextTick(() => {
  843. ruleFormRef.value?.clearValidate("applyDesc");
  844. });
  845. }
  846. }
  847. );
  848. /**
  849. * 监听使用开始时间变化
  850. * 更新虚拟字段以支持表单验证
  851. */
  852. watch(
  853. () => voucherModel.value.buyUseStartTime,
  854. () => {
  855. // 更新虚拟字段
  856. voucherModel.value.usageTime =
  857. voucherModel.value.buyUseStartTime && voucherModel.value.buyUseEndTime
  858. ? [voucherModel.value.buyUseStartTime, voucherModel.value.buyUseEndTime]
  859. : null;
  860. }
  861. );
  862. /**
  863. * 监听使用结束时间变化
  864. * 更新虚拟字段以支持表单验证
  865. */
  866. watch(
  867. () => voucherModel.value.buyUseEndTime,
  868. () => {
  869. // 更新虚拟字段
  870. voucherModel.value.usageTime =
  871. voucherModel.value.buyUseStartTime && voucherModel.value.buyUseEndTime
  872. ? [voucherModel.value.buyUseStartTime, voucherModel.value.buyUseEndTime]
  873. : null;
  874. }
  875. );
  876. /**
  877. * 监听不可用日期类型变化
  878. * 当切换到自定义不可用日期时,确保至少有一个日期项
  879. */
  880. watch(
  881. () => voucherModel.value.unusedType,
  882. newVal => {
  883. if (newVal == 3) {
  884. // 切换到自定义不可用日期时,如果disableDateList为空,则添加一个默认项
  885. if (!voucherModel.value.disableDateList || voucherModel.value.disableDateList.length === 0) {
  886. voucherModel.value.disableDateList = [null];
  887. }
  888. }
  889. },
  890. { immediate: true }
  891. );
  892. // ==================== 生命周期钩子 ====================
  893. /**
  894. * 组件挂载时初始化
  895. * 从路由参数中获取页面类型和ID
  896. */
  897. onMounted(async () => {
  898. id.value = (route.query.id as string) || "";
  899. type.value = (route.query.type as string) || "";
  900. // 加载节日列表
  901. let params = {
  902. year: new Date().getFullYear(),
  903. page: 1,
  904. size: 500,
  905. openFlag: 1,
  906. holidayName: ""
  907. };
  908. let res: any = await getHolidayList(params);
  909. if (res && res.code == 200) {
  910. holidayList.value = res.data.records;
  911. }
  912. // 编辑模式下加载数据
  913. if (type.value != "add") {
  914. let res: any = await getVoucherDetail({ id: id.value });
  915. voucherModel.value = { ...voucherModel.value, ...res.data };
  916. // 处理有效期时间段:将时间戳字符串转换为数字数组
  917. if (voucherModel.value.validityPeriod && voucherModel.value.expirationType == 2) {
  918. const periodArray = voucherModel.value.validityPeriod.split(",");
  919. voucherModel.value.validityPeriod = periodArray
  920. .map((item: string) => Number(item.trim()))
  921. .filter((item: number) => !isNaN(item));
  922. } else {
  923. voucherModel.value.validityPeriod = [];
  924. }
  925. // 确保星期和节日字段存在;
  926. if (voucherModel.value.unusedType == 2) {
  927. const listVal = voucherModel.value.unusedDate ? voucherModel.value.unusedDate.split(";") : [];
  928. voucherModel.value.unavailableWeekdays = listVal[0] ? listVal[0].split(",").filter((item: string) => item) : [];
  929. voucherModel.value.unavailableHolidays = listVal[1] ? listVal[1].split(",").filter((item: string) => item) : [];
  930. }
  931. // 确保自定义不可用日期字段存在;
  932. if (voucherModel.value.unusedType === 3) {
  933. if (!voucherModel.value.disableDateList || voucherModel.value.disableDateList.length === 0) {
  934. voucherModel.value.disableDateList = [null];
  935. }
  936. }
  937. // 处理单日可用数量类型:如果有值,设置为"限制",否则设置为"不限制"
  938. if (voucherModel.value.singleCanUse && voucherModel.value.singleCanUse.toString().trim() !== "") {
  939. voucherModel.value.singleCanUseType = "2";
  940. } else {
  941. voucherModel.value.singleCanUseType = "1";
  942. voucherModel.value.singleCanUse = "";
  943. }
  944. // 处理限购数量类型:如果有值,设置为"限制",否则设置为"不限制"
  945. if (voucherModel.value.purchaseLimitCode && voucherModel.value.purchaseLimitCode.toString().trim() !== "") {
  946. voucherModel.value.purchaseLimitType = "2";
  947. } else {
  948. voucherModel.value.purchaseLimitType = "1";
  949. voucherModel.value.purchaseLimitCode = "";
  950. }
  951. // 处理适用范围类型:如果有值,设置为"部分不可用",否则设置为"全场通用"
  952. if (voucherModel.value.applyDesc && voucherModel.value.applyDesc.toString().trim() !== "") {
  953. voucherModel.value.applyType = "2";
  954. } else {
  955. voucherModel.value.applyType = "1";
  956. voucherModel.value.applyDesc = "";
  957. }
  958. console.log(voucherModel.value);
  959. }
  960. await nextTick();
  961. ruleFormRef.value?.clearValidate();
  962. isInitializing.value = false;
  963. });
  964. // ==================== 事件处理函数 ====================
  965. /**
  966. * 返回上一页
  967. */
  968. const goBack = () => {
  969. router.go(-1);
  970. };
  971. /**
  972. * 切换星期选择
  973. * @param value 星期值
  974. */
  975. const toggleWeekday = (value: string) => {
  976. if (!voucherModel.value.unavailableWeekdays) {
  977. voucherModel.value.unavailableWeekdays = [];
  978. }
  979. const index = voucherModel.value.unavailableWeekdays.indexOf(value);
  980. if (index > -1) {
  981. voucherModel.value.unavailableWeekdays.splice(index, 1);
  982. } else {
  983. voucherModel.value.unavailableWeekdays.push(value);
  984. }
  985. // 触发表单验证(同时验证星期和节日字段)
  986. nextTick(() => {
  987. ruleFormRef.value?.validateField("unavailableWeekdays");
  988. ruleFormRef.value?.validateField("unavailableHolidays");
  989. });
  990. };
  991. /**
  992. * 切换节日选择
  993. * @param value 节日值
  994. */
  995. const toggleHoliday = (value: string | number) => {
  996. if (!voucherModel.value.unavailableHolidays) {
  997. voucherModel.value.unavailableHolidays = [];
  998. }
  999. // 统一转换为字符串进行比较
  1000. const valueStr = String(value);
  1001. const index = voucherModel.value.unavailableHolidays.findIndex((item: any) => String(item) === valueStr);
  1002. if (index > -1) {
  1003. voucherModel.value.unavailableHolidays.splice(index, 1);
  1004. } else {
  1005. voucherModel.value.unavailableHolidays.push(valueStr);
  1006. }
  1007. // 触发表单验证(同时验证星期和节日字段)
  1008. nextTick(() => {
  1009. ruleFormRef.value?.validateField("unavailableWeekdays");
  1010. ruleFormRef.value?.validateField("unavailableHolidays");
  1011. });
  1012. };
  1013. /**
  1014. * 添加自定义不可用日期
  1015. */
  1016. const addDate = () => {
  1017. if (!voucherModel.value.disableDateList) {
  1018. voucherModel.value.disableDateList = [];
  1019. }
  1020. voucherModel.value.disableDateList.push(null);
  1021. };
  1022. /**
  1023. * 删除自定义不可用日期
  1024. * @param index 要删除的日期索引
  1025. */
  1026. const removeDate = (index: number) => {
  1027. if (voucherModel.value.disableDateList.length <= 1) {
  1028. ElMessage.warning("至少需要保留一个日期项");
  1029. return;
  1030. }
  1031. voucherModel.value.disableDateList.splice(index, 1);
  1032. // 删除日期项后,重新验证表单以清除旧的验证错误
  1033. nextTick(() => {
  1034. ruleFormRef.value?.validateField("customUnavailableDates");
  1035. });
  1036. };
  1037. // ==================== 表单引用 ====================
  1038. const ruleFormRef = ref<FormInstance>(); // 表单引用
  1039. /**
  1040. * 提交数据(新增/编辑)
  1041. * 验证表单,通过后调用相应的API接口
  1042. */
  1043. const handleSubmit = async (submitType?: string) => {
  1044. // 组装提交参数
  1045. let params: any = { ...voucherModel.value };
  1046. params.storeId = localGet("createdId");
  1047. params.status = 1;
  1048. // 处理有效期:只有当expirationType为2(指定时间段内可用)时才处理validityPeriod
  1049. if (params.expirationType == 2 && params.validityPeriod && Array.isArray(params.validityPeriod)) {
  1050. params.validityPeriod = params.validityPeriod.join(",");
  1051. } else if (params.expirationType == 1) {
  1052. // 指定天数模式,不需要validityPeriod字段
  1053. params.validityPeriod = "";
  1054. }
  1055. // 处理不可用日期
  1056. if (params.unusedType == 2) {
  1057. params.unusedDate = params.unavailableWeekdays.join(",") + ";" + params.unavailableHolidays.join(",");
  1058. } else if (params.unusedType == 3) {
  1059. // 处理自定义不可用日期
  1060. if (params.disableDateList && params.disableDateList.length > 0) {
  1061. params.unusedDate = params.disableDateList
  1062. .map((dateRange: any) => (dateRange && dateRange.length === 2 ? dateRange.join(",") : ""))
  1063. .filter((item: string) => item)
  1064. .join(";");
  1065. }
  1066. }
  1067. // 处理单日可用数量:如果选择"不限制",清空值
  1068. if (params.singleCanUseType == 1) {
  1069. params.singleCanUse = "";
  1070. }
  1071. // 处理限购数量:如果选择"不限制",清空值
  1072. if (params.purchaseLimitType == 1) {
  1073. params.purchaseLimitCode = "";
  1074. }
  1075. params.dataType = submitType ? 1 : 0;
  1076. delete params.unavailableWeekdays;
  1077. delete params.unavailableHolidays;
  1078. delete params.disableDateList;
  1079. delete params.singleCanUseType;
  1080. delete params.purchaseLimitType;
  1081. console.log("提交参数:", params);
  1082. if (submitType) {
  1083. if (!voucherModel.value.name) {
  1084. ElMessage.warning("请填写代金券名称");
  1085. return;
  1086. }
  1087. let res: any = await addOrUpdateCoupon(params);
  1088. if (res && res.code == 200) {
  1089. ElMessage.success("保存成功");
  1090. goBack();
  1091. }
  1092. return;
  1093. }
  1094. // 验证表单
  1095. ruleFormRef.value!.validate(async (valid: boolean) => {
  1096. if (!valid) return;
  1097. let res: any = await addOrUpdateCoupon(params);
  1098. if (res && res.code == 200) {
  1099. ElMessage.success("创建成功,请耐心等待审核");
  1100. goBack();
  1101. }
  1102. });
  1103. };
  1104. // ==================== 工具函数 ====================
  1105. /**
  1106. * 禁用开始售卖时间的日期
  1107. * 不能选择早于当前时间的日期
  1108. */
  1109. const disabledStartDate = (time: Date) => {
  1110. const today = new Date();
  1111. today.setHours(0, 0, 0, 0);
  1112. return time.getTime() < today.getTime();
  1113. };
  1114. /**
  1115. * 禁用结束售卖时间的日期
  1116. * 不能选择早于当前时间的日期,也不能选择早于或等于开始售卖时间的日期
  1117. */
  1118. const disabledEndDate = (time: Date) => {
  1119. const today = new Date();
  1120. today.setHours(0, 0, 0, 0);
  1121. if (time.getTime() < today.getTime()) {
  1122. return true;
  1123. }
  1124. if (voucherModel.value.startDate) {
  1125. const startDate = new Date(voucherModel.value.startDate);
  1126. startDate.setHours(0, 0, 0, 0);
  1127. return time.getTime() < startDate.getTime();
  1128. }
  1129. return false;
  1130. };
  1131. /**
  1132. * 禁用有效期时间段的日期
  1133. * 不能选择早于当前时间的日期
  1134. * @param time 日期对象
  1135. * @returns 是否禁用该日期
  1136. */
  1137. const disabledValidityDate = (time: Date) => {
  1138. const today = new Date();
  1139. today.setHours(0, 0, 0, 0);
  1140. return time.getTime() < today.getTime();
  1141. };
  1142. /**
  1143. * 禁用自定义不可用日期的日期
  1144. * 不能选择早于当前时间的日期
  1145. * @param time 日期对象
  1146. * @returns 是否禁用该日期
  1147. */
  1148. const disabledCustomUnavailableDate = (time: Date) => {
  1149. const today = new Date();
  1150. today.setHours(0, 0, 0, 0);
  1151. return time.getTime() < today.getTime();
  1152. };
  1153. </script>
  1154. <style scoped lang="scss">
  1155. /* 页面容器 */
  1156. .table-box {
  1157. display: flex;
  1158. flex-direction: column;
  1159. height: auto !important;
  1160. min-height: 100%;
  1161. }
  1162. /* 头部区域 */
  1163. .header {
  1164. display: flex;
  1165. align-items: center;
  1166. padding: 20px;
  1167. border-bottom: 1px solid #e4e7ed;
  1168. }
  1169. .title {
  1170. flex: 1;
  1171. margin: 0;
  1172. font-size: 18px;
  1173. font-weight: bold;
  1174. text-align: center;
  1175. }
  1176. /* 内容区域布局 */
  1177. .content {
  1178. display: flex;
  1179. flex: 1;
  1180. column-gap: 20px;
  1181. width: 98%;
  1182. margin: 20px auto;
  1183. /* 左侧内容区域 */
  1184. .contentLeft {
  1185. width: 50%;
  1186. }
  1187. /* 右侧内容区域 */
  1188. .contentRight {
  1189. width: 50%;
  1190. }
  1191. }
  1192. /* 模块容器 */
  1193. .model {
  1194. margin-bottom: 50px;
  1195. }
  1196. /* 表单容器 */
  1197. .formBox {
  1198. display: flex;
  1199. flex-direction: column;
  1200. width: 100%;
  1201. min-height: 100%;
  1202. }
  1203. /* 底部按钮容器 - 居中显示 */
  1204. .button-container {
  1205. display: flex;
  1206. gap: 12px;
  1207. align-items: center;
  1208. justify-content: center;
  1209. padding: 20px 0;
  1210. margin-top: 20px;
  1211. }
  1212. /* 有效期天数容器 */
  1213. .expiration-date-container {
  1214. display: flex;
  1215. gap: 12px;
  1216. align-items: center;
  1217. width: fit-content;
  1218. padding: 8px 12px;
  1219. border-radius: 4px;
  1220. }
  1221. /* 有效期标签文字 */
  1222. .expiration-label {
  1223. font-size: 14px;
  1224. color: #606266;
  1225. white-space: nowrap;
  1226. }
  1227. /* 有效期输入框 */
  1228. .expiration-input {
  1229. width: 150px;
  1230. }
  1231. /* 不可用日期容器 */
  1232. .unavailable-dates-container {
  1233. display: flex;
  1234. flex-direction: column;
  1235. gap: 20px;
  1236. width: 100%;
  1237. }
  1238. /* 日期选择区块 */
  1239. .date-select-section {
  1240. display: flex;
  1241. flex-direction: column;
  1242. gap: 12px;
  1243. }
  1244. /* 区块标题 */
  1245. .section-title {
  1246. font-size: 14px;
  1247. font-weight: 500;
  1248. color: #606266;
  1249. }
  1250. /* 按钮组 */
  1251. .button-group {
  1252. display: flex;
  1253. flex-wrap: wrap;
  1254. gap: 10px;
  1255. }
  1256. /* 日期选择按钮 */
  1257. .date-select-btn {
  1258. min-width: 80px;
  1259. height: 36px;
  1260. padding: 8px 16px;
  1261. margin: 0;
  1262. font-size: 14px;
  1263. border-radius: 4px;
  1264. transition: all 0.1s;
  1265. }
  1266. /* 日期选择器容器 */
  1267. .date-picker-container {
  1268. display: flex;
  1269. flex-direction: column;
  1270. gap: 12px;
  1271. width: 100%;
  1272. }
  1273. /* 添加日期按钮 */
  1274. .add-date-btn {
  1275. width: fit-content;
  1276. margin-bottom: 8px;
  1277. }
  1278. /* 日期项容器 */
  1279. .date-item {
  1280. display: flex;
  1281. gap: 12px;
  1282. align-items: center;
  1283. padding: 8px;
  1284. border-radius: 4px;
  1285. transition: background-color 0.1s;
  1286. &:hover {
  1287. background-color: #ecf5ff;
  1288. }
  1289. }
  1290. /* 日期选择器 */
  1291. .date-item .date-picker {
  1292. flex: 1;
  1293. max-width: 500px;
  1294. }
  1295. /* 删除按钮 */
  1296. .date-item .delete-btn {
  1297. flex-shrink: 0;
  1298. }
  1299. /* 时间范围容器 */
  1300. .time-range-container {
  1301. display: flex;
  1302. gap: 12px;
  1303. align-items: center;
  1304. width: 100%;
  1305. }
  1306. /* 时间选择器 */
  1307. .time-picker {
  1308. flex: 1;
  1309. max-width: 200px;
  1310. }
  1311. /* 时间分隔符 */
  1312. .time-separator {
  1313. font-size: 14px;
  1314. color: #606266;
  1315. white-space: nowrap;
  1316. }
  1317. </style>