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.

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