You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1094 lines
34 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
3 months ago
6 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
  1. <script setup>
  2. import { onMounted, reactive, ref, watch, computed } from "vue";
  3. import { ElIcon, ElMessage } from "element-plus";
  4. import moment from "moment";
  5. import request from "@/util/http.js"
  6. import Cookies from 'js-cookie';
  7. import { useAdminStore } from "@/store/index.js";
  8. import { storeToRefs } from "pinia";
  9. import { WarnTriangleFilled } from "@element-plus/icons-vue";
  10. import dayjs from "dayjs";
  11. // 国际化
  12. import { useI18n } from 'vue-i18n'
  13. const { t } = useI18n()
  14. const adminStore = useAdminStore();
  15. const { adminData, menuTree } = storeToRefs(adminStore);
  16. // 精网号去空格
  17. const trimJwCode = () => {
  18. if (addConsume.value.jwcode) {
  19. // 去除所有空格,并尝试转换为整数
  20. const trimmed = addConsume.value.jwcode.toString().replace(/\s/g, '')
  21. const numeric = Number(trimmed)
  22. // 如果转换为数字成功,保存为数字,否则提示错误
  23. if (!isNaN(numeric)) {
  24. addConsume.value.jwcode = numeric
  25. } else {
  26. ElMessage.error(t('elmessage.limitDigitJwcode'))
  27. }
  28. }
  29. }
  30. //提交禁止重复点击
  31. const addDisabled = ref(false)
  32. // 通过精网号查询用户(客户)信息 表单
  33. const user = ref({
  34. jwcode: null,
  35. name: "",
  36. market: "",
  37. red: null,
  38. historySumGold: null,
  39. historyPermanentGold: null,
  40. historyFreeGold: null,
  41. historyTaskGold: null,
  42. rechargeNum: null,
  43. consumeNum: null,
  44. firstRecharge: "",
  45. nowPermanentGold: null,
  46. nowFreeJune: null,
  47. nowTaskGold: null,
  48. nowFreeDecember: null,
  49. nowFreeGold: null,
  50. nowSumGold: null
  51. })
  52. // 这是添加消费信息的表单(金币)
  53. const addConsume = ref({
  54. // jwcode 是数字
  55. jwcode: null, //精网号
  56. goodsName: "",// 商品名称
  57. price: null, // 原价
  58. sumGold: null, // 消费金币总数
  59. freeGold: null, // 免费金币
  60. permanentGold: null, // 永久金币
  61. taskGold: null, // 任务金币
  62. remark: "",//备注
  63. adminId: null,// 当前管理员id
  64. adminName: adminData.value.adminName,
  65. redMoney: 1 // 是否使用红包:1-使用,0-不使用
  66. })
  67. const Ref = ref(null)
  68. const rules = reactive({
  69. jwcode: [
  70. { required: true, message: t('elmessage.checkJwcode'), trigger: "blur" },
  71. ],
  72. goodsName: [{ required: true, message: t('elmessage.checkGoodsName'), trigger: "change" }],
  73. sumGold: [
  74. { required: true, message: t('elmessage.noEmptySumGold'), trigger: "blur" },
  75. {
  76. validator: (rule, value, callback) => {
  77. // 如果使用了红包,且红包总额抵扣完了原价,允许为0
  78. if (addConsume.value.redMoney === 1) {
  79. const price = Number(addConsume.value.price || 0)
  80. const redAmount = totalRedAmount.value
  81. if (redAmount >= price) {
  82. // 如果红包抵扣完了,允许0
  83. if (parseFloat(value) === 0 || value === 0) {
  84. callback();
  85. return;
  86. }
  87. }
  88. }
  89. // 允许0开头的小数(如0.1)但不允许单独的0
  90. const isValid = /^(0\.\d{1,2})|([1-9]\d*(\.\d{1,2})?)$/.test(value);
  91. if (!isValid) {
  92. callback(new Error(t('elmessage.limitPositiveNumber')));
  93. } else {
  94. callback();
  95. }
  96. },
  97. trigger: "blur"
  98. }
  99. ]
  100. });
  101. // 查询商品的表单
  102. const goods = ref([])
  103. // 输入验证函数
  104. function validateInput() {
  105. const sumGold = parseFloat(addConsume.value.sumGold);
  106. trimJwCode();
  107. if (user.value.jwcode == null) {
  108. ElMessage.warning(t('elmessage.checkUserInfo'));
  109. addConsume.value.sumGold = null;
  110. user.value = {};
  111. return false;
  112. }
  113. /*
  114. // 验证金币数值
  115. if (user.value.jwcode && (isNaN(sumGold) || sumGold <= 0)) {
  116. ElMessage.warning("消费金币总数必须是大于0的数字");
  117. // 将sumGold设置为null
  118. addConsume.value.sumGold = null;
  119. return false;
  120. }
  121. */
  122. // sumGold 补充0(比如.1 为0.1)
  123. if (addConsume.value.sumGold && addConsume.value.sumGold.toString().startsWith('.')) {
  124. addConsume.value.sumGold = '0' + addConsume.value.sumGold;
  125. // ElMessage.info('已自动补充前导0');
  126. }
  127. // 验证金币不能为负数
  128. if (sumGold < 0) {
  129. ElMessage.warning(t('elmessage.limitNegativeNumber'));
  130. addConsume.value.sumGold = null;
  131. return false;
  132. }
  133. // 小数位数限制 2位,整数位数限制 6位
  134. if (addConsume.value.sumGold) {
  135. const sumGoldStr = addConsume.value.sumGold.toString();
  136. // 检查整数部分长度
  137. if (sumGoldStr.includes('.')) {
  138. const integerPart = sumGoldStr.split('.')[0];
  139. if (integerPart.length > 6) {
  140. // 截断整数部分到6位并提示
  141. const truncatedInteger = integerPart.slice(0, 6);
  142. addConsume.value.sumGold = parseFloat(truncatedInteger);
  143. ElMessage.info(t('elmessage.limitSix'));
  144. return; // 直接返回,不再处理小数部分
  145. }
  146. } else {
  147. // 纯整数情况
  148. if (sumGoldStr.length > 6) {
  149. addConsume.value.sumGold = parseFloat(sumGoldStr.slice(0, 6));
  150. ElMessage.info(t('elmessage.limitSix'));
  151. return;
  152. }
  153. }
  154. // 处理小数部分
  155. if (sumGoldStr.includes('.')) {
  156. const decimalPart = sumGoldStr.split('.')[1];
  157. if (decimalPart.length > 2) {
  158. // 截断到两位小数并提示
  159. const truncatedValue = parseFloat(sumGoldStr.slice(0, sumGoldStr.indexOf('.') + 3));
  160. addConsume.value.sumGold = truncatedValue;
  161. ElMessage.info(t('elmessage.limitTwoDecimal'));
  162. }
  163. }
  164. }
  165. // 验证金币总和
  166. const totalAvailableGold = (user.value.nowSumGold)
  167. // 校验消耗金币数是否超过可用金币数
  168. if (user.value.jwcode && sumGold > totalAvailableGold) {
  169. if (addConsume.value.redMoney === 1) {
  170. ElMessage.error('红包抵扣额不足');
  171. // 如果红包额不足,清空已选红包
  172. selectedReds.value = []
  173. } else {
  174. ElMessage.error(t('elmessage.limitExceeded'));
  175. }
  176. // 将sumGold设置为null
  177. addConsume.value.sumGold = null;
  178. return false;
  179. }
  180. return true;
  181. }
  182. // 仅在鼠标离开(blur)时校验:消耗金币数 + 红包抵扣金额 ≥ 原价
  183. function validateRedLimit() {
  184. const sumGold = parseFloat(addConsume.value.sumGold);
  185. const price = Number(addConsume.value.price || 0);
  186. // 优先使用选中的红包总额,如果没有选中(可能是旧逻辑),则使用user.red
  187. const redAmount = (addConsume.value.redMoney == 1 && selectedReds.value.length > 0)
  188. ? totalRedAmount.value
  189. : (addConsume.value.redMoney == 1 ? Number(user.value.red || 0) : 0);
  190. if (!isNaN(price) && price > 0) {
  191. // 如果红包足额,允许sumGold为0
  192. const isCovered = redAmount >= price;
  193. if (isNaN(sumGold) || (sumGold <= 0 && !isCovered)) {
  194. ElMessage.error(t('elmessage.noEmptySumGold'));
  195. return false;
  196. }
  197. // 如果勾选使用红包,才需要校验消耗金币数 + 红包抵扣金额 ≥ 原价
  198. if (addConsume.value.redMoney == 1) {
  199. if (sumGold + redAmount < price) {
  200. ElMessage.error(t('elmessage.limitRedAmount'));
  201. return false;
  202. }
  203. }
  204. }
  205. return true;
  206. }
  207. // 消耗金币计算函数
  208. function calculateCoins(sumGold) {
  209. console.log("消耗金币计算函数:计算金币", sumGold);
  210. const parsedSumGold = parseFloat(sumGold);
  211. if (isNaN(parsedSumGold) || parsedSumGold <= 0 || !user.value.jwcode) {
  212. return { free: 0, permanent: 0, task: 0 };
  213. }
  214. const { nowFreeGold, nowPermanentGold, nowTaskGold } = user.value;
  215. let remaining = parsedSumGold;
  216. let freeUsed = 0, permanentUsed = 0, taskUsed = 0;
  217. // 先消耗免费金币
  218. if (remaining > 0 && nowFreeGold > 0) {
  219. freeUsed = Math.min(parseFloat(nowFreeGold.toFixed(4)), remaining);
  220. remaining = parseFloat((remaining - freeUsed).toFixed(4));
  221. }
  222. // 再消耗永久金币
  223. if (remaining > 0 && nowPermanentGold > 0) {
  224. permanentUsed = Math.min(parseFloat(nowPermanentGold.toFixed(4)), remaining);
  225. remaining = parseFloat((remaining - permanentUsed).toFixed(4));
  226. }
  227. // 最后消耗任务金币
  228. if (remaining > 0 && nowTaskGold > 0) {
  229. const availableTaskGold = parseFloat(nowTaskGold.toFixed(4));
  230. taskUsed = Math.min(availableTaskGold, remaining);
  231. remaining = parseFloat((remaining - taskUsed).toFixed(4));
  232. }
  233. // 更新金币值
  234. addConsume.value.freeGold = freeUsed;
  235. addConsume.value.permanentGold = permanentUsed;
  236. addConsume.value.taskGold = taskUsed;
  237. return { free: freeUsed, permanent: permanentUsed, task: taskUsed };
  238. }
  239. // 用来写的 cookie 的 key
  240. const WriteCookies = ref(null)
  241. // 用来写的 cookie 的 value
  242. const WriteCookiesTime = ref(null)
  243. // 用来读的 cookie 的 key
  244. const ReadCookies = ref(null)
  245. // 用来读的 cookie 的 value
  246. const ReadCookiesTime = ref(null)
  247. // 这是添加消费信息的接口
  248. const add = async function () {
  249. try {
  250. // 验证输入数据 再验证一次
  251. if (!validateInput()) {
  252. return;
  253. }
  254. // 计算金币使用情况
  255. calculateCoins(addConsume.value.sumGold);
  256. console.log("addConsume.value", addConsume.value)
  257. addDisabled.value = true
  258. // 发送POST请求
  259. const result = await request({
  260. url: "/consume/add",
  261. data: {
  262. jwcode: addConsume.value.jwcode,
  263. adminId: adminData.value.id,
  264. price: Number(addConsume.value.price || 0) * 100,
  265. sumGold: addConsume.value.sumGold * 100,
  266. freeGold: addConsume.value.freeGold * 100,
  267. taskGold: addConsume.value.taskGold * 100,
  268. permanentGold: addConsume.value.permanentGold * 100,
  269. goodsName: addConsume.value.goodsName.value,
  270. remark: addConsume.value.remark,
  271. adminName: adminData.value.adminName,
  272. redMoney: Number(addConsume.value.redMoney), // 1-使用红包,0-不使用红包
  273. redIds: addConsume.value.redMoney === 1 ? selectedReds.value.map(item => item.id) : [] // 选中的红包ID数组
  274. }
  275. })
  276. addDisabled.value = false
  277. console.log("add请求", result);
  278. // 处理响应
  279. handleResponse(result);
  280. // 重置表单
  281. resetForm();
  282. } catch (error) {
  283. console.error("请求失败", error);
  284. ElMessage.error(t('elmessage.addFailed'));
  285. }
  286. };
  287. // 响应处理函数
  288. function handleResponse(result) {
  289. console.log("响应结果", result)
  290. if (result.code === 200) {
  291. //存一下 用户的jwcode
  292. // 拼接 jwcode:permanentGold:freeGold
  293. WriteCookies.value = `coinConsume:${addConsume.value.jwcode}:${addConsume.value.goodsName.value}`
  294. //value 为当前消耗时间
  295. WriteCookiesTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss");
  296. // 设置cookies,用户jwcode为key,value也是jwcode,过期时间为1天
  297. Cookies.set(WriteCookies.value, WriteCookiesTime.value, {
  298. expires:
  299. 1, path: '/'
  300. });
  301. ElMessage.success(t('elmessage.addSuccess'));
  302. console.log("请求成功", result);
  303. } else {
  304. ElMessage.error(result.msg || t('elmessage.addFailedUnknown'));
  305. }
  306. }
  307. // 重置表单函数
  308. function resetForm() {
  309. // 清空表单数据
  310. Ref.value.resetFields();
  311. addConsume.value = {
  312. jwcode: null,
  313. goodsName: "",
  314. sumGold: null,
  315. freeGold: null,
  316. permanentGold: null,
  317. taskGold: null,
  318. remark: "",
  319. adminId: adminData.value.id,
  320. adminName: adminData.value.adminName,
  321. redMoney: 1 // 默认使用红包
  322. }
  323. // 清空红包相关数据
  324. selectedReds.value = []
  325. redList.value = []
  326. console.log("重置表单")
  327. user.value = {
  328. jwcode: null,
  329. name: "",
  330. market: "",
  331. historySumGold: null,
  332. historyPermanentGold: null,
  333. historyFreeGold: null,
  334. historyTaskGold: null,
  335. rechargeNum: null,
  336. consumeNum: null,
  337. firstRecharge: "",
  338. nowPermanentGold: null,
  339. nowFreeJune: null,
  340. nowTaskGold: null,
  341. nowFreeDecember: null,
  342. nowFreeGold: null,
  343. nowSumGold: null
  344. }
  345. if (Ref.value) {
  346. Ref.value.clearValidate()
  347. }
  348. }
  349. // 充值对话框显示状态
  350. const ConsumeDialogVisible = ref(false);
  351. // 关闭对话框
  352. const ConsumeDialogVisiblehandleClose = () => {
  353. ConsumeDialogVisible.value = false;
  354. // 重置表单数据
  355. resetForm()
  356. user.value = {}
  357. };
  358. ``
  359. // 确认使用cookie继续充值
  360. const ConsumeDialogVisibleContinue = () => {
  361. ConsumeDialogVisible.value = false;
  362. add();
  363. };
  364. const ConsumeDialogVisibleCancel = () => {
  365. ConsumeDialogVisible.value = false
  366. resetForm()
  367. user.value = {}
  368. };
  369. // 第一次弹窗
  370. // 充值对话框显示状态
  371. const FirstConsumeDialogVisible = ref(false);
  372. // 关闭对话框
  373. const FirstConsumeDialogVisiblehandleClose = () => {
  374. FirstConsumeDialogVisible.value = false;
  375. // 重置表单数据
  376. resetForm()
  377. user.value = {}
  378. };
  379. // 第一次消耗
  380. const FirstConsumeDialogVisibleContinue = () => {
  381. FirstConsumeDialogVisible.value = false;
  382. add();
  383. };
  384. const FirstConsumeDialogVisibleCancel = () => {
  385. FirstConsumeDialogVisible.value = false
  386. resetForm()
  387. user.value = {}
  388. };
  389. // 实际执行充值操作
  390. // const proceedWithConsume = () => {
  391. // ElMessageBox.confirm('确认购买?')
  392. // .then(() => {
  393. // add();
  394. // console.log('添加成功');
  395. // })
  396. // .catch(() => {
  397. // console.log('取消添加');
  398. // });
  399. // };
  400. // 添加前验证
  401. const addBefore = () => {
  402. Ref.value.validate(async (valid) => {
  403. // 验证cookie
  404. if (!valid) {
  405. ElMessage({
  406. type: 'error',
  407. message: t('elmessage.checkInputContent')
  408. });
  409. return;
  410. }
  411. if (!validateInput() || !validateRedLimit()) {
  412. return;
  413. }
  414. ReadCookies.value = `coinConsume:${addConsume.value.jwcode}:${addConsume.value.goodsName.value}`
  415. // 获取cookie
  416. const cookie = Cookies.get(ReadCookies.value)
  417. console.log("time", WriteCookiesTime.value)
  418. // 格式化时间
  419. ReadCookiesTime.value = moment(cookie).format('YYYY-MM-DD HH:mm:ss')
  420. console.log("cookie========", cookie)
  421. if (cookie) {
  422. ConsumeDialogVisible.value = true;
  423. } else {
  424. FirstConsumeDialogVisible.value = true;
  425. }
  426. });
  427. };
  428. // 查询客户信息(通过精网号)
  429. const getUser = async function (jwcode) {
  430. try {
  431. // 验证精网号
  432. if (!jwcode) {
  433. ElMessage.warning(t('elmessage.noEmptyJwcode'));
  434. return;
  435. }
  436. // 验证精网号是否为数字
  437. if (!/^\d{1,9}$/.test(jwcode)) {
  438. ElMessage.warning(t('elmessage.limitJwcodeNine'));
  439. resetForm()
  440. return;
  441. }
  442. // 发送POST请求
  443. const result = await request({
  444. url: "/user/selectUser",
  445. data: { jwcode }
  446. });
  447. console.log("请求成功", result);
  448. if (result.code === 200 && result.data) {
  449. // 查询成功后,重置表单(保留精网号),防止上一个用户的数据干扰
  450. const currentJwcode = addConsume.value.jwcode;
  451. resetForm();
  452. addConsume.value.jwcode = currentJwcode;
  453. // 处理用户数据
  454. user.value = {
  455. ...result.data,
  456. // 统一处理所有黄金数值,除以100
  457. nowPermanentGold: result.data.nowPermanentGold,
  458. nowFreeGold: result.data.nowFreeGold,
  459. nowSumGold: result.data.nowSumGold,
  460. nowTaskGold: result.data.nowTaskGold,
  461. nowFreeJune: result.data.nowFreeJune,
  462. nowFreeDecember: result.data.nowFreeDecember,
  463. historySumGold: result.data.historySumGold,
  464. historyPermanentGold: result.data.historyPermanentGold,
  465. historyFreeGold: result.data.historyFreeGold,
  466. historyTaskGold: result.data.historyTaskGold
  467. };
  468. ElMessage.success(t('elmessage.searchSuccess'));
  469. // 如果当前开启了红包模式,获取红包列表
  470. if (addConsume.value.redMoney === 1) {
  471. getRedList()
  472. }
  473. // 验证输入
  474. validateInput()
  475. } else if (!result.data) {
  476. ElMessage.warning(t('elmessage.noUser'));
  477. user.value.jwcode = null
  478. addConsume.value.jwcode = null
  479. // resetForm(); // 重置表单
  480. } else {
  481. ElMessage.warning(result.msg || t('elmessage.checkQueryParams'));
  482. }
  483. } catch (error) {
  484. console.error("请求失败", error);
  485. ElMessage.error(t('elmessage.queryFailed'));
  486. resetForm(); // 重置表单
  487. }
  488. };
  489. // 获取商品信息(三楼接口)
  490. const getGoods = async function () {
  491. try {
  492. // 发送POST请求
  493. const result = await request({
  494. // url: "/product", //
  495. // url: "http://39.101.133.168:8828/live_mall/api/product/all",
  496. url: "https://api.homilychart.com/live_mall/api/product/all",
  497. });
  498. // 将响应结果存储到响应式数据中
  499. console.log("请求成功", result);
  500. goods.value = result.data.map(item => ({
  501. id: item.id,
  502. label: item.name,
  503. value: item.name,
  504. price: item.price
  505. }));
  506. } catch (error) {
  507. console.log("请求失败", error);
  508. // 在这里可以处理错误逻辑,比如显示错误提示等
  509. }
  510. };
  511. /*
  512. ====================监听=================================
  513. */
  514. // 监听消费总金额变化,自动计算三类金币
  515. watch(
  516. () => addConsume.value.sumGold,
  517. (newValue) => {
  518. const parsedNewValue = parseFloat(newValue);
  519. if (!isNaN(parsedNewValue) && parsedNewValue >= 0) {
  520. const { free, permanent, task } = calculateCoins(parsedNewValue);
  521. addConsume.value.freeGold = free;
  522. addConsume.value.permanentGold = permanent;
  523. addConsume.value.taskGold = task;
  524. } else {
  525. addConsume.value.freeGold = null;
  526. addConsume.value.permanentGold = null;
  527. addConsume.value.taskGold = null;
  528. }
  529. }
  530. );
  531. // 监听商品选择,自动展示原价
  532. watch(
  533. () => addConsume.value.goodsName,
  534. (newGoods) => {
  535. if (newGoods && typeof newGoods === 'object') {
  536. addConsume.value.price = Number(newGoods.price || 0) || null;
  537. } else {
  538. addConsume.value.price = null;
  539. }
  540. }
  541. );
  542. /*
  543. ====================红包逻辑=================================
  544. */
  545. // 红包列表
  546. const redList = ref([])
  547. // 选中的红包
  548. const selectedReds = ref([])
  549. // 获取红包列表
  550. const getRedList = async () => {
  551. // 必须先有查询到的用户信息
  552. if (!user.value.jwcode) return
  553. try {
  554. const result = await request({
  555. url: "/Temporary/RedList",
  556. data: { jwcode: user.value.jwcode }
  557. })
  558. console.log("红包列表", result)
  559. if (result.code === 200) {
  560. redList.value = result.data || []
  561. }
  562. } catch (error) {
  563. console.error("获取红包列表失败", error)
  564. }
  565. }
  566. // 监听是否使用红包
  567. watch(() => addConsume.value.redMoney, (val) => {
  568. // 切换模式时,清除sumGold的验证状态,防止之前的错误提示残留
  569. if (Ref.value) {
  570. Ref.value.clearValidate('sumGold')
  571. }
  572. if (val === 1) {
  573. if (!user.value.jwcode) {
  574. if (addConsume.value.jwcode) {
  575. ElMessage.warning(t('elmessage.checkUserInfo'))
  576. }
  577. redList.value = []
  578. selectedReds.value = []
  579. addConsume.value.sumGold = null
  580. return
  581. }
  582. selectedReds.value = []
  583. getRedList()
  584. // 初始消耗金币等于原价(未选红包时)
  585. if (addConsume.value.price) {
  586. addConsume.value.sumGold = addConsume.value.price
  587. } else {
  588. addConsume.value.sumGold = null
  589. }
  590. } else {
  591. selectedReds.value = []
  592. addConsume.value.sumGold = null
  593. }
  594. })
  595. // 计算已选红包总额
  596. const totalRedAmount = computed(() => {
  597. return selectedReds.value.reduce((sum, item) => {
  598. // 根据实际接口返回,使用 discount 字段
  599. const amount = Number(item.discount || 0)
  600. return sum + amount
  601. }, 0)
  602. })
  603. // 监听选中红包变化,计算剩余金币
  604. watch(selectedReds, () => {
  605. if (addConsume.value.redMoney === 1) {
  606. const price = Number(addConsume.value.price || 0)
  607. const redTotal = totalRedAmount.value
  608. // 剩余需要消耗的金币
  609. let remaining = price - redTotal
  610. if (remaining < 0) remaining = 0
  611. addConsume.value.sumGold = remaining
  612. }
  613. }, { deep: true })
  614. // 判断红包选项是否禁用
  615. const isRedOptionDisabled = (item) => {
  616. // 如果已经选中,不禁用(允许取消)
  617. if (selectedReds.value.some(r => r.id === item.id)) return false
  618. const price = Number(addConsume.value.price || 0)
  619. // 如果当前总额已经大于等于原价,则不能再选新的
  620. if (totalRedAmount.value >= price) return true
  621. return false
  622. }
  623. // 监听原价变化(比如换了商品),如果在红包模式下,需要重新计算
  624. watch(() => addConsume.value.price, (newPrice) => {
  625. if (addConsume.value.redMoney === 1) {
  626. const redTotal = totalRedAmount.value
  627. let remaining = (newPrice || 0) - redTotal
  628. if (remaining < 0) remaining = 0
  629. addConsume.value.sumGold = remaining
  630. }
  631. })
  632. /*
  633. ====================挂载=================================
  634. */
  635. // 挂载
  636. onMounted(async function () {
  637. await getGoods()
  638. console.log('adminData', adminData.value)
  639. })
  640. </script>
  641. <template>
  642. <div class="father1">
  643. <div class="left">
  644. <el-form :model="addConsume" ref="Ref" :rules="rules" style="min-width: 600px;" class="add-form"
  645. label-width="auto" label-position="right">
  646. <el-form-item prop="jwcode" :label="t('common_add.jwcode')" style="margin-top: 50px">
  647. <el-input v-model="addConsume.jwcode" style="width: 200px;" @keyup.enter="getUser(addConsume.jwcode)" />
  648. <el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 20px">
  649. {{ t('common.search') }}
  650. </el-button>
  651. </el-form-item>
  652. <el-form-item prop="goodsName" :label="t('common_add.goodsName')">
  653. <el-select v-model="addConsume.goodsName" :placeholder="t('common_add.goodsNamePlaceholder')"
  654. style="width: 200px" clearable filterable>
  655. <el-option v-for="(item, index) in goods" :key="index" :label="item.label" :value="item" />
  656. </el-select>
  657. </el-form-item>
  658. <el-form-item prop="price" :label="t('common_add.price')">
  659. <el-input v-model="addConsume.price" style="width: 120px" disabled />
  660. </el-form-item>
  661. <!-- <el-form-item prop="sumGold" :label="t('common_add.consumeTotalGold')">
  662. <el-input v-model="addConsume.sumGold" style="width: 120px"
  663. @blur="validateRedLimit()" />
  664. </el-form-item> -->
  665. <el-form-item prop="sumGold" :label="t('common_add.consumeTotalGold')">
  666. <el-input v-model="addConsume.sumGold" style="width: 120px" @blur="validateRedLimit()" :disabled="addConsume.redMoney === 1" />
  667. <el-radio v-model="addConsume.redMoney" :label="1" style="margin-left: 10px;">使用红包</el-radio>
  668. <el-radio v-model="addConsume.redMoney" :label="0">不使用红包</el-radio>
  669. </el-form-item>
  670. <el-form-item v-if="addConsume.redMoney === 1" label="选择红包">
  671. <el-select
  672. v-model="selectedReds"
  673. multiple
  674. placeholder="请选择红包"
  675. style="width: 200px"
  676. value-key="id"
  677. collapse-tags
  678. >
  679. <el-option
  680. v-for="item in redList"
  681. :key="item.id"
  682. :label="`${item.title} (${item.discount})`"
  683. :value="item"
  684. :disabled="isRedOptionDisabled(item)"
  685. />
  686. </el-select>
  687. <span style="margin-left: 10px; color: #666;">
  688. 已抵扣: {{ totalRedAmount }}
  689. </span>
  690. </el-form-item>
  691. <!-- 三类金币自动计算禁用状态不可编辑 -->
  692. <el-form-item prop="permanentGold" :label="t('common_add.permanentGold')">
  693. <el-input v-model="addConsume.permanentGold" disabled style="width: 120px">
  694. <template #default="scope">{{ scope.row.permanentGold }}</template>
  695. </el-input>
  696. <p style="margin-right: 0px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  697. </el-form-item>
  698. <el-form-item prop="freeCoin" :label="t('common_add.freeGold')">
  699. <el-input disabled v-model="addConsume.freeGold" style="width: 120px" />
  700. <p style="margin-right: 0px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  701. </el-form-item>
  702. <el-form-item prop="taskGold" :label="t('common_add.taskGold')">
  703. <el-input disabled v-model="addConsume.taskGold" style="width: 120px" />
  704. <p style="margin-right: 20px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  705. </el-form-item>
  706. <el-form-item prop="remark" :label="t('common_add.remark')">
  707. <el-input v-model="addConsume.remark" style="width: 250px" :rows="4" maxlength="100" show-word-limit
  708. type="textarea" />
  709. </el-form-item>
  710. <el-button type="success" @click="resetForm()" style="margin-left: 200px;margin-top:10px">{{ t('common.reset')
  711. }}</el-button>
  712. <el-button type="primary" :disabled="addDisabled" @click="addBefore" style="margin-top:10px">{{
  713. t('common.submit')
  714. }}</el-button>
  715. </el-form>
  716. </div>
  717. <div class="right">
  718. <!-- 客户信息栏 -->
  719. <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
  720. <el-form :model="user" label-width="auto" style="max-width: 1200px" label-position="left">
  721. <el-text size="large" style="margin-left: 20px">{{ $t('common_add_user.customerInfo') }}</el-text>
  722. <!-- 第一行姓名 + 历史金币 -->
  723. <el-row style="margin-top: 20px">
  724. <el-col :span="9">
  725. <el-form-item label-width="120px" :label="$t('common_add_user.name')">
  726. <p>{{ user.name }}</p>
  727. </el-form-item>
  728. </el-col>
  729. <el-col :span="14">
  730. <el-form-item :label="$t('common_add_user.currentGoldCoinTotal')" style="width: 500px">
  731. <span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{
  732. user.nowSumGold
  733. }}</span>
  734. </el-form-item>
  735. <!-- 金币详情独立显示 -->
  736. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  737. <span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">({{
  738. $t('common_add_user.permanentGold') }}:{{
  739. user.nowPermanentGold
  740. }};
  741. {{ $t('common_add_user.freeGold') }}:{{ user.nowFreeGold }};
  742. {{ $t('common_add_user.taskGold') }}:{{ user.nowTaskGold }})</span>
  743. </el-form-item>
  744. </el-col>
  745. </el-row>
  746. <!-- 第二行精网号 + 当前金币独立行 -->
  747. <el-row>
  748. <el-col :span="9">
  749. <el-form-item label-width="120px" :label="$t('common_add_user.jwcode')">
  750. <p>{{ user.jwcode }}</p>
  751. </el-form-item>
  752. </el-col>
  753. <el-col :span="14">
  754. <el-form-item :label="$t('common_add_user.consumptionTimes')">
  755. <p style="color: #2fa1ff">{{ user.consumeNum }} </p>
  756. </el-form-item>
  757. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  758. <p style="font-size: small; color: #b1b1b1">({{ $t('common_add_user.onlyStatisticsDataAfter20250101')
  759. }})
  760. </p>
  761. </el-form-item>
  762. </el-col>
  763. </el-row>
  764. <!-- 第三行首次充值日期 + 充值次数 -->
  765. <!-- <el-row >
  766. <el-col :span="9">
  767. <el-form-item label="首次充值日期">
  768. <p v-if="user.firstRecharge">
  769. {{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
  770. </p>
  771. </el-form-item>
  772. </el-col>
  773. </el-row> -->
  774. <!-- 第四行消费次数 + 所属门店 -->
  775. <el-row>
  776. <el-col :span="9">
  777. <el-form-item :label="$t('common_add_user.store')">
  778. <p>{{ user.market }}</p>
  779. </el-form-item>
  780. </el-col>
  781. <el-col :span="14">
  782. <el-form-item :label="$t('common_add_user.maxReductionAmount')">
  783. <p style="color: #2fa1ff">{{ user.red }} </p>
  784. </el-form-item>
  785. </el-col>
  786. </el-row>
  787. </el-form>
  788. </el-card>
  789. <el-dialog v-model="FirstConsumeDialogVisible" :title="$t('common_add.operationConfirm')"
  790. :before-close="FirstConsumeDialogVisiblehandleClose" :close-on-click-modal="false" width="480px">
  791. <!-- 内容整体居中且收窄 -->
  792. <div class="confirm-body">
  793. <!-- 用户信息 -->
  794. <div>
  795. <div class="field-label">{{ $t('common_add.userInfo') }}</div>
  796. <el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
  797. </div>
  798. <!-- 商品名称 -->
  799. <div class="field">
  800. <div class="field-label">{{ $t('common_add.goodsName') }}</div>
  801. <el-input v-model="addConsume.goodsName.value" disabled />
  802. </div>
  803. <!--金币总数 -->
  804. <div class="field">
  805. <div class="field-label">{{ $t('common_add.totalGold') }}</div>
  806. <el-input v-model="addConsume.sumGold" disabled />
  807. </div>
  808. <!-- 金币详细信息同一行左右排列 -->
  809. <el-row :gutter="20" class="coins-row">
  810. <el-col :span="8">
  811. <div class="field">
  812. <div class="field-label">{{ $t('common_add.permanentGold') }}</div>
  813. <el-input v-model="addConsume.permanentGold" disabled />
  814. </div>
  815. </el-col>
  816. <el-col :span="8">
  817. <div class="field">
  818. <div class="field-label">{{ $t('common_add.freeGold') }}</div>
  819. <el-input v-model="addConsume.freeGold" disabled />
  820. </div>
  821. </el-col>
  822. <el-col :span="8">
  823. <div class="field">
  824. <div class="field-label">{{ $t('common_add.taskGold') }}</div>
  825. <el-input v-model="addConsume.taskGold" disabled />
  826. </div>
  827. </el-col>
  828. </el-row>
  829. <div class="field">
  830. <div class="field-label">{{ $t('common_add.remark') }}</div>
  831. <el-input v-model="addConsume.remark" disabled />
  832. </div>
  833. </div>
  834. <!-- 底部按钮居中 -->
  835. <template #footer>
  836. <div class="dialog-footer-center">
  837. <el-button @click="FirstConsumeDialogVisibleCancel">{{ $t('common.cancel') }}</el-button>
  838. <el-button type="primary" @click="FirstConsumeDialogVisibleContinue">{{ $t('common.confirm') }}</el-button>
  839. </div>
  840. </template>
  841. </el-dialog>
  842. <el-dialog v-model="ConsumeDialogVisible" :title="$t('common_add.operationConfirm')"
  843. :before-close="ConsumeDialogVisiblehandleClose" :close-on-click-modal="false" width="480px">
  844. <!-- 内容整体居中且收窄 -->
  845. <div class="confirm-body">
  846. <!-- 用户信息 -->
  847. <div>
  848. <div class="field-label">{{ $t('common_add.userInfo') }}</div>
  849. <el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
  850. </div>
  851. <!-- 商品名称 -->
  852. <div class="field">
  853. <div class="field-label">{{ $t('common_add.goodsName') }}</div>
  854. <el-input v-model="addConsume.goodsName.value" disabled />
  855. </div>
  856. <!--金币总数 -->
  857. <div class="field">
  858. <div class="field-label">{{ $t('common_add.totalGold') }}</div>
  859. <el-input v-model="addConsume.sumGold" disabled />
  860. </div>
  861. <!-- 金币详细信息同一行左右排列 -->
  862. <el-row :gutter="20" class="coins-row">
  863. <el-col :span="8">
  864. <div class="field">
  865. <div class="field-label">{{ $t('common_add.permanentGold') }}</div>
  866. <el-input v-model="addConsume.permanentGold" disabled />
  867. </div>
  868. </el-col>
  869. <el-col :span="8">
  870. <div class="field">
  871. <div class="field-label">{{ $t('common_add.freeGold') }}</div>
  872. <el-input v-model="addConsume.freeGold" disabled />
  873. </div>
  874. </el-col>
  875. <el-col :span="8">
  876. <div class="field">
  877. <div class="field-label">{{ $t('common_add.taskGold') }}</div>
  878. <el-input v-model="addConsume.taskGold" disabled />
  879. </div>
  880. </el-col>
  881. </el-row>
  882. <!-- 风险提示 -->
  883. <div style="display: flex; align-items: center; margin-top: 20px;">
  884. <el-icon :size="24" color="#FFD700">
  885. <WarnTriangleFilled />
  886. </el-icon>
  887. <p>{{ $t('common_add.prompt') }}</p>
  888. </div>
  889. <!-- 记录 + 虚线分隔 -->
  890. <div>
  891. <el-divider border-style="dashed" />
  892. <p>{{ $t('common_add.similarRechargeRecords') }}</p>
  893. · {{ ReadCookiesTime }} {{ $t('common_add.buy') }} {{ addConsume.goodsName.value }}({{
  894. $t('common_add.operator') }}: {{ adminData.adminName }})
  895. </div>
  896. <div style="margin-top: 10px">
  897. <p>{{ $t('common_add.continueOperation') }}</p>
  898. </div>
  899. </div>
  900. <!-- 底部按钮居中 -->
  901. <template #footer>
  902. <div class="dialog-footer-center">
  903. <el-button @click="ConsumeDialogVisibleCancel">{{ $t('common.cancel') }}</el-button>
  904. <el-button type="primary" @click="ConsumeDialogVisibleContinue">{{ $t('common.confirm') }}</el-button>
  905. </div>
  906. </template>
  907. </el-dialog>
  908. </div>
  909. </div>
  910. </template>
  911. <style scoped lang="scss">
  912. p {
  913. margin: 0px;
  914. }
  915. /* 上传图片的格式 */
  916. .avatar-uploader .avatar {
  917. width: 50px;
  918. height: 50px;
  919. display: block;
  920. }
  921. .add-form {
  922. width: 400px;
  923. float: left;
  924. }
  925. /* 标题居中 */
  926. .el-dialog__header {
  927. text-align: center;
  928. }
  929. .confirm-body {
  930. width: 350px;
  931. margin: 0 auto;
  932. }
  933. /* 字段块与标签样式 */
  934. .field {
  935. margin-bottom: 14px;
  936. }
  937. .field-label {
  938. font-size: 14px;
  939. color: #606266;
  940. margin-bottom: 6px;
  941. }
  942. /* 金币行紧凑 */
  943. .coins-row .field {
  944. margin-bottom: 0;
  945. }
  946. /* 底部按钮居中 */
  947. .dialog-footer-center {
  948. display: flex;
  949. justify-content: center;
  950. gap: 12px;
  951. }
  952. .father1 {
  953. width: 82vw;
  954. height: 80vh;
  955. display: flex;
  956. .left {
  957. width: 500px;
  958. float: left;
  959. display: flex;
  960. }
  961. .right {
  962. flex: 1;
  963. height: 50vh;
  964. display: flex;
  965. align-items: center;
  966. .customer-info {
  967. width: 300px;
  968. margin-left: 20px;
  969. display: flex;
  970. justify-content: center;
  971. align-items: center;
  972. }
  973. }
  974. }
  975. </style>