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.

1156 lines
36 KiB

7 months ago
6 months ago
6 months ago
6 months ago
3 months ago
3 months ago
6 months ago
6 months ago
3 months ago
3 months ago
3 months ago
3 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
3 months ago
6 months ago
6 months ago
7 months ago
8 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
3 months ago
7 months ago
7 months ago
7 months ago
6 months ago
3 months ago
6 months ago
6 months ago
6 months ago
3 months ago
3 months ago
3 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
3 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
3 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
6 months ago
7 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
  1. <script setup>
  2. import { onMounted, reactive, ref, watch, computed, nextTick } 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(t('elmessage.limitRedAmount'));
  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 WriteCookiesVale = ref({})
  243. // 用来读的 cookie 的 key
  244. const ReadCookies = ref(null)
  245. // 用来读的 cookie 的 value
  246. const ReadCookiesValue = ref({})
  247. const orderStatus = ref('')
  248. // 这是添加消费信息的接口
  249. const add = async function () {
  250. try {
  251. // 验证输入数据 再验证一次
  252. if (!validateInput()) {
  253. return;
  254. }
  255. // 计算金币使用情况
  256. calculateCoins(addConsume.value.sumGold);
  257. console.log("addConsume.value", addConsume.value)
  258. addDisabled.value = true
  259. // 发送POST请求
  260. const result = await request({
  261. url: "/consume/add",
  262. data: {
  263. jwcode: addConsume.value.jwcode,
  264. adminId: adminData.value.id,
  265. price: Number(addConsume.value.price || 0) * 100,
  266. sumGold: addConsume.value.sumGold * 100,
  267. freeGold: addConsume.value.freeGold * 100,
  268. taskGold: addConsume.value.taskGold * 100,
  269. permanentGold: addConsume.value.permanentGold * 100,
  270. goodsName: addConsume.value.goodsName,
  271. remark: addConsume.value.remark,
  272. adminName: adminData.value.adminName,
  273. redMoney: Number(addConsume.value.redMoney), // 1-使用红包,0-不使用红包
  274. redIds: addConsume.value.redMoney === 1 ? selectedReds.value.map(item => item.id) : [] // 选中的红包ID数组
  275. }
  276. })
  277. addDisabled.value = false
  278. console.log("add请求", result);
  279. // 处理响应
  280. handleResponse(result);
  281. // 重置表单
  282. resetForm();
  283. } catch (error) {
  284. console.error("请求失败", error);
  285. ElMessage.error(t('elmessage.addFailed'));
  286. }
  287. };
  288. // 响应处理函数
  289. function handleResponse(result) {
  290. console.log("响应结果", result)
  291. if (result.code === 200) {
  292. WriteCookies.value = `coinConsume:${addConsume.value.jwcode}:${addConsume.value.goodsName}`
  293. //value 为消耗时间
  294. WriteCookiesVale.value = JSON.stringify({
  295. payTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
  296. orderCode: result.data
  297. });
  298. Cookies.set(WriteCookies.value, WriteCookiesVale.value, {expires: 1, path: '/'});
  299. ElMessage.success(t('elmessage.addSuccess'));
  300. console.log("请求成功", result);
  301. } else {
  302. ElMessage.error(result.msg || t('elmessage.addFailedUnknown'));
  303. }
  304. }
  305. // 重置表单函数
  306. function resetForm() {
  307. // 清空表单数据
  308. Ref.value.resetFields();
  309. addConsume.value = {
  310. jwcode: null,
  311. goodsName: "",
  312. sumGold: null,
  313. freeGold: null,
  314. permanentGold: null,
  315. taskGold: null,
  316. remark: "",
  317. adminId: adminData.value.id,
  318. adminName: adminData.value.adminName,
  319. redMoney: 1 // 默认使用红包
  320. }
  321. // 清空红包相关数据
  322. selectedReds.value = []
  323. redList.value = []
  324. console.log("重置表单")
  325. user.value = {
  326. jwcode: null,
  327. name: "",
  328. market: "",
  329. historySumGold: null,
  330. historyPermanentGold: null,
  331. historyFreeGold: null,
  332. historyTaskGold: null,
  333. rechargeNum: null,
  334. consumeNum: null,
  335. firstRecharge: "",
  336. nowPermanentGold: null,
  337. nowFreeJune: null,
  338. nowTaskGold: null,
  339. nowFreeDecember: null,
  340. nowFreeGold: null,
  341. nowSumGold: null
  342. }
  343. if (Ref.value) {
  344. nextTick(() => {
  345. Ref.value.clearValidate()
  346. })
  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. const historyOrder = ref({})
  401. // 查询订单号对应的状态
  402. const selectByOrderCode = async function (data) {
  403. try {
  404. const result = await request({
  405. url: '/recharge/selectByOrderCode',
  406. data: data,
  407. headers: {
  408. 'Content-Type': 'text/plain;charset=utf-8'
  409. },
  410. })
  411. if(result.code == 200){
  412. historyOrder.value = result.data
  413. }
  414. } catch (error) {
  415. console.log('请求失败', error)
  416. // 在这里可以处理错误逻辑,比如显示错误提示等
  417. }
  418. }
  419. // 添加前验证
  420. const addBefore = () => {
  421. Ref.value.validate(async (valid) => {
  422. // 验证cookie
  423. if (!valid) {
  424. ElMessage({
  425. type: 'error',
  426. message: t('elmessage.checkInputContent')
  427. });
  428. return;
  429. }
  430. if (!validateInput() || !validateRedLimit()) {
  431. return;
  432. }
  433. ReadCookies.value = `coinConsume:${addConsume.value.jwcode}:${addConsume.value.goodsName}`
  434. console.log('ReadCookies',ReadCookies.value);
  435. // 获取cookie
  436. const cookieValue = Cookies.get(ReadCookies.value);
  437. if (cookieValue) {
  438. // 解析为对象
  439. const rechargeInfo = JSON.parse(cookieValue);
  440. console.log('充值时间:', rechargeInfo.payTime);
  441. console.log('订单号:', rechargeInfo.orderCode);
  442. ReadCookiesValue.value = {
  443. ...rechargeInfo,
  444. }
  445. }
  446. if (Object.keys(ReadCookiesValue.value).length > 0) {
  447. await selectByOrderCode(ReadCookiesValue.value.orderCode)
  448. if(historyOrder.value.type == 1 ){
  449. if(historyOrder.value.auditStatus == 1 ){
  450. if(historyOrder.value.isRefund == 0){
  451. orderStatus.value = '正常'
  452. }else if(historyOrder.value.isRefund == 1){
  453. orderStatus.value = '已退款'
  454. }
  455. }
  456. }
  457. ConsumeDialogVisible.value = true;
  458. } else {
  459. FirstConsumeDialogVisible.value = true;
  460. }
  461. });
  462. };
  463. // 查询客户信息(通过精网号)
  464. const getUser = async function (jwcode) {
  465. try {
  466. // 验证精网号
  467. if (!jwcode) {
  468. ElMessage.warning(t('elmessage.noEmptyJwcode'));
  469. return;
  470. }
  471. // 验证精网号是否为数字
  472. if (!/^\d{1,9}$/.test(jwcode)) {
  473. ElMessage.warning(t('elmessage.limitJwcodeNine'));
  474. resetForm()
  475. return;
  476. }
  477. // 发送POST请求
  478. const result = await request({
  479. url: "/user/selectUser",
  480. data: { jwcode }
  481. });
  482. console.log("请求成功", result);
  483. const { code, data } = result;
  484. const region = data && data.market;
  485. if (data && Object.keys(data).length > 0) {
  486. // 查询成功后,重置表单(保留精网号),防止上一个用户的数据干扰
  487. const currentJwcode = addConsume.value.jwcode;
  488. resetForm();
  489. addConsume.value.jwcode = currentJwcode;
  490. // 处理用户数据
  491. user.value = {
  492. ...data,
  493. // 统一处理所有黄金数值,除以100
  494. nowPermanentGold: data.nowPermanentGold,
  495. nowFreeGold: data.nowFreeGold,
  496. nowSumGold: data.nowSumGold,
  497. nowTaskGold: data.nowTaskGold,
  498. nowFreeJune: data.nowFreeJune,
  499. nowFreeDecember: data.nowFreeDecember,
  500. historySumGold: data.historySumGold,
  501. historyPermanentGold: data.historyPermanentGold,
  502. historyFreeGold: data.historyFreeGold,
  503. historyTaskGold: data.historyTaskGold
  504. };
  505. if (code === 0 && region) {
  506. ElMessage.info({
  507. message: t('elmessage.userRegionHint', { region }),
  508. customClass: 'custom-blue-message'
  509. });
  510. return;
  511. }
  512. if (code === 200) {
  513. ElMessage.success(t('elmessage.searchSuccess'));
  514. // 如果当前开启了红包模式,获取红包列表
  515. if (addConsume.value.redMoney === 1) {
  516. getRedList()
  517. }
  518. // 验证输入
  519. validateInput()
  520. } else {
  521. ElMessage.warning(result.msg || t('elmessage.checkQueryParams'));
  522. }
  523. } else {
  524. ElMessage.warning(t('elmessage.noUser'));
  525. user.value.jwcode = null
  526. addConsume.value.jwcode = null
  527. // resetForm(); // 重置表单
  528. }
  529. } catch (error) {
  530. console.error("请求失败", error);
  531. ElMessage.error(t('elmessage.jwcodeError'));
  532. resetForm(); // 重置表单
  533. }
  534. };
  535. // 获取商品信息(三楼接口)
  536. const getGoods = async function () {
  537. try {
  538. // 发送POST请求
  539. const result = await request({
  540. // url: "/product", //
  541. // url: "http://39.101.133.168:8828/live_mall/api/product/all",
  542. url: "https://api.homilychart.com/live_mall/api/product/all",
  543. });
  544. // 将响应结果存储到响应式数据中
  545. console.log("请求成功", result);
  546. goods.value = result.data.map(item => ({
  547. id: item.id,
  548. label: item.name,
  549. value: item.name,
  550. price: item.price
  551. }));
  552. } catch (error) {
  553. console.log("请求失败", error);
  554. // 在这里可以处理错误逻辑,比如显示错误提示等
  555. }
  556. };
  557. /*
  558. ====================监听=================================
  559. */
  560. // 监听消费总金额变化,自动计算三类金币
  561. watch(
  562. () => addConsume.value.sumGold,
  563. (newValue) => {
  564. const parsedNewValue = parseFloat(newValue);
  565. if (!isNaN(parsedNewValue) && parsedNewValue >= 0) {
  566. const { free, permanent, task } = calculateCoins(parsedNewValue);
  567. addConsume.value.freeGold = free;
  568. addConsume.value.permanentGold = permanent;
  569. addConsume.value.taskGold = task;
  570. } else {
  571. addConsume.value.freeGold = null;
  572. addConsume.value.permanentGold = null;
  573. addConsume.value.taskGold = null;
  574. }
  575. }
  576. );
  577. // 监听商品选择,自动展示原价
  578. const handleGoodsChange = (newGoods) => {
  579. if (newGoods && typeof newGoods === 'object') {
  580. addConsume.value.price = Number(newGoods.price || 0) || null;
  581. } else {
  582. addConsume.value.price = null;
  583. }
  584. }
  585. // 监听商品选择,自动展示原价
  586. // watch(
  587. // () => addConsume.value.goodsName,
  588. // (newGoods) => {
  589. // if (newGoods && typeof newGoods === 'object') {
  590. // addConsume.value.price = Number(newGoods.price || 0) || null;
  591. // } else {
  592. // addConsume.value.price = null;
  593. // }
  594. // }
  595. // );
  596. /*
  597. ====================红包逻辑=================================
  598. */
  599. // 红包列表
  600. const redList = ref([])
  601. // 选中的红包
  602. const selectedReds = ref([])
  603. // 获取红包列表
  604. const getRedList = async () => {
  605. // 必须先有查询到的用户信息
  606. if (!user.value.jwcode) return
  607. try {
  608. const result = await request({
  609. url: "/Temporary/RedList",
  610. data: { jwcode: user.value.jwcode }
  611. })
  612. console.log("红包列表", result)
  613. if (result.code === 200) {
  614. redList.value = result.data || []
  615. }
  616. } catch (error) {
  617. console.error("获取红包列表失败", error)
  618. }
  619. }
  620. // 监听是否使用红包
  621. watch(() => addConsume.value.redMoney, (val) => {
  622. // 切换模式时,清除sumGold的验证状态,防止之前的错误提示残留
  623. if (Ref.value) {
  624. Ref.value.clearValidate('sumGold')
  625. }
  626. if (val === 1) {
  627. if (!user.value.jwcode) {
  628. if (addConsume.value.jwcode) {
  629. ElMessage.warning(t('elmessage.checkUserInfo'))
  630. }
  631. redList.value = []
  632. selectedReds.value = []
  633. addConsume.value.sumGold = null
  634. return
  635. }
  636. selectedReds.value = []
  637. getRedList()
  638. // 初始消耗金币等于原价(未选红包时)
  639. if (addConsume.value.price) {
  640. addConsume.value.sumGold = addConsume.value.price
  641. } else {
  642. addConsume.value.sumGold = null
  643. }
  644. } else {
  645. selectedReds.value = []
  646. addConsume.value.sumGold = null
  647. }
  648. })
  649. // 计算已选红包总额
  650. const totalRedAmount = computed(() => {
  651. return selectedReds.value.reduce((sum, item) => {
  652. // 根据实际接口返回,使用 discount 字段
  653. const amount = Number(item.discount || 0)
  654. return sum + amount
  655. }, 0)
  656. })
  657. // 监听选中红包变化,计算剩余金币
  658. watch(selectedReds, () => {
  659. if (addConsume.value.redMoney === 1) {
  660. const price = Number(addConsume.value.price || 0)
  661. const redTotal = totalRedAmount.value
  662. // 剩余需要消耗的金币
  663. let remaining = price - redTotal
  664. if (remaining < 0) remaining = 0
  665. addConsume.value.sumGold = remaining
  666. }
  667. }, { deep: true })
  668. // 判断红包选项是否禁用
  669. const isRedOptionDisabled = (item) => {
  670. // 如果已经选中,不禁用(允许取消)
  671. if (selectedReds.value.some(r => r.id === item.id)) return false
  672. const price = Number(addConsume.value.price || 0)
  673. // 如果当前总额已经大于等于原价,则不能再选新的
  674. if (totalRedAmount.value >= price) return true
  675. return false
  676. }
  677. // 监听原价变化(比如换了商品),如果在红包模式下,需要重新计算
  678. watch(() => addConsume.value.price, (newPrice) => {
  679. if (addConsume.value.redMoney === 1) {
  680. const redTotal = totalRedAmount.value
  681. let remaining = (newPrice || 0) - redTotal
  682. if (remaining < 0) remaining = 0
  683. addConsume.value.sumGold = remaining
  684. }
  685. })
  686. /*
  687. ====================挂载=================================
  688. */
  689. // 挂载
  690. onMounted(async function () {
  691. await getGoods()
  692. console.log('adminData', adminData.value)
  693. })
  694. </script>
  695. <template>
  696. <div class="father1">
  697. <div class="left">
  698. <el-form :model="addConsume" ref="Ref" :rules="rules" style="min-width: 600px;" class="add-form"
  699. label-width="auto" label-position="right">
  700. <el-form-item prop="jwcode" :label="t('common_add.jwcode')" style="margin-top: 50px">
  701. <el-input v-model="addConsume.jwcode" style="width: 200px;" @keyup.enter="getUser(addConsume.jwcode)" />
  702. <el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 20px">
  703. {{ t('common.search') }}
  704. </el-button>
  705. </el-form-item>
  706. <el-form-item prop="goodsName" :label="t('common_add.goodsName')">
  707. <el-select v-model="addConsume.goodsName" :placeholder="t('common_add.goodsNamePlaceholder')"
  708. style="width: 200px" clearable filterable >
  709. <el-option v-for="(item, index) in goods" :key="index" :label="item.label" :value="item.value" @click="handleGoodsChange(item)" />
  710. </el-select>
  711. </el-form-item>
  712. <el-form-item prop="price" :label="t('common_add.price')">
  713. <el-input v-model="addConsume.price" style="width: 120px" disabled />
  714. </el-form-item>
  715. <!-- <el-form-item prop="sumGold" :label="t('common_add.consumeTotalGold')">
  716. <el-input v-model="addConsume.sumGold" style="width: 120px"
  717. @blur="validateRedLimit()" />
  718. </el-form-item> -->
  719. <el-form-item prop="sumGold" :label="t('common_add.consumeTotalGold')">
  720. <div style="display: flex; align-items: flex-start;">
  721. <el-input v-model="addConsume.sumGold" style="width: 120px; flex-shrink: 0;" @blur="validateRedLimit()"
  722. :disabled="addConsume.redMoney === 1" />
  723. <div style="display: flex; flex-wrap: wrap; margin-left: 10px;">
  724. <el-radio v-model="addConsume.redMoney" :label="1">{{ t('common_add.使用红包') }}</el-radio>
  725. <el-radio v-model="addConsume.redMoney" :label="0">{{ t('common_add.不使用红包') }}</el-radio>
  726. </div>
  727. </div>
  728. </el-form-item>
  729. <el-form-item v-if="addConsume.redMoney === 1" :label="t('common_add.选择红包')">
  730. <el-select
  731. v-model="selectedReds"
  732. multiple
  733. :placeholder="t('common_add.请选择红包')"
  734. style="width: 200px"
  735. value-key="id"
  736. collapse-tags
  737. >
  738. <el-option
  739. v-for="item in redList"
  740. :key="item.id"
  741. :label="`${item.title} (${item.discount})`"
  742. :value="item"
  743. :disabled="isRedOptionDisabled(item)"
  744. />
  745. </el-select>
  746. <span style="margin-left: 10px; color: #666;">
  747. {{ t('common_add.已抵扣') }}: {{ totalRedAmount }}
  748. </span>
  749. </el-form-item>
  750. <!-- 三类金币自动计算禁用状态不可编辑 -->
  751. <el-form-item prop="permanentGold" :label="t('common_add.permanentGold')">
  752. <el-input v-model="addConsume.permanentGold" disabled style="width: 120px">
  753. <template #default="scope">{{ scope.row.permanentGold }}</template>
  754. </el-input>
  755. <p style="margin-right: 0px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  756. </el-form-item>
  757. <el-form-item prop="freeCoin" :label="t('common_add.freeGold')">
  758. <el-input disabled v-model="addConsume.freeGold" style="width: 120px" />
  759. <p style="margin-right: 0px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  760. </el-form-item>
  761. <el-form-item prop="taskGold" :label="t('common_add.taskGold')">
  762. <el-input disabled v-model="addConsume.taskGold" style="width: 120px" />
  763. <p style="margin-right: 20px">&nbsp;&nbsp;{{ $t('common.个') }}</p>
  764. </el-form-item>
  765. <el-form-item prop="remark" :label="t('common_add.remark')">
  766. <el-input v-model="addConsume.remark" style="width: 250px" :rows="4" maxlength="100" show-word-limit
  767. type="textarea" />
  768. </el-form-item>
  769. <el-button type="success" @click="resetForm()" style="margin-left: 200px;margin-top:10px">{{ t('common.reset')
  770. }}</el-button>
  771. <el-button type="primary" :disabled="addDisabled" @click="addBefore" style="margin-top:10px">{{
  772. t('common.submit')
  773. }}</el-button>
  774. </el-form>
  775. </div>
  776. <div class="right">
  777. <!-- 客户信息栏 -->
  778. <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
  779. <el-form :model="user" label-width="auto" style="max-width: 1200px" label-position="left">
  780. <el-text size="large" style="margin-left: 20px">{{ $t('common_add_user.customerInfo') }}</el-text>
  781. <!-- 第一行姓名 + 历史金币 -->
  782. <el-row style="margin-top: 20px">
  783. <el-col :span="9">
  784. <el-form-item label-width="120px" :label="$t('common_add_user.name')">
  785. <p>{{ user.name }}</p>
  786. </el-form-item>
  787. </el-col>
  788. <el-col :span="14">
  789. <el-form-item :label="$t('common_add_user.currentGoldCoinTotal')" style="width: 500px">
  790. <span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{
  791. user.nowSumGold
  792. }}</span>
  793. </el-form-item>
  794. <!-- 金币详情独立显示 -->
  795. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  796. <span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">({{
  797. $t('common_add_user.permanentGold') }}:{{
  798. user.nowPermanentGold
  799. }};
  800. {{ $t('common_add_user.freeGold') }}:{{ user.nowFreeGold }};
  801. {{ $t('common_add_user.taskGold') }}:{{ user.nowTaskGold }})</span>
  802. </el-form-item>
  803. </el-col>
  804. </el-row>
  805. <!-- 第二行精网号 + 当前金币独立行 -->
  806. <el-row>
  807. <el-col :span="9">
  808. <el-form-item label-width="120px" :label="$t('common_add_user.jwcode')">
  809. <p>{{ user.jwcode }}</p>
  810. </el-form-item>
  811. </el-col>
  812. <el-col :span="14">
  813. <el-form-item :label="$t('common_add_user.consumptionTimes')">
  814. <p style="color: #2fa1ff">{{ user.consumeNum }} </p>
  815. </el-form-item>
  816. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  817. <p style="font-size: small; color: #b1b1b1">({{ $t('common_add_user.onlyStatisticsDataAfter20250101')
  818. }})
  819. </p>
  820. </el-form-item>
  821. </el-col>
  822. </el-row>
  823. <!-- 第三行首次充值日期 + 充值次数 -->
  824. <!-- <el-row >
  825. <el-col :span="9">
  826. <el-form-item label="首次充值日期">
  827. <p v-if="user.firstRecharge">
  828. {{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
  829. </p>
  830. </el-form-item>
  831. </el-col>
  832. </el-row> -->
  833. <!-- 第四行消费次数 + 所属门店 -->
  834. <el-row>
  835. <el-col :span="9">
  836. <el-form-item :label="$t('common_add_user.store')">
  837. <p>{{ user.market }}</p>
  838. </el-form-item>
  839. </el-col>
  840. <el-col :span="14">
  841. <el-form-item :label="$t('common_add_user.maxReductionAmount')">
  842. <p style="color: #2fa1ff">{{ user.red }} </p>
  843. </el-form-item>
  844. </el-col>
  845. </el-row>
  846. </el-form>
  847. </el-card>
  848. <el-dialog v-model="FirstConsumeDialogVisible" :title="$t('common_add.operationConfirm')"
  849. :before-close="FirstConsumeDialogVisiblehandleClose" :close-on-click-modal="false" width="480px">
  850. <!-- 内容整体居中且收窄 -->
  851. <div class="confirm-body">
  852. <!-- 用户信息 -->
  853. <div>
  854. <div class="field-label">{{ $t('common_add.userInfo') }}</div>
  855. <el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
  856. </div>
  857. <!-- 商品名称 -->
  858. <div class="field">
  859. <div class="field-label">{{ $t('common_add.goodsName') }}</div>
  860. <el-input v-model="addConsume.goodsName" disabled />
  861. </div>
  862. <!--金币总数 -->
  863. <div class="field">
  864. <div class="field-label">{{ $t('common_add.totalGold') }}</div>
  865. <el-input v-model="addConsume.sumGold" disabled />
  866. </div>
  867. <!-- 金币详细信息同一行左右排列 -->
  868. <el-row :gutter="20" class="coins-row">
  869. <el-col :span="8">
  870. <div class="field">
  871. <div class="field-label">{{ $t('common_add.permanentGold') }}</div>
  872. <el-input v-model="addConsume.permanentGold" disabled />
  873. </div>
  874. </el-col>
  875. <el-col :span="8">
  876. <div class="field">
  877. <div class="field-label">{{ $t('common_add.freeGold') }}</div>
  878. <el-input v-model="addConsume.freeGold" disabled />
  879. </div>
  880. </el-col>
  881. <el-col :span="8">
  882. <div class="field">
  883. <div class="field-label">{{ $t('common_add.taskGold') }}</div>
  884. <el-input v-model="addConsume.taskGold" disabled />
  885. </div>
  886. </el-col>
  887. </el-row>
  888. <div class="field">
  889. <div class="field-label">{{ $t('common_add.remark') }}</div>
  890. <el-input v-model="addConsume.remark" disabled />
  891. </div>
  892. </div>
  893. <!-- 底部按钮居中 -->
  894. <template #footer>
  895. <div class="dialog-footer-center">
  896. <el-button @click="FirstConsumeDialogVisibleCancel">{{ $t('common.cancel') }}</el-button>
  897. <el-button type="primary" @click="FirstConsumeDialogVisibleContinue">{{ $t('common.confirm') }}</el-button>
  898. </div>
  899. </template>
  900. </el-dialog>
  901. <el-dialog v-model="ConsumeDialogVisible" :title="$t('common_add.operationConfirm')"
  902. :before-close="ConsumeDialogVisiblehandleClose" :close-on-click-modal="false" width="480px">
  903. <!-- 内容整体居中且收窄 -->
  904. <div class="confirm-body">
  905. <!-- 用户信息 -->
  906. <div>
  907. <div class="field-label">{{ $t('common_add.userInfo') }}</div>
  908. <el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
  909. </div>
  910. <!-- 商品名称 -->
  911. <div class="field">
  912. <div class="field-label">{{ $t('common_add.goodsName') }}</div>
  913. <el-input v-model="addConsume.goodsName" disabled />
  914. </div>
  915. <!--金币总数 -->
  916. <div class="field">
  917. <div class="field-label">{{ $t('common_add.totalGold') }}</div>
  918. <el-input v-model="addConsume.sumGold" disabled />
  919. </div>
  920. <!-- 金币详细信息同一行左右排列 -->
  921. <el-row :gutter="20" class="coins-row">
  922. <el-col :span="8">
  923. <div class="field">
  924. <div class="field-label">{{ $t('common_add.permanentGold') }}</div>
  925. <el-input v-model="addConsume.permanentGold" disabled />
  926. </div>
  927. </el-col>
  928. <el-col :span="8">
  929. <div class="field">
  930. <div class="field-label">{{ $t('common_add.freeGold') }}</div>
  931. <el-input v-model="addConsume.freeGold" disabled />
  932. </div>
  933. </el-col>
  934. <el-col :span="8">
  935. <div class="field">
  936. <div class="field-label">{{ $t('common_add.taskGold') }}</div>
  937. <el-input v-model="addConsume.taskGold" disabled />
  938. </div>
  939. </el-col>
  940. </el-row>
  941. <!-- 风险提示 -->
  942. <div style="display: flex; align-items: center; margin-top: 20px;">
  943. <el-icon :size="24" color="#FFD700">
  944. <WarnTriangleFilled />
  945. </el-icon>
  946. <p>{{ $t('common_add.promptConsume') }}</p>
  947. </div>
  948. <!-- 记录 + 虚线分隔 -->
  949. <div>
  950. <el-divider border-style="dashed" />
  951. <p>{{ $t('common_add.similarCosumeRecords') }}</p>
  952. · {{ ReadCookiesValue.payTime }} {{ $t('common_add.buy') }} {{ addConsume.goodsName }}{{ $t('common_add.orderStatus') }}: {{ orderStatus }}({{
  953. $t('common_add.operator') }}: {{ adminData.adminName }})
  954. </div>
  955. <div style="margin-top: 10px">
  956. <p>{{ $t('common_add.continueOperation') }}</p>
  957. </div>
  958. </div>
  959. <!-- 底部按钮居中 -->
  960. <template #footer>
  961. <div class="dialog-footer-center">
  962. <el-button @click="ConsumeDialogVisibleCancel">{{ $t('common.cancel') }}</el-button>
  963. <el-button type="primary" @click="ConsumeDialogVisibleContinue">{{ $t('common.confirm') }}</el-button>
  964. </div>
  965. </template>
  966. </el-dialog>
  967. </div>
  968. </div>
  969. </template>
  970. <style scoped lang="scss">
  971. p {
  972. margin: 0px;
  973. }
  974. /* 上传图片的格式 */
  975. .avatar-uploader .avatar {
  976. width: 50px;
  977. height: 50px;
  978. display: block;
  979. }
  980. .add-form {
  981. width: 400px;
  982. float: left;
  983. }
  984. /* 标题居中 */
  985. .el-dialog__header {
  986. text-align: center;
  987. }
  988. .confirm-body {
  989. width: 350px;
  990. margin: 0 auto;
  991. }
  992. /* 字段块与标签样式 */
  993. .field {
  994. margin-bottom: 14px;
  995. }
  996. .field-label {
  997. font-size: 14px;
  998. color: #606266;
  999. margin-bottom: 6px;
  1000. }
  1001. /* 金币行紧凑 */
  1002. .coins-row .field {
  1003. margin-bottom: 0;
  1004. }
  1005. /* 底部按钮居中 */
  1006. .dialog-footer-center {
  1007. display: flex;
  1008. justify-content: center;
  1009. gap: 12px;
  1010. }
  1011. .father1 {
  1012. width: 82vw;
  1013. height: 80vh;
  1014. display: flex;
  1015. .left {
  1016. width: 500px;
  1017. float: left;
  1018. display: flex;
  1019. }
  1020. .right {
  1021. flex: 1;
  1022. height: 50vh;
  1023. display: flex;
  1024. align-items: center;
  1025. .customer-info {
  1026. width: 300px;
  1027. margin-left: 20px;
  1028. display: flex;
  1029. justify-content: center;
  1030. align-items: center;
  1031. }
  1032. }
  1033. }
  1034. </style>