index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  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="type"> 新建优惠券 </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">
  31. 状态:{{ scope.row.dataType === 1 ? "草稿" : scope.row.statusName ? scope.row.statusName : "--" }}
  32. </p>
  33. <p style="margin: 0; line-height: 1.5" v-if="scope.row.dataType == 0">审核状态:{{ scope.row.reviewType || "--" }}</p>
  34. </template>
  35. <!-- 优惠券:只显示状态一行 -->
  36. <template v-else>
  37. <span>{{ getStatusLabel(scope.row.status) }}</span>
  38. </template>
  39. </template>
  40. <!-- 表格操作 -->
  41. <template #operation="scope">
  42. <!-- 上架按钮 -->
  43. <el-button
  44. v-if="canShowButton(scope.row.status, currentOperationPermissions.上架)"
  45. link
  46. type="primary"
  47. @click="changeTypes(scope.row, 5)"
  48. >
  49. 上架
  50. </el-button>
  51. <!-- 下架按钮 -->
  52. <el-button
  53. v-if="canShowButton(scope.row.status, currentOperationPermissions.下架)"
  54. link
  55. type="primary"
  56. @click="changeTypes(scope.row, 6)"
  57. >
  58. 下架
  59. </el-button>
  60. <!-- 修改库存按钮(仅代金券) -->
  61. <el-button
  62. v-if="isVoucher && canShowButton(scope.row.status, VOUCHER_OPERATION_PERMISSIONS.修改库存)"
  63. link
  64. type="primary"
  65. @click="changeInventory(scope.row)"
  66. >
  67. 修改库存
  68. </el-button>
  69. <!-- 查看详情按钮 -->
  70. <el-button
  71. v-if="
  72. isVoucher
  73. ? canShowButton(scope.row.status, currentOperationPermissions.查看详情) && scope.row.dataType == 0
  74. : canShowButton(scope.row.status, currentOperationPermissions.查看详情)
  75. "
  76. link
  77. type="primary"
  78. @click="toDetail(scope.row)"
  79. >
  80. 查看详情
  81. </el-button>
  82. <!-- 编辑按钮 -->
  83. <el-button
  84. v-if="
  85. isVoucher
  86. ? canShowButton(scope.row.status, currentOperationPermissions.编辑) || scope.row.dataType == 1
  87. : canShowButton(scope.row.status, currentOperationPermissions.编辑)
  88. "
  89. link
  90. type="primary"
  91. @click="editRow(scope.row)"
  92. >
  93. 编辑
  94. </el-button>
  95. <!-- 删除按钮 -->
  96. <el-button
  97. v-if="
  98. isVoucher
  99. ? canShowButton(scope.row.status, currentOperationPermissions.删除) || scope.row.dataType == 1
  100. : canShowButton(scope.row.status, currentOperationPermissions.删除)
  101. "
  102. link
  103. type="primary"
  104. @click="deleteRow(scope.row)"
  105. >
  106. 删除
  107. </el-button>
  108. <!-- 查看拒绝原因按钮(仅代金券) -->
  109. <el-button
  110. v-if="isVoucher && canShowButton(scope.row.status, VOUCHER_OPERATION_PERMISSIONS.查看拒绝原因)"
  111. link
  112. type="primary"
  113. @click="viewRejectReason(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待审核 2未开始 3审核拒绝 4已售罄 5进行中 6已下架 7已结束
  349. const VO_STATUS = {
  350. 待审核: 1,
  351. 未开始: 2,
  352. 审核拒绝: 3,
  353. 已售罄: 4,
  354. 进行中: 5,
  355. 已下架: 6,
  356. 已结束: 7
  357. } as const;
  358. // 状态枚举:0进行中 1已结束 2未开始 3已下架 4已售罄 5草稿
  359. const CO_STATUS = {
  360. 进行中: 0,
  361. 已结束: 1,
  362. 未开始: 2,
  363. 已下架: 3,
  364. 已售罄: 4,
  365. 草稿: 5
  366. } as const;
  367. // 代金券操作按钮权限配置:定义每个操作按钮在哪些状态下显示 草稿单独判断
  368. const VOUCHER_OPERATION_PERMISSIONS = {
  369. // 查看详情:待审核、未开始、审核拒绝、进行中、已售罄、已下架
  370. 查看详情: [VO_STATUS.待审核, VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.进行中, VO_STATUS.已售罄, VO_STATUS.已下架],
  371. // 上架:未开始、已下架
  372. 上架: [VO_STATUS.未开始, VO_STATUS.已下架],
  373. // 下架:进行中
  374. 下架: [VO_STATUS.进行中],
  375. // 修改库存:未开始、进行中、已售罄
  376. 修改库存: [VO_STATUS.未开始, VO_STATUS.进行中, VO_STATUS.已售罄],
  377. // 编辑:审核拒绝、已售罄、已下架、已结束
  378. 编辑: [VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已下架, VO_STATUS.已结束],
  379. // 删除:未开始、审核拒绝、已售罄、已结束
  380. 删除: [VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已结束],
  381. // 查看拒绝原因:审核拒绝
  382. 查看拒绝原因: [VO_STATUS.审核拒绝]
  383. } as const;
  384. // 优惠券操作按钮权限配置
  385. const COUPON_OPERATION_PERMISSIONS = {
  386. // 查看详情:草稿、未开始、进行中、已下架、已结束、已售罄
  387. 查看详情: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
  388. // 上架:未开始、已下架
  389. 上架: [CO_STATUS.未开始, CO_STATUS.已下架],
  390. // 下架:进行中
  391. 下架: [CO_STATUS.进行中],
  392. // 编辑:草稿、未开始、进行中、已下架、已结束、已售罄
  393. 编辑: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
  394. // 删除:草稿、已结束、已售罄
  395. 删除: [CO_STATUS.草稿, CO_STATUS.已结束, CO_STATUS.已售罄]
  396. } as const;
  397. // 判断按钮是否显示的工具函数
  398. const canShowButton = (status: number, allowedStatuses: readonly number[]) => {
  399. return allowedStatuses.includes(status);
  400. };
  401. // 根据当前tab获取操作权限配置
  402. const currentOperationPermissions = computed(() => {
  403. return activeName.value === "1" ? VOUCHER_OPERATION_PERMISSIONS : COUPON_OPERATION_PERMISSIONS;
  404. });
  405. // 判断是否为代金券
  406. const isVoucher = computed(() => activeName.value === "1");
  407. // 判断是否为优惠券
  408. const isCoupon = computed(() => activeName.value === "2");
  409. // 获取状态标签
  410. const getStatusLabel = (status: any) => {
  411. // 根据当前选中的tab选择对应的状态枚举
  412. const statusEnum = CO_STATUS;
  413. // 从状态枚举中查找对应的标签
  414. for (const [label, value] of Object.entries(statusEnum)) {
  415. if (value === status) {
  416. return label;
  417. }
  418. }
  419. return "--";
  420. };
  421. // 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
  422. const initParam = reactive({
  423. storeId: localGet("createdId") || "",
  424. groupType: localGet("businessSection") || "1",
  425. couponType: activeName
  426. });
  427. const type = ref(false);
  428. const typeCoupon = ref(false);
  429. // 恢复 activeName 的函数
  430. const restoreActiveName = () => {
  431. const savedActiveName = sessionStorage.getItem("ticketManagement_activeName");
  432. if (savedActiveName && (savedActiveName === "1" || savedActiveName === "2")) {
  433. activeName.value = savedActiveName;
  434. // 恢复后清除 sessionStorage,避免影响其他场景
  435. sessionStorage.removeItem("ticketManagement_activeName");
  436. }
  437. };
  438. // 页面加载时触发查询
  439. onMounted(async () => {
  440. type.value = await usePermission("新建券");
  441. // typeCoupon.value = await usePermission("新建优惠券");
  442. // 从 sessionStorage 恢复 activeName
  443. restoreActiveName();
  444. proTable.value?.getTableList();
  445. });
  446. // 从其他页面返回时触发查询
  447. onActivated(() => {
  448. // 从 sessionStorage 恢复 activeName
  449. restoreActiveName();
  450. proTable.value?.getTableList();
  451. });
  452. // 监听路由变化,当从详情页或编辑页返回时恢复 activeName
  453. watch(
  454. () => route.path,
  455. (newPath, oldPath) => {
  456. // 如果当前路径是列表页,且之前路径是详情页或编辑页,则恢复 activeName
  457. if (
  458. newPath.includes("/ticketManagement") &&
  459. !newPath.includes("/detail") &&
  460. !newPath.includes("/newVoucher") &&
  461. !newPath.includes("/newCoupon") &&
  462. (oldPath?.includes("/ticketManagement/detail") ||
  463. oldPath?.includes("/ticketManagement/couponDetail") ||
  464. oldPath?.includes("/ticketManagement/newVoucher") ||
  465. oldPath?.includes("/ticketManagement/newCoupon"))
  466. ) {
  467. restoreActiveName();
  468. }
  469. },
  470. { immediate: false }
  471. );
  472. // dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
  473. // 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
  474. const dataCallback = (data: any) => {
  475. // 代金券从 couponList.records 取值,优惠券从 discountList.records 取值
  476. if (activeName.value === "1") {
  477. // 代金券
  478. return {
  479. list: data.couponList?.records || [],
  480. total: data.couponList?.total || 0
  481. };
  482. } else {
  483. // 优惠券
  484. return {
  485. list: data.discountList?.records || [],
  486. total: data.discountList?.total || 0
  487. };
  488. }
  489. };
  490. // 如果你想在请求之前对当前请求参数做一些操作,可以自定义如下函数:params 为当前所有的请求参数(包括分页),最后返回请求列表接口
  491. // 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
  492. const getTableList = (params: any) => {
  493. let newParams = JSON.parse(JSON.stringify(params));
  494. if (activeName.value === "1") {
  495. // 代金券
  496. if (newParams.status === "0") {
  497. newParams.dataType = 1; // 草稿
  498. } else if (newParams.status) {
  499. newParams.dataType = 0;
  500. } else {
  501. newParams.dataType = 2;
  502. }
  503. } else {
  504. // 优惠券
  505. delete newParams.dataType;
  506. newParams.couponsFromType = 1;
  507. }
  508. return getThaliList(newParams);
  509. };
  510. const newGroupBuying = () => {
  511. router.push(`/ticketManagement/newVoucher?type=add`);
  512. };
  513. const newCoupon = () => {
  514. router.push(`/ticketManagement/newCoupon?type=add`);
  515. };
  516. // 跳转详情页 - 根据类型跳转不同页面
  517. const toDetail = (row: any) => {
  518. // 保存当前 activeName 到 sessionStorage
  519. sessionStorage.setItem("ticketManagement_activeName", activeName.value);
  520. const path = isVoucher.value ? `/ticketManagement/detail?id=${row.id}` : `/ticketManagement/couponDetail?id=${row.id}`;
  521. router.push(path);
  522. };
  523. // 编辑行数据
  524. const editRow = (row: any) => {
  525. // 保存当前 activeName 到 sessionStorage
  526. sessionStorage.setItem("ticketManagement_activeName", activeName.value);
  527. const path = isVoucher.value
  528. ? `/ticketManagement/newVoucher?id=${row.id}&type=edit`
  529. : `/ticketManagement/newCoupon?id=${row.id}&type=edit`;
  530. router.push(path);
  531. };
  532. // 删除行数据
  533. const deleteRow = (row: any) => {
  534. ElMessageBox.confirm("确定要删除吗?", "提示", {
  535. confirmButtonText: "确定",
  536. cancelButtonText: "取消",
  537. type: "warning"
  538. })
  539. .then(() => {
  540. if (isVoucher.value) {
  541. // 代金券删除逻辑
  542. const params = {
  543. id: row.id,
  544. groupType: localGet("businessSection")
  545. };
  546. return delThaliById(params);
  547. } else {
  548. // 优惠券删除逻辑
  549. const params = {
  550. id: row.id,
  551. groupType: localGet("businessSection")
  552. };
  553. return delCouponById(params);
  554. }
  555. })
  556. .then(() => {
  557. ElMessage.success("删除成功");
  558. proTable.value?.getTableList();
  559. })
  560. .catch(() => {
  561. // 用户取消删除,不做任何操作
  562. });
  563. };
  564. // Tab切换处理
  565. const handleClick = () => {
  566. // initParam 中的 couponType 是响应式的,当 activeName 变化时会自动触发 ProTable 内部的 watch 监听,无需手动调用
  567. // proTable.value?.getTableList();
  568. };
  569. // 修改状态(上架/下架)
  570. const changeTypes = async (row: any, status: number) => {
  571. if (isVoucher.value) {
  572. // 代金券上架/下架逻辑
  573. const res = await updateStatus({ id: row.id, status: status, approvalComments: "" });
  574. if (res && res.code == 200) {
  575. ElMessage.success("操作成功");
  576. proTable.value?.getTableList();
  577. }
  578. } else {
  579. // 优惠券上架/下架逻辑
  580. const res = await updateCouponStatus({ counponId: row.id });
  581. if (res && res.code == 200) {
  582. ElMessage.success("操作成功");
  583. proTable.value?.getTableList();
  584. }
  585. }
  586. };
  587. // 修改库存
  588. const changeInventory = (row: any) => {
  589. formInventory.value = {
  590. id: row.id,
  591. name: row.name,
  592. singleQty: row.singleQty,
  593. newInventory: ""
  594. };
  595. dialogFormVisible.value = true;
  596. };
  597. // 提交修改库存
  598. const handleSubmit = async () => {
  599. if (!ruleFormRef.value) return;
  600. await ruleFormRef.value.validate(async valid => {
  601. if (valid) {
  602. const params = {
  603. id: formInventory.value.id,
  604. singleQty: formInventory.value.newInventory
  605. };
  606. if (isVoucher.value) {
  607. const res = await updateNum(params);
  608. if (res && res.code == 200) {
  609. ElMessage.success("修改成功");
  610. closeDialog();
  611. proTable.value?.getTableList();
  612. }
  613. } else {
  614. const res = await updateCouponSingleQty(params);
  615. if (res && res.code == 200) {
  616. ElMessage.success("修改成功");
  617. closeDialog();
  618. proTable.value?.getTableList();
  619. }
  620. }
  621. }
  622. });
  623. };
  624. // 关闭弹窗
  625. const closeDialog = () => {
  626. dialogFormVisible.value = false;
  627. formInventory.value = {
  628. id: "",
  629. name: "",
  630. singleQty: "",
  631. newInventory: ""
  632. };
  633. };
  634. // 查看拒绝原因
  635. const viewRejectReason = (row: any) => {
  636. rejectReasonData.value = {
  637. name: row.name,
  638. approvalComments: row.approvalComments
  639. };
  640. rejectReasonDialogVisible.value = true;
  641. };
  642. // 关闭拒绝原因弹窗
  643. const closeRejectReasonDialog = () => {
  644. rejectReasonDialogVisible.value = false;
  645. };
  646. </script>
  647. <style lang="scss" scoped>
  648. // 在组件样式中添加
  649. .date-range {
  650. display: block; // 确保换行生效
  651. padding: 0 8px; // 可选:增加内边距
  652. word-wrap: break-word; // 长单词内换行
  653. white-space: normal; // 允许自然换行
  654. }
  655. .table-header-btn {
  656. .header-actions {
  657. display: flex;
  658. justify-content: space-between;
  659. width: 100%;
  660. .tabs {
  661. flex: 0 0 auto;
  662. :deep(.el-tabs__nav-wrap::after) {
  663. height: 0;
  664. }
  665. }
  666. }
  667. }
  668. .action-buttons {
  669. display: flex;
  670. flex: 0 0 auto;
  671. gap: 10px;
  672. margin-right: 20px;
  673. .button {
  674. margin-bottom: 0;
  675. }
  676. }
  677. .reject-reason-content {
  678. padding: 20px 0;
  679. .reject-reason-item {
  680. display: flex;
  681. margin-bottom: 20px;
  682. &:last-child {
  683. margin-bottom: 0;
  684. }
  685. .reject-reason-label {
  686. flex-shrink: 0;
  687. min-width: 100px;
  688. font-size: 14px;
  689. font-weight: 500;
  690. color: #606266;
  691. }
  692. .reject-reason-value {
  693. flex: 1;
  694. font-size: 14px;
  695. color: #303133;
  696. word-break: break-word;
  697. &.reject-reason-text {
  698. min-height: 80px;
  699. padding: 12px;
  700. line-height: 1.6;
  701. white-space: pre-wrap;
  702. background-color: #f5f7fa;
  703. border-radius: 4px;
  704. }
  705. }
  706. }
  707. }
  708. </style>