index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. <template>
  2. <div class="table-box button-table">
  3. <ProTable
  4. ref="proTable"
  5. :columns="columns"
  6. :request-api="getTableList"
  7. :init-param="initParam"
  8. :data-callback="dataCallback"
  9. :key="activeName"
  10. >
  11. <!-- 表格 header 按钮 -->
  12. <template #tableHeader="scope">
  13. <div class="table-header-btn">
  14. <div class="header-actions">
  15. <el-tabs v-model="activeName" class="tabs" @tab-click="handleClick">
  16. <el-tab-pane v-for="tab in allTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
  17. </el-tabs>
  18. </div>
  19. </div>
  20. </template>
  21. <template #tableHeaderRight="scope">
  22. <div class="action-buttons">
  23. <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying" v-if="type"> 新建代金券 </el-button>
  24. <el-button :icon="Plus" class="button" type="primary" @click="newCoupon" v-if="typeCoupon"> 新建优惠券 </el-button>
  25. </div>
  26. </template>
  27. <template #status="scope">
  28. <!-- 代金券:显示状态和审核状态两行 -->
  29. <template v-if="activeName === '1'">
  30. <p style="margin: 0; line-height: 1.5">状态:{{ getStatusLabel(scope.row.status, scope.row.dataType) || "--" }}</p>
  31. <p style="margin: 0; line-height: 1.5">审核状态:{{ scope.row.statusName || "--" }}</p>
  32. </template>
  33. <!-- 优惠券:只显示状态一行 -->
  34. <template v-else>
  35. <span>{{ getStatusLabel(scope.row.status) }}</span>
  36. </template>
  37. </template>
  38. <!-- 表格操作 -->
  39. <template #operation="scope">
  40. <!-- 上架按钮 -->
  41. <el-button
  42. v-if="canShowButton(scope.row.status, currentOperationPermissions.上架)"
  43. link
  44. type="primary"
  45. @click="changeTypes(scope.row, isVoucher ? 1 : 5)"
  46. >
  47. 上架
  48. </el-button>
  49. <!-- 下架按钮 -->
  50. <el-button
  51. v-if="canShowButton(scope.row.status, currentOperationPermissions.下架)"
  52. link
  53. type="primary"
  54. @click="changeTypes(scope.row, 6)"
  55. >
  56. 下架
  57. </el-button>
  58. <!-- 修改库存按钮 -->
  59. <el-button
  60. v-if="canShowButton(scope.row.status, currentOperationPermissions.修改库存)"
  61. link
  62. type="primary"
  63. @click="changeInventory(scope.row)"
  64. >
  65. 修改库存
  66. </el-button>
  67. <!-- 查看拒绝原因按钮(仅代金券) -->
  68. <el-button
  69. v-if="isVoucher && canShowButton(scope.row.status, VOUCHER_OPERATION_PERMISSIONS.查看拒绝原因)"
  70. link
  71. type="primary"
  72. @click="viewRejectReason(scope.row)"
  73. >
  74. 查看拒绝原因
  75. </el-button>
  76. <!-- 查看详情按钮 -->
  77. <el-button
  78. v-if="
  79. isVoucher
  80. ? canShowButton(scope.row.status, currentOperationPermissions.查看详情) && scope.row.dataType != 1
  81. : canShowButton(scope.row.status, currentOperationPermissions.查看详情)
  82. "
  83. link
  84. type="primary"
  85. @click="toDetail(scope.row)"
  86. >
  87. 查看详情
  88. </el-button>
  89. <!-- 编辑按钮 -->
  90. <el-button
  91. v-if="
  92. isVoucher
  93. ? canShowButton(scope.row.status, currentOperationPermissions.编辑) ||
  94. (scope.row.dataType == 1 && scope.row.status == 0)
  95. : canShowButton(scope.row.status, currentOperationPermissions.编辑)
  96. "
  97. link
  98. type="primary"
  99. @click="editRow(scope.row)"
  100. >
  101. 编辑
  102. </el-button>
  103. <!-- 删除按钮 -->
  104. <el-button
  105. v-if="
  106. isVoucher
  107. ? canShowButton(scope.row.status, currentOperationPermissions.删除) ||
  108. (scope.row.dataType == 1 && scope.row.status == 0)
  109. : canShowButton(scope.row.status, currentOperationPermissions.删除)
  110. "
  111. link
  112. type="primary"
  113. @click="deleteRow(scope.row)"
  114. >
  115. 删除
  116. </el-button>
  117. </template>
  118. </ProTable>
  119. <el-dialog v-model="dialogFormVisible" title="修改库存" width="500">
  120. <el-form ref="ruleFormRef" :model="formInventory" :rules="rules" @submit.prevent>
  121. <el-form-item label="套餐名">
  122. {{ formInventory.name }}
  123. </el-form-item>
  124. <el-form-item label="剩余库存"> {{ formInventory.singleQty }}张 </el-form-item>
  125. <el-form-item label="修改库存" prop="newInventory">
  126. <el-input v-model="formInventory.newInventory" placeholder="请输入" />
  127. </el-form-item>
  128. </el-form>
  129. <template #footer>
  130. <div class="dialog-footer">
  131. <el-button @click="closeDialog"> 取消 </el-button>
  132. <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
  133. </div>
  134. </template>
  135. </el-dialog>
  136. <!-- 查看拒绝原因弹窗 -->
  137. <el-dialog v-model="rejectReasonDialogVisible" title="查看拒绝原因" width="600px">
  138. <div class="reject-reason-content">
  139. <div class="reject-reason-item">
  140. <div class="reject-reason-label">代金券名称:</div>
  141. <div class="reject-reason-value">
  142. {{ rejectReasonData.name || "--" }}
  143. </div>
  144. </div>
  145. <div class="reject-reason-item">
  146. <div class="reject-reason-label">拒绝原因:</div>
  147. <div class="reject-reason-value reject-reason-text">
  148. {{ rejectReasonData.approvalComments || "暂无拒绝原因" }}
  149. </div>
  150. </div>
  151. </div>
  152. <template #footer>
  153. <div class="dialog-footer">
  154. <el-button type="primary" @click="closeRejectReasonDialog"> 确定 </el-button>
  155. </div>
  156. </template>
  157. </el-dialog>
  158. </div>
  159. </template>
  160. <script setup lang="tsx" name="voucherManagement">
  161. import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
  162. import { useRouter, useRoute } from "vue-router";
  163. import type { FormInstance, FormRules } from "element-plus";
  164. import { ElMessage } from "element-plus";
  165. import ProTable from "@/components/ProTable/index.vue";
  166. import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
  167. import { Plus } from "@element-plus/icons-vue";
  168. import { delThaliById, getThaliList, updateNum, updateStatus } from "@/api/modules/voucherManagement";
  169. import { delCouponById, updateCouponSingleQty, updateCouponStatus } from "@/api/modules/couponManagement";
  170. import { ElMessageBox } from "element-plus/es";
  171. import { localGet, usePermission } from "@/utils";
  172. import { formatCurrency } from "@/utils/formatCurrency";
  173. const router = useRouter();
  174. const route = useRoute();
  175. const dialogFormVisible = ref(false);
  176. const formInventory: any = ref({
  177. id: "",
  178. name: "",
  179. singleQty: "",
  180. newInventory: ""
  181. });
  182. const activeName = ref("1");
  183. // 查看拒绝原因弹窗相关
  184. const rejectReasonDialogVisible = ref(false);
  185. const rejectReasonData = ref<any>({
  186. name: "",
  187. approvalComments: ""
  188. });
  189. // 定义表单类型
  190. interface RuleForm {
  191. newInventory: string;
  192. }
  193. const ruleFormRef = ref<FormInstance>();
  194. const rules = reactive<FormRules<RuleForm>>({
  195. newInventory: [
  196. { required: true, message: "请输入库存数量", trigger: "blur" },
  197. {
  198. pattern: /^(0|[1-9][0-9]*)$/,
  199. message: "请输入整数,不允许输入小数,负数",
  200. trigger: "blur"
  201. },
  202. {
  203. validator: (rule: any, value: any, callback: any) => {
  204. if (value) {
  205. const numValue = Number(value);
  206. if (!isNaN(numValue) && numValue > 10000) {
  207. callback(new Error("库存不得大于10000"));
  208. return;
  209. }
  210. // 验证新库存值不能小于剩余库存
  211. const currentQty = Number(formInventory.value.singleQty) || 0;
  212. if (numValue < currentQty) {
  213. callback(new Error(`库存不能小于剩余库存(${currentQty})`));
  214. return;
  215. }
  216. }
  217. callback();
  218. },
  219. trigger: "blur"
  220. }
  221. ]
  222. });
  223. const statusEnum = [
  224. { label: "草稿", value: "0" },
  225. { label: "进行中", value: "5" },
  226. { label: "未开始", value: "2" },
  227. { label: "已下架", value: "6" },
  228. { label: "已售罄", value: "4" },
  229. { label: "已结束", value: "7" }
  230. ];
  231. const statusEnumY = [
  232. { label: "草稿", value: "3" },
  233. { label: "进行中", value: "1" },
  234. { label: "已结束", value: "2" }
  235. ];
  236. // ProTable 实例(需要在使用它的地方之前定义)
  237. const proTable = ref<ProTableInstance>();
  238. // 代金券表格列配置
  239. const voucherColumns = reactive<ColumnProps<any>[]>([
  240. {
  241. prop: "name",
  242. label: "券名称",
  243. search: {
  244. el: "input"
  245. }
  246. },
  247. {
  248. prop: "price",
  249. label: "价格",
  250. render: (scope: any) => {
  251. return formatCurrency(scope.row.price, 2, "¥") || "--";
  252. }
  253. },
  254. {
  255. prop: "saleNum",
  256. label: "已售",
  257. render: scope => {
  258. return scope.row.saleNum === null || scope.row.saleNum === undefined || scope.row.saleNum === "" ? 0 : scope.row.saleNum;
  259. }
  260. },
  261. {
  262. prop: "singleQty",
  263. label: "剩余库存",
  264. render: scope => {
  265. return scope.row.singleQty === null || scope.row.singleQty === undefined || scope.row.singleQty === ""
  266. ? 0
  267. : scope.row.singleQty + "张";
  268. }
  269. },
  270. {
  271. prop: "endDate",
  272. label: "结束时间",
  273. render: (scope: any) => {
  274. return scope.row.endDate?.replace(/-/g, "/") || "--";
  275. }
  276. },
  277. {
  278. prop: "status",
  279. label: "状态",
  280. search: {
  281. el: "select",
  282. props: { placeholder: "请选择" }
  283. },
  284. enum: statusEnum,
  285. fieldNames: { label: "label", value: "value" }
  286. },
  287. { prop: "operation", label: "操作", fixed: "right", width: 330 }
  288. ]);
  289. // 优惠券表格列配置
  290. const couponColumns = reactive<ColumnProps<any>[]>([
  291. {
  292. prop: "name",
  293. label: "券名称",
  294. search: {
  295. el: "input"
  296. }
  297. },
  298. {
  299. prop: "quantityClaimed",
  300. label: "已领",
  301. render: scope => {
  302. return scope.row.quantityClaimed === null || scope.row.quantityClaimed === undefined || scope.row.quantityClaimed === ""
  303. ? 0
  304. : scope.row.quantityClaimed;
  305. }
  306. },
  307. {
  308. prop: "singleQty",
  309. label: "剩余库存",
  310. render: scope => {
  311. return scope.row.singleQty === null || scope.row.singleQty === undefined || scope.row.singleQty === ""
  312. ? 0
  313. : scope.row.singleQty + "张";
  314. }
  315. },
  316. {
  317. prop: "endGetDate",
  318. label: "结束时间",
  319. render: (scope: any) => {
  320. return scope.row.endGetDate?.replace(/-/g, "/") || "--";
  321. }
  322. },
  323. {
  324. prop: "couponStatus",
  325. label: "状态",
  326. isShow: false,
  327. search: {
  328. el: "select",
  329. props: { placeholder: "请选择" }
  330. },
  331. enum: statusEnumY,
  332. fieldNames: { label: "label", value: "value" }
  333. },
  334. {
  335. prop: "status",
  336. label: "状态"
  337. },
  338. { prop: "operation", label: "操作", fixed: "right", width: 330 }
  339. ]);
  340. // 根据当前选中的tab动态返回列配置
  341. const columns = computed(() => {
  342. return activeName.value === "1" ? voucherColumns : couponColumns;
  343. });
  344. const allTabOptions = [
  345. { label: "代金券", name: "1" },
  346. { label: "优惠券", name: "2" }
  347. ];
  348. // 状态枚举:1草稿 1待审核 2未开始 3审核拒绝 4已售罄 5进行中 6已下架 7已结束
  349. const VO_STATUS = {
  350. 草稿: 1,
  351. 待审核: 1,
  352. 未开始: 2,
  353. 审核拒绝: 3,
  354. 已售罄: 4,
  355. 进行中: 5,
  356. 已下架: 6,
  357. 已结束: 7
  358. } as const;
  359. // 状态枚举:1草稿 1待审核 2未开始 3审核拒绝 4已售罄 5进行中 6已下架 7已结束
  360. const CO_STATUS = {
  361. 进行中: 0,
  362. 已结束: 1,
  363. 未开始: 2,
  364. 已下架: 3,
  365. 已售罄: 4,
  366. 草稿: 5
  367. } as const;
  368. // 代金券操作按钮权限配置:定义每个操作按钮在哪些状态下显示
  369. const VOUCHER_OPERATION_PERMISSIONS = {
  370. // 查看详情:待审核、未开始、审核拒绝、进行中、已售罄、已下架
  371. 查看详情: [VO_STATUS.待审核, VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.进行中, VO_STATUS.已售罄, VO_STATUS.已下架],
  372. // 上架:未开始、已下架
  373. 上架: [VO_STATUS.未开始, VO_STATUS.已下架],
  374. // 下架:进行中
  375. 下架: [VO_STATUS.进行中],
  376. // 修改库存:未开始、进行中、已售罄
  377. 修改库存: [VO_STATUS.未开始, VO_STATUS.进行中, VO_STATUS.已售罄],
  378. // 编辑:草稿、审核拒绝、已售罄、已下架、已结束
  379. 编辑: [VO_STATUS.草稿, VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已下架, VO_STATUS.已结束],
  380. // 删除:草稿、未开始、审核拒绝、已售罄、已结束
  381. 删除: [VO_STATUS.草稿, VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已结束],
  382. // 查看拒绝原因:审核拒绝
  383. 查看拒绝原因: [VO_STATUS.审核拒绝]
  384. } as const;
  385. // 优惠券操作按钮权限配置
  386. const COUPON_OPERATION_PERMISSIONS = {
  387. // 查看详情:草稿、未开始、进行中、已下架、已结束、已售罄
  388. 查看详情: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
  389. // 上架:未开始、已下架
  390. 上架: [CO_STATUS.未开始, CO_STATUS.已下架],
  391. // 下架:进行中
  392. 下架: [CO_STATUS.进行中],
  393. // 修改库存:未开始、进行中、已售罄
  394. 修改库存: [CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已售罄],
  395. // 编辑:草稿、未开始、进行中、已下架、已结束、已售罄
  396. 编辑: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
  397. // 删除:草稿、已结束、已售罄
  398. 删除: [CO_STATUS.草稿, CO_STATUS.已结束, CO_STATUS.已售罄]
  399. } as const;
  400. // 判断按钮是否显示的工具函数
  401. const canShowButton = (status: number, allowedStatuses: readonly number[]) => {
  402. return allowedStatuses.includes(status);
  403. };
  404. // 根据当前tab获取操作权限配置
  405. const currentOperationPermissions = computed(() => {
  406. return activeName.value === "1" ? VOUCHER_OPERATION_PERMISSIONS : COUPON_OPERATION_PERMISSIONS;
  407. });
  408. // 判断是否为代金券
  409. const isVoucher = computed(() => activeName.value === "1");
  410. // 判断是否为优惠券
  411. const isCoupon = computed(() => activeName.value === "2");
  412. // 获取状态标签
  413. const getStatusLabel = (status: number, dataType?: number) => {
  414. // 代金券且状态为1时,根据dataType判断
  415. if (isVoucher.value && status === 1) {
  416. if (dataType === 0) {
  417. return "待审核";
  418. } else if (dataType === 1) {
  419. return "草稿";
  420. }
  421. }
  422. // 根据当前选中的tab选择对应的状态枚举
  423. const statusEnum = isVoucher.value ? VO_STATUS : CO_STATUS;
  424. // 从状态枚举中查找对应的标签
  425. for (const [label, value] of Object.entries(statusEnum)) {
  426. if (value === status) {
  427. return label;
  428. }
  429. }
  430. return "--";
  431. };
  432. // 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
  433. const initParam = reactive({
  434. storeId: localGet("createdId"),
  435. groupType: localGet("businessSection"),
  436. couponType: activeName
  437. });
  438. const type = ref(false);
  439. const typeCoupon = ref(false);
  440. // 恢复 activeName 的函数
  441. const restoreActiveName = () => {
  442. const savedActiveName = sessionStorage.getItem("ticketManagement_activeName");
  443. if (savedActiveName && (savedActiveName === "1" || savedActiveName === "2")) {
  444. activeName.value = savedActiveName;
  445. // 恢复后清除 sessionStorage,避免影响其他场景
  446. sessionStorage.removeItem("ticketManagement_activeName");
  447. }
  448. };
  449. // 页面加载时触发查询
  450. onMounted(async () => {
  451. type.value = await usePermission("新建代金券");
  452. typeCoupon.value = await usePermission("新建优惠券");
  453. // 从 sessionStorage 恢复 activeName
  454. restoreActiveName();
  455. proTable.value?.getTableList();
  456. });
  457. // 从其他页面返回时触发查询
  458. onActivated(() => {
  459. // 从 sessionStorage 恢复 activeName
  460. restoreActiveName();
  461. proTable.value?.getTableList();
  462. });
  463. // 监听路由变化,当从详情页或编辑页返回时恢复 activeName
  464. watch(
  465. () => route.path,
  466. (newPath, oldPath) => {
  467. // 如果当前路径是列表页,且之前路径是详情页或编辑页,则恢复 activeName
  468. if (
  469. newPath.includes("/ticketManagement") &&
  470. !newPath.includes("/detail") &&
  471. !newPath.includes("/newVoucher") &&
  472. !newPath.includes("/newCoupon") &&
  473. (oldPath?.includes("/ticketManagement/detail") ||
  474. oldPath?.includes("/ticketManagement/couponDetail") ||
  475. oldPath?.includes("/ticketManagement/newVoucher") ||
  476. oldPath?.includes("/ticketManagement/newCoupon"))
  477. ) {
  478. restoreActiveName();
  479. }
  480. },
  481. { immediate: false }
  482. );
  483. // dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
  484. // 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
  485. const dataCallback = (data: any) => {
  486. // 代金券从 couponList.records 取值,优惠券从 discountList.records 取值
  487. if (activeName.value === "1") {
  488. // 代金券
  489. return {
  490. list: data.couponList?.records || [],
  491. total: data.couponList?.total || 0
  492. };
  493. } else {
  494. // 优惠券
  495. return {
  496. list: data.discountList?.records || [],
  497. total: data.discountList?.total || 0
  498. };
  499. }
  500. };
  501. // 如果你想在请求之前对当前请求参数做一些操作,可以自定义如下函数:params 为当前所有的请求参数(包括分页),最后返回请求列表接口
  502. // 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
  503. const getTableList = (params: any) => {
  504. let newParams = JSON.parse(JSON.stringify(params));
  505. if (activeName.value === "1") {
  506. // 代金券
  507. if (newParams.status === "0") {
  508. newParams.dataType = 1; // 草稿
  509. } else if (newParams.status) {
  510. newParams.dataType = 0;
  511. } else {
  512. newParams.dataType = 2;
  513. }
  514. } else {
  515. // 优惠券
  516. delete newParams.dataType;
  517. newParams.couponsFromType = 1;
  518. }
  519. return getThaliList(newParams);
  520. };
  521. const newGroupBuying = () => {
  522. router.push(`/ticketManagement/newVoucher?type=add`);
  523. };
  524. const newCoupon = () => {
  525. router.push(`/ticketManagement/newCoupon?type=add`);
  526. };
  527. // 跳转详情页 - 根据类型跳转不同页面
  528. const toDetail = (row: any) => {
  529. // 保存当前 activeName 到 sessionStorage
  530. sessionStorage.setItem("ticketManagement_activeName", activeName.value);
  531. const path = isVoucher.value ? `/ticketManagement/detail?id=${row.id}` : `/ticketManagement/couponDetail?id=${row.id}`;
  532. router.push(path);
  533. };
  534. // 编辑行数据
  535. const editRow = (row: any) => {
  536. // 保存当前 activeName 到 sessionStorage
  537. sessionStorage.setItem("ticketManagement_activeName", activeName.value);
  538. const path = isVoucher.value
  539. ? `/ticketManagement/newVoucher?id=${row.id}&type=edit`
  540. : `/ticketManagement/newCoupon?id=${row.id}&type=edit`;
  541. router.push(path);
  542. };
  543. // 删除行数据
  544. const deleteRow = (row: any) => {
  545. ElMessageBox.confirm("确定要删除吗?", "提示", {
  546. confirmButtonText: "确定",
  547. cancelButtonText: "取消",
  548. type: "warning"
  549. })
  550. .then(() => {
  551. if (isVoucher.value) {
  552. // 代金券删除逻辑
  553. const params = {
  554. id: row.id,
  555. groupType: localGet("businessSection")
  556. };
  557. return delThaliById(params);
  558. } else {
  559. // 优惠券删除逻辑
  560. const params = {
  561. id: row.id,
  562. groupType: localGet("businessSection")
  563. };
  564. return delCouponById(params);
  565. }
  566. })
  567. .then(() => {
  568. ElMessage.success("删除成功");
  569. proTable.value?.getTableList();
  570. })
  571. .catch(() => {
  572. // 用户取消删除,不做任何操作
  573. });
  574. };
  575. // Tab切换处理
  576. const handleClick = () => {
  577. // initParam 中的 couponType 是响应式的,当 activeName 变化时会自动触发 ProTable 内部的 watch 监听,无需手动调用
  578. // proTable.value?.getTableList();
  579. };
  580. // 修改状态(上架/下架)
  581. const changeTypes = async (row: any, status: number) => {
  582. if (isVoucher.value) {
  583. // 代金券上架/下架逻辑
  584. const res = await updateStatus({ id: row.id, status: status, approvalComments: "" });
  585. if (res && res.code == 200) {
  586. ElMessage.success("操作成功");
  587. proTable.value?.getTableList();
  588. }
  589. } else {
  590. // 优惠券上架/下架逻辑
  591. const res = await updateCouponStatus({ counponId: row.id });
  592. if (res && res.code == 200) {
  593. ElMessage.success("操作成功");
  594. proTable.value?.getTableList();
  595. }
  596. }
  597. };
  598. // 修改库存
  599. const changeInventory = (row: any) => {
  600. formInventory.value = {
  601. id: row.id,
  602. name: row.name,
  603. singleQty: row.singleQty,
  604. newInventory: ""
  605. };
  606. dialogFormVisible.value = true;
  607. };
  608. // 提交修改库存
  609. const handleSubmit = async () => {
  610. if (!ruleFormRef.value) return;
  611. await ruleFormRef.value.validate(async valid => {
  612. if (valid) {
  613. const params = {
  614. id: formInventory.value.id,
  615. singleQty: formInventory.value.newInventory
  616. };
  617. if (isVoucher.value) {
  618. const res = await updateNum(params);
  619. if (res && res.code == 200) {
  620. ElMessage.success("修改成功");
  621. closeDialog();
  622. proTable.value?.getTableList();
  623. }
  624. } else {
  625. const res = await updateCouponSingleQty(params);
  626. if (res && res.code == 200) {
  627. ElMessage.success("修改成功");
  628. closeDialog();
  629. proTable.value?.getTableList();
  630. }
  631. }
  632. }
  633. });
  634. };
  635. // 关闭弹窗
  636. const closeDialog = () => {
  637. dialogFormVisible.value = false;
  638. formInventory.value = {
  639. id: "",
  640. name: "",
  641. singleQty: "",
  642. newInventory: ""
  643. };
  644. };
  645. // 查看拒绝原因
  646. const viewRejectReason = (row: any) => {
  647. rejectReasonData.value = {
  648. name: row.name || "--",
  649. approvalComments: row.approvalComments || "--"
  650. };
  651. rejectReasonDialogVisible.value = true;
  652. };
  653. // 关闭拒绝原因弹窗
  654. const closeRejectReasonDialog = () => {
  655. rejectReasonDialogVisible.value = false;
  656. rejectReasonData.value = {
  657. name: "",
  658. approvalComments: ""
  659. };
  660. };
  661. </script>
  662. <style lang="scss" scoped>
  663. // 在组件样式中添加
  664. .date-range {
  665. display: block; // 确保换行生效
  666. padding: 0 8px; // 可选:增加内边距
  667. word-wrap: break-word; // 长单词内换行
  668. white-space: normal; // 允许自然换行
  669. }
  670. .table-header-btn {
  671. .header-actions {
  672. display: flex;
  673. justify-content: space-between;
  674. width: 100%;
  675. .tabs {
  676. flex: 0 0 auto;
  677. :deep(.el-tabs__nav-wrap::after) {
  678. height: 0;
  679. }
  680. }
  681. }
  682. }
  683. .action-buttons {
  684. display: flex;
  685. flex: 0 0 auto;
  686. gap: 10px;
  687. margin-right: 20px;
  688. .button {
  689. margin-bottom: 0;
  690. }
  691. }
  692. .reject-reason-content {
  693. padding: 20px 0;
  694. .reject-reason-item {
  695. display: flex;
  696. margin-bottom: 20px;
  697. &:last-child {
  698. margin-bottom: 0;
  699. }
  700. .reject-reason-label {
  701. flex-shrink: 0;
  702. min-width: 100px;
  703. font-size: 14px;
  704. font-weight: 500;
  705. color: #606266;
  706. }
  707. .reject-reason-value {
  708. flex: 1;
  709. font-size: 14px;
  710. color: #303133;
  711. word-break: break-word;
  712. &.reject-reason-text {
  713. min-height: 80px;
  714. padding: 12px;
  715. line-height: 1.6;
  716. white-space: pre-wrap;
  717. background-color: #f5f7fa;
  718. border-radius: 4px;
  719. }
  720. }
  721. }
  722. }
  723. </style>