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.

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