friendRelation.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <template>
  2. <div class="table-box button-table friend-relation-container">
  3. <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :init-param="initParam" :data-callback="dataCallback">
  4. <!-- 表格 header 按钮 -->
  5. <template #tableHeader="scope">
  6. <div class="header-button">
  7. <el-button type="primary" @click="openAddDialog"> 添加活动 </el-button>
  8. </div>
  9. </template>
  10. <!-- 状态列 -->
  11. <template #status="scope">
  12. <el-tag :type="getStatusType(scope.row.status)">
  13. {{ getStatusText(scope.row.status) }}
  14. </el-tag>
  15. </template>
  16. <!-- 表格操作 -->
  17. <template #operation="scope">
  18. <el-button link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
  19. <el-button link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
  20. <el-button v-if="scope.row.status === 0" link type="primary" @click="handleApprove(scope.row)"> 同意 </el-button>
  21. </template>
  22. </ProTable>
  23. <!-- 添加/编辑活动对话框 -->
  24. <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px" @close="closeDialog">
  25. <el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px">
  26. <el-form-item label="活动名称" prop="acName">
  27. <el-input v-model="formData.acName" placeholder="请输入" clearable />
  28. </el-form-item>
  29. <el-form-item label="消费门槛金额(¥)" required>
  30. <div style="display: flex; gap: 10px; align-items: center; width: 100%">
  31. <el-form-item prop="moneyLow" style=" flex: 1;margin-bottom: 0">
  32. <el-input v-model.number="formData.moneyLow" placeholder="0.00" clearable>
  33. <template #prefix> ¥ </template>
  34. </el-input>
  35. </el-form-item>
  36. <span>~</span>
  37. <el-form-item prop="moneyHigh" style=" flex: 1;margin-bottom: 0">
  38. <el-input v-model.number="formData.moneyHigh" placeholder="0.00" clearable>
  39. <template #prefix> ¥ </template>
  40. </el-input>
  41. </el-form-item>
  42. </div>
  43. </el-form-item>
  44. <el-form-item label="来源商家及优惠券">
  45. <div class="merchant-coupon-list">
  46. <div v-for="(item, index) in formData.details" :key="index" class="merchant-coupon-item">
  47. <el-select
  48. v-model="item.friendStoreUserId"
  49. placeholder="选择商家"
  50. style="width: 200px; margin-right: 10px"
  51. @change="handleMerchantChange(index)"
  52. >
  53. <el-option
  54. v-for="merchant in merchantList"
  55. :key="merchant.couponId"
  56. :label="merchant.storeName"
  57. :value="merchant.couponId"
  58. />
  59. </el-select>
  60. <el-select
  61. v-model="item.couponId"
  62. placeholder="选择优惠券"
  63. style="width: 200px; margin-right: 10px"
  64. @change="handleCouponChange(index)"
  65. >
  66. <el-option
  67. v-for="coupon in item.couponList"
  68. :key="coupon.couponId || coupon.id"
  69. :label="coupon.couponName"
  70. :value="coupon.couponId || coupon.id"
  71. />
  72. </el-select>
  73. <el-input v-model="item.remainingCount" placeholder="剩余张数1000张" style="width: 180px; margin-right: 10px" />
  74. <el-button type="danger" link @click="removeMerchantCoupon(index)" v-if="formData.details.length > 1">
  75. 删除
  76. </el-button>
  77. </div>
  78. <el-button type="primary" link @click="addMerchantCoupon" style="margin-top: 10px">
  79. <el-icon><Plus /></el-icon>
  80. 添加商家优惠券
  81. </el-button>
  82. </div>
  83. </el-form-item>
  84. </el-form>
  85. <template #footer>
  86. <div class="dialog-footer">
  87. <el-button @click="closeDialog"> 取消 </el-button>
  88. <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
  89. </div>
  90. </template>
  91. </el-dialog>
  92. </div>
  93. </template>
  94. <script setup lang="tsx" name="friendRelation">
  95. import { computed, onMounted, reactive, ref } from "vue";
  96. import type { FormInstance, FormRules } from "element-plus";
  97. import { ElMessage, ElMessageBox } from "element-plus";
  98. import { Plus } from "@element-plus/icons-vue";
  99. import ProTable from "@/components/ProTable/index.vue";
  100. import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
  101. import { localGet } from "@/utils";
  102. import {
  103. getRuleList,
  104. saveFriendCouponRule,
  105. getReceivedFriendCouponList,
  106. delFriendCouponRule,
  107. getRuleById
  108. } from "@/api/modules/newLoginApi";
  109. // ProTable 实例
  110. const proTable = ref<ProTableInstance>();
  111. // 对话框相关
  112. const dialogVisible = ref(false);
  113. const dialogTitle = computed(() => (isEdit.value ? "编辑活动" : "添加活动"));
  114. const isEdit = ref(false);
  115. const currentEditId = ref("");
  116. // 表单引用
  117. const formRef = ref<FormInstance>();
  118. // 商家列表
  119. const merchantList = ref<any[]>([]);
  120. // 表单数据
  121. const formData = reactive({
  122. acName: "",
  123. moneyLow: undefined as number | undefined,
  124. moneyHigh: undefined as number | undefined,
  125. details: [] as Array<{
  126. friendStoreUserId: string | number;
  127. couponId: string | number;
  128. couponList: any[]; // 当前商家的优惠券列表
  129. remainingCount: number; // 剩余张数
  130. }>
  131. });
  132. // 表单验证规则
  133. const formRules = reactive<FormRules>({
  134. acName: [{ required: true, message: "请输入活动名称", trigger: "blur" }],
  135. moneyLow: [{ required: true, message: "请输入最低消费金额", trigger: "blur" }],
  136. moneyHigh: [{ required: true, message: "请输入最高消费金额", trigger: "blur" }]
  137. });
  138. // 表格列配置
  139. const columns = reactive<ColumnProps<any>[]>([
  140. {
  141. prop: "acName",
  142. label: "活动名称"
  143. },
  144. {
  145. prop: "endDate",
  146. label: "有效期至"
  147. },
  148. {
  149. prop: "relationType",
  150. label: "消费门槛",
  151. render: (scope: any) => {
  152. return scope.row.moneyLow + "元" + "~" + scope.row.moneyHigh + "元";
  153. }
  154. },
  155. {
  156. prop: "status",
  157. label: "状态",
  158. enum: [
  159. { label: "启用中", value: "0" },
  160. { label: "已停用", value: "1" }
  161. ],
  162. fieldNames: { label: "label", value: "value" },
  163. render: (scope: any) => {
  164. return scope.row.status == "0" ? "启用中" : "已停用";
  165. }
  166. },
  167. { prop: "operation", label: "操作", fixed: "right", width: 250 }
  168. ]);
  169. // 初始化请求参数
  170. const initParam = reactive({
  171. storeId: localGet("createdId") || ""
  172. });
  173. // 数据回调处理
  174. const dataCallback = (data: any) => {
  175. return {
  176. list: data || [],
  177. total: data?.total || 0
  178. };
  179. };
  180. // 获取表格列表
  181. const getTableList = (params: any) => {
  182. return getRuleList(params);
  183. };
  184. // 获取状态文本
  185. const getStatusText = (status: number) => {
  186. const statusMap: Record<number, string> = {
  187. 0: "待同意",
  188. 1: "已同意",
  189. 2: "已拒绝"
  190. };
  191. return statusMap[status] || "--";
  192. };
  193. // 获取状态类型
  194. const getStatusType = (status: number): "success" | "warning" | "info" | "danger" => {
  195. const typeMap: Record<number, "success" | "warning" | "info" | "danger"> = {
  196. 0: "warning",
  197. 1: "success",
  198. 2: "info"
  199. };
  200. return typeMap[status] || "info";
  201. };
  202. // 获取商家列表(选择商家)
  203. const getMerchantList = async () => {
  204. try {
  205. const res: any = await getReceivedFriendCouponList({
  206. storeId: localGet("createdId") || ""
  207. });
  208. if (res.code == 200) {
  209. merchantList.value = res.data;
  210. console.log(merchantList.value);
  211. }
  212. } catch (error) {
  213. console.error("获取商家列表失败", error);
  214. }
  215. };
  216. // 打开添加对话框
  217. const openAddDialog = async () => {
  218. isEdit.value = false;
  219. await getMerchantList();
  220. // 初始化一个空的商家优惠券项
  221. formData.details = [
  222. {
  223. friendStoreUserId: "",
  224. couponId: "",
  225. couponList: [],
  226. remainingCount: 0
  227. }
  228. ];
  229. dialogVisible.value = true;
  230. };
  231. // 关闭对话框
  232. const closeDialog = () => {
  233. dialogVisible.value = false;
  234. formRef.value?.resetFields();
  235. Object.assign(formData, {
  236. acName: "",
  237. moneyLow: undefined,
  238. moneyHigh: undefined,
  239. details: []
  240. });
  241. currentEditId.value = "";
  242. };
  243. // 添加商家优惠券
  244. const addMerchantCoupon = () => {
  245. formData.details.push({
  246. friendStoreUserId: "",
  247. couponId: "",
  248. couponList: [],
  249. remainingCount: 0
  250. });
  251. };
  252. // 移除商家优惠券
  253. const removeMerchantCoupon = (index: number) => {
  254. formData.details.splice(index, 1);
  255. };
  256. // 商家选择改变时,获取该商家的优惠券列表(选择优惠券)
  257. const handleMerchantChange = async (index: number) => {
  258. const item = formData.details[index];
  259. item.couponId = ""; // 重置优惠券选择
  260. item.remainingCount = 0; // 重置剩余张数
  261. if (!item.friendStoreUserId) {
  262. item.couponList = [];
  263. return;
  264. }
  265. // item.friendStoreUserId 存储的是商家的 couponId,需要找到对应商家的 friendStoreUserId
  266. const merchant = merchantList.value.find((m: any) => m.couponId === item.friendStoreUserId);
  267. if (!merchant) {
  268. item.couponList = [];
  269. return;
  270. }
  271. try {
  272. const res: any = await getReceivedFriendCouponList({
  273. storeId: localGet("createdId") || "",
  274. friendStoreUserId: merchant.friendStoreUserId
  275. });
  276. if (res.code == 200) {
  277. item.couponList = res.data;
  278. } else {
  279. item.couponList = [];
  280. }
  281. } catch (error) {
  282. console.error("获取优惠券列表失败", error);
  283. item.couponList = [];
  284. }
  285. };
  286. // 优惠券选择改变时,更新剩余张数
  287. const handleCouponChange = (index: number) => {
  288. const item = formData.details[index];
  289. const coupon = item.couponList.find((c: any) => (c.couponId || c.id) === item.couponId);
  290. if (coupon) {
  291. item.remainingCount = coupon.couponNum || 0; // 使用 getReceivedFriendCouponList 返回的 couponNum
  292. }
  293. };
  294. // 编辑行数据
  295. const editRow = async (row: any) => {
  296. isEdit.value = true;
  297. currentEditId.value = row.id;
  298. try {
  299. // 1. 获取商家列表
  300. await getMerchantList();
  301. // 2. 调用 getRuleById 获取活动详情
  302. const res: any = await getRuleById({ id: row.id });
  303. if (res.code == 200 && res.data) {
  304. const detailData = res.data;
  305. console.log("getRuleById 返回的数据:", detailData);
  306. // 3. 处理 lifeDiscountCouponFriendRuleDetailVos 数据
  307. let details: Array<{
  308. friendStoreUserId: string | number;
  309. couponId: string | number;
  310. couponList: any[];
  311. remainingCount: number;
  312. }> = [];
  313. const detailVos = detailData.lifeDiscountCouponFriendRuleDetailVos || detailData.details || [];
  314. if (detailVos && Array.isArray(detailVos) && detailVos.length > 0) {
  315. details = await Promise.all(
  316. detailVos.map(async (item: any) => {
  317. console.log("detail item:", item);
  318. // 根据 couponId 在商家列表中找到对应的商家
  319. const matchedMerchant = merchantList.value.find((m: any) => m.couponId === item.couponId);
  320. console.log("匹配到的商家:", matchedMerchant);
  321. // 如果没有匹配到商家,返回默认值
  322. if (!matchedMerchant) {
  323. return {
  324. friendStoreUserId: item.couponId, // 商家选择框绑定的是 couponId
  325. couponId: "",
  326. couponList: [],
  327. remainingCount: 0
  328. };
  329. }
  330. // 获取该商家的优惠券列表
  331. let couponList: any[] = [];
  332. try {
  333. const couponRes: any = await getReceivedFriendCouponList({
  334. storeId: localGet("createdId") || "",
  335. friendStoreUserId: matchedMerchant.friendStoreUserId
  336. });
  337. if (couponRes.code == 200) {
  338. couponList = couponRes.data || [];
  339. }
  340. } catch (error) {
  341. console.error("获取优惠券列表失败", error);
  342. }
  343. // 在优惠券列表中找到匹配的优惠券
  344. let matchedCoupon = couponList.find((c: any) => c.couponId === item.couponId);
  345. console.log("匹配到的优惠券:", matchedCoupon);
  346. // 如果没有在列表中找到,使用 lifeDiscountCouponFriendRuleDetailVos 中的数据创建一个临时对象
  347. if (!matchedCoupon && item.couponId) {
  348. matchedCoupon = {
  349. couponId: item.couponId,
  350. couponName: item.couponName, // 使用 getRuleById 返回的 couponName
  351. couponNum: item.couponNum || 0 // 使用 getRuleById 返回的 couponNum
  352. };
  353. // 将这个临时对象添加到列表中,以便下拉框可以显示
  354. couponList = [matchedCoupon, ...couponList];
  355. }
  356. return {
  357. friendStoreUserId: item.couponId, // 商家选择框的值是商家的 couponId
  358. couponId: matchedCoupon?.couponId || item.couponId || "", // 优惠券选择框的值使用 couponId
  359. couponList: couponList,
  360. remainingCount: matchedCoupon?.couponNum || 0 // 使用 getReceivedFriendCouponList 返回的 couponNum
  361. };
  362. })
  363. );
  364. } else {
  365. details = [
  366. {
  367. friendStoreUserId: "",
  368. couponId: "",
  369. couponList: [],
  370. remainingCount: 0
  371. }
  372. ];
  373. }
  374. Object.assign(formData, {
  375. acName: detailData.acName,
  376. moneyLow: detailData.moneyLow,
  377. moneyHigh: detailData.moneyHigh,
  378. details: details
  379. });
  380. dialogVisible.value = true;
  381. } else {
  382. ElMessage.error(res.msg || "获取活动详情失败");
  383. }
  384. } catch (error: any) {
  385. ElMessage.error(error?.msg || "获取活动详情失败");
  386. }
  387. };
  388. // 删除行数据
  389. const deleteRow = (row: any) => {
  390. ElMessageBox.confirm("确定要删除这个活动吗?", "提示", {
  391. confirmButtonText: "确定",
  392. cancelButtonText: "取消",
  393. type: "warning"
  394. })
  395. .then(async () => {
  396. try {
  397. const res: any = await delFriendCouponRule({ id: row.id });
  398. if (res.code == 200) {
  399. ElMessage.success("删除成功");
  400. proTable.value?.getTableList();
  401. } else {
  402. ElMessage.error(res.msg || "删除失败");
  403. }
  404. } catch (error: any) {
  405. ElMessage.error(error?.msg || "删除失败");
  406. }
  407. })
  408. .catch(() => {
  409. // 用户取消删除
  410. });
  411. };
  412. // 同意好友申请(此页面可能不需要)
  413. const handleApprove = async (row: any) => {
  414. try {
  415. ElMessage.success("操作成功");
  416. proTable.value?.getTableList();
  417. } catch (error) {
  418. ElMessage.error("操作失败");
  419. }
  420. };
  421. // 提交表单
  422. const handleSubmit = async () => {
  423. if (!formRef.value) return;
  424. await formRef.value.validate(async (valid: boolean) => {
  425. if (valid) {
  426. // 验证是否至少添加了一个商家优惠券
  427. if (!formData.details.length) {
  428. ElMessage.warning("请至少添加一个商家优惠券");
  429. return;
  430. }
  431. // 验证所有商家优惠券项是否都已选择
  432. const hasEmpty = formData.details.some(item => !item.friendStoreUserId || !item.couponId);
  433. if (hasEmpty) {
  434. ElMessage.warning("请完善所有商家优惠券信息");
  435. return;
  436. }
  437. try {
  438. // 转换数据:item.friendStoreUserId 存储的是商家的 couponId,item.couponId 存储的是优惠券的 couponId
  439. const details = formData.details.map(item => {
  440. // 找到对应的商家
  441. const merchant = merchantList.value.find((m: any) => m.couponId === item.friendStoreUserId);
  442. // 找到对应的优惠券
  443. const coupon = item.couponList.find((c: any) => (c.couponId || c.id) === item.couponId);
  444. return {
  445. couponId: item.couponId, // 直接使用选中的 couponId
  446. friendStoreUserId: merchant?.friendStoreUserId || "" // 使用商家的真实 friendStoreUserId
  447. };
  448. });
  449. const params = {
  450. storeId: localGet("createdId") || "",
  451. acName: formData.acName,
  452. moneyHigh: formData.moneyHigh,
  453. moneyLow: formData.moneyLow,
  454. details: details
  455. };
  456. console.log("提交参数:", params);
  457. const res: any = await saveFriendCouponRule(params);
  458. if (res.code == 200) {
  459. ElMessage.success(isEdit.value ? "编辑成功" : "添加成功");
  460. closeDialog();
  461. proTable.value?.getTableList();
  462. } else {
  463. ElMessage.error(res.msg || "操作失败");
  464. }
  465. } catch (error: any) {
  466. ElMessage.error(error?.msg || (isEdit.value ? "编辑失败" : "添加失败"));
  467. }
  468. }
  469. });
  470. };
  471. // 页面加载时触发查询
  472. onMounted(() => {
  473. proTable.value?.getTableList();
  474. });
  475. </script>
  476. <style lang="scss" scoped>
  477. .friend-relation-container {
  478. .header-button {
  479. margin-bottom: 16px;
  480. }
  481. .merchant-coupon-list {
  482. width: 100%;
  483. .merchant-coupon-item {
  484. display: flex;
  485. align-items: center;
  486. margin-bottom: 10px;
  487. }
  488. }
  489. .dialog-footer {
  490. display: flex;
  491. gap: 10px;
  492. justify-content: flex-end;
  493. }
  494. }
  495. </style>