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.

634 lines
20 KiB

1 month ago
1 month ago
1 month ago
1 month ago
  1. <script setup>
  2. import { onMounted, reactive, ref, watch } from "vue";
  3. import { ElMessage, ElMessageBox } from "element-plus";
  4. import moment from "moment";
  5. import request from "@/util/http.js"
  6. // 精网号去空格
  7. const trimJwCode = () => {
  8. if (addConsume.value.jwcode) {
  9. // 去除所有空格,并尝试转换为整数
  10. const trimmed = addConsume.value.jwcode.toString().replace(/\s/g, '');
  11. const numeric = Number(trimmed);
  12. // 如果转换为数字成功,保存为数字,否则提示错误
  13. if (!isNaN(numeric)) {
  14. addConsume.value.jwcode = numeric;
  15. } else {
  16. ElMessage.error("精网号格式不正确,请输入数字");
  17. }
  18. }
  19. }
  20. /*
  21. ====================数据=================================
  22. */
  23. //这是获取当前登录的用户信息
  24. const adminData = ref({});
  25. // 通过精网号查询用户(客户)信息 表单
  26. const user = ref({
  27. jwcode: null,
  28. name: "",
  29. market: "",
  30. historySumGold: null,
  31. historyPermanentGold: null,
  32. historyFreeGold: null,
  33. historyTaskGold: null,
  34. rechargeNum: null,
  35. consumeNum: null,
  36. firstRecharge: "",
  37. nowPermanentGold: null,
  38. nowFreeJune: null,
  39. nowTaskGold: null,
  40. nowFreeDecember: null,
  41. nowFreeGold: null,
  42. nowSumGold: null
  43. })
  44. // 这是添加消费信息的表单(金币)
  45. const addConsume = ref({
  46. // jwcode 是数字
  47. jwcode: null, //精网号
  48. goodsName: "",// 商品名称
  49. sumGold: null, // 消费金币总数
  50. freeGold: null, // 免费金币
  51. permanentGold: null, // 永久金币
  52. taskGold: null, // 任务金币
  53. remark: "",//备注
  54. adminId: null,// 当前管理员id
  55. });
  56. // 表单验证
  57. const Ref = ref(null);
  58. // 表单验证规则
  59. const rules = reactive({
  60. jwcode: [
  61. { required: true, message: "请输入精网号", trigger: "blur" },
  62. // { type: 'number', message: "精网号必须为数字", trigger: "blur" }
  63. ],
  64. goodsName: [{ required: true, message: "请选择商品", trigger: "blur" }],
  65. sumGold: [
  66. { required: true, message: "消耗金币总数不能为空", trigger: "blur" },
  67. {
  68. validator: (rule, value, callback) => {
  69. // 允许0开头的小数(如0.1)但不允许单独的0
  70. const isValid = /^(0\.\d{1,2})|([1-9]\d*(\.\d{1,2})?)$/.test(value);
  71. if (!isValid) {
  72. callback(new Error("请输入大于0的正数(可包含最多两位小数)"));
  73. } else {
  74. callback();
  75. }
  76. },
  77. trigger: "blur"
  78. }
  79. ]
  80. });
  81. // 查询商品的表单
  82. const goods = ref([]);
  83. /*
  84. ====================方法=================================
  85. */
  86. const getAdminData = async function () {
  87. try {//await 暂停函数执行,直到请求完成
  88. const result = await request({
  89. url: "/admin/userinfo",
  90. data: {},
  91. });
  92. adminData.value = result;
  93. addConsume.value.adminId = adminData.value.id;
  94. addConsume.value.name = adminData.value.adminName;
  95. console.log("请求成功", result);
  96. console.log("用户信息", adminData.value);
  97. } catch (error) {
  98. console.log("请求失败", error);
  99. }
  100. };
  101. // 输入验证函数
  102. function validateInput() {
  103. const sumGold = parseFloat(addConsume.value.sumGold);
  104. trimJwCode();
  105. if (user.value.jwcode == null) {
  106. ElMessage.warning("请先查询用户信息");
  107. addConsume.value.sumGold = null;
  108. user.value = {};
  109. return false;
  110. }
  111. /*
  112. // 验证金币数值
  113. if (user.value.jwcode && (isNaN(sumGold) || sumGold <= 0)) {
  114. ElMessage.warning("消费金币总数必须是大于0的数字");
  115. // 将sumGold设置为null
  116. addConsume.value.sumGold = null;
  117. return false;
  118. }
  119. */
  120. // sumGold 补充0(比如.1 为0.1)
  121. if (addConsume.value.sumGold && addConsume.value.sumGold.toString().startsWith('.')) {
  122. addConsume.value.sumGold = '0' + addConsume.value.sumGold;
  123. // ElMessage.info('已自动补充前导0');
  124. }
  125. // 验证金币不能为负数
  126. if (sumGold < 0) {
  127. ElMessage.warning("消耗金币总数不能为负数");
  128. addConsume.value.sumGold = null;
  129. return false;
  130. }
  131. // 小数位数限制 2位,整数位数限制 6位
  132. if (addConsume.value.sumGold) {
  133. const sumGoldStr = addConsume.value.sumGold.toString();
  134. // 检查整数部分长度
  135. if (sumGoldStr.includes('.')) {
  136. const integerPart = sumGoldStr.split('.')[0];
  137. if (integerPart.length > 6) {
  138. // 截断整数部分到6位并提示
  139. const truncatedInteger = integerPart.slice(0, 6);
  140. addConsume.value.sumGold = parseFloat(truncatedInteger);
  141. ElMessage.info('整数部分最多允许6位');
  142. return; // 直接返回,不再处理小数部分
  143. }
  144. } else {
  145. // 纯整数情况
  146. if (sumGoldStr.length > 6) {
  147. addConsume.value.sumGold = parseFloat(sumGoldStr.slice(0, 6));
  148. ElMessage.info('整数部分最多允许6位');
  149. return;
  150. }
  151. }
  152. // 处理小数部分
  153. if (sumGoldStr.includes('.')) {
  154. const decimalPart = sumGoldStr.split('.')[1];
  155. if (decimalPart.length > 2) {
  156. // 截断到两位小数并提示
  157. const truncatedValue = parseFloat(sumGoldStr.slice(0, sumGoldStr.indexOf('.') + 3));
  158. addConsume.value.sumGold = truncatedValue;
  159. ElMessage.info('最多允许输入两位小数');
  160. }
  161. }
  162. }
  163. // 验证金币总和
  164. const totalAvailableGold = (user.value.nowSumGold)
  165. if (user.value.jwcode && sumGold > totalAvailableGold) {
  166. ElMessage.error("消耗金币总数超过可用金币总和");
  167. // 将sumGold设置为null
  168. addConsume.value.sumGold = null;
  169. return false;
  170. }
  171. return true;
  172. }
  173. // 消耗金币计算函数
  174. function calculateCoins(sumGold) {
  175. console.log("消耗金币计算函数:计算金币", sumGold);
  176. const parsedSumGold = parseFloat(sumGold);
  177. if (isNaN(parsedSumGold) || parsedSumGold <= 0 || !user.value.jwcode) {
  178. return { free: 0, permanent: 0, task: 0 };
  179. }
  180. const { nowFreeGold, nowPermanentGold, nowTaskGold } = user.value;
  181. let remaining = parsedSumGold;
  182. let freeUsed = 0, permanentUsed = 0, taskUsed = 0;
  183. // 优先消耗免费金币
  184. if (nowFreeGold > 0) {
  185. freeUsed = Math.min(parseFloat(nowFreeGold.toFixed(4)), remaining);
  186. remaining = parseFloat((remaining - freeUsed).toFixed(4));
  187. }
  188. // 其次消耗永久金币
  189. if (remaining > 0 && nowPermanentGold > 0) {
  190. permanentUsed = Math.min(parseFloat(nowPermanentGold.toFixed(4)), remaining);
  191. remaining = parseFloat((remaining - permanentUsed).toFixed(4));
  192. }
  193. // 最后消耗任务金币
  194. if (remaining > 0 && nowTaskGold > 0) {
  195. taskUsed = parseFloat(remaining.toFixed(4));
  196. }
  197. // 更新金币值
  198. addConsume.value.freeGold = freeUsed;
  199. addConsume.value.permanentGold = permanentUsed;
  200. addConsume.value.taskGold = taskUsed;
  201. return { free: freeUsed, permanent: permanentUsed, task: taskUsed };
  202. }
  203. // 这是添加消费信息的接口
  204. const add = async function () {
  205. try {
  206. // 验证输入数据 再验证一次
  207. if (!validateInput()) {
  208. return;
  209. }
  210. // 计算金币使用情况
  211. calculateCoins(addConsume.value.sumGold);
  212. console.log("addConsume.value", addConsume.value)
  213. // 发送POST请求
  214. const result = await request({
  215. // url: "/consume/add",
  216. url: "/consume/add",
  217. data: {
  218. ...addConsume.value,
  219. jwcode: addConsume.value.jwcode,
  220. adminId: addConsume.value.adminId,
  221. sumGold: addConsume.value.sumGold * 100,
  222. freeGold: addConsume.value.freeGold * 100,
  223. taskGold: addConsume.value.taskGold * 100,
  224. permanentGold: addConsume.value.permanentGold * 100,
  225. goodsName: addConsume.value.goodsName,
  226. remark: addConsume.value.remark
  227. }
  228. });
  229. console.log("add请求", result);
  230. // 处理响应
  231. handleResponse(result);
  232. // 重置表单
  233. resetForm();
  234. } catch (error) {
  235. console.error("请求失败", error);
  236. ElMessage.error("添加失败,请检查网络连接或联系管理员");
  237. }
  238. };
  239. // 响应处理函数
  240. function handleResponse(result) {
  241. console.log("响应结果", result)
  242. if (result.code === 200) {
  243. ElMessage.success("添加成功");
  244. console.log("请求成功", result);
  245. } else {
  246. ElMessage.error(result.msg || "添加失败,未知错误");
  247. }
  248. }
  249. // 重置表单函数
  250. function resetForm() {
  251. // 清空表单数据
  252. addConsume.value = {
  253. jwcode: null,
  254. goodsName: "",
  255. sumGold: null,
  256. freeGold: null,
  257. permanentGold: null,
  258. taskGold: null,
  259. remark: "",
  260. adminId: adminData.value.id,
  261. adminName: adminData.value.adminName,
  262. };
  263. console.log("重置表单")
  264. user.value = {
  265. jwcode: null,
  266. name: "",
  267. market: "",
  268. historySumGold: null,
  269. historyPermanentGold: null,
  270. historyFreeGold: null,
  271. historyTaskGold: null,
  272. rechargeNum: null,
  273. consumeNum: null,
  274. firstRecharge: "",
  275. nowPermanentGold: null,
  276. nowFreeJune: null,
  277. nowTaskGold: null,
  278. nowFreeDecember: null,
  279. nowFreeGold: null,
  280. nowSumGold: null
  281. }
  282. }
  283. // 添加前验证
  284. const addBefore = () => {
  285. Ref.value.validate(async (valid) => {
  286. if (valid) {
  287. ElMessageBox.confirm("确认添加?")
  288. .then(() => {
  289. console.log("这里是jwcode", addConsume.value.jwcode)
  290. add();
  291. console.log("添加成功",);
  292. addConsume.value = {};
  293. })
  294. .catch(() => {
  295. console.log("取消添加");
  296. });
  297. } else {
  298. //提示
  299. ElMessage({
  300. type: "error",
  301. message: "请检查输入内容",
  302. });
  303. }
  304. });
  305. };
  306. // 查询客户信息(通过精网号)
  307. const getUser = async function (jwcode) {
  308. try {
  309. // 验证精网号
  310. if (!jwcode) {
  311. ElMessage.warning('精网号不能为空');
  312. return;
  313. }
  314. // 验证精网号是否为数字
  315. if (!/^\d{1,9}$/.test(jwcode)) {
  316. ElMessage.warning('精网号必须为数字且不超过九位');
  317. resetForm()
  318. return;
  319. }
  320. // 发送POST请求
  321. const result = await request({
  322. url: "/user/selectUser",
  323. data: { jwcode }
  324. });
  325. console.log("请求成功", result);
  326. if (result.code === 200 && result.data) {
  327. // 处理用户数据
  328. user.value = {
  329. ...result.data,
  330. // 统一处理所有黄金数值,除以100
  331. nowPermanentGold: result.data.nowPermanentGold / 100,
  332. nowFreeGold: result.data.nowFreeGold / 100,
  333. nowSumGold: result.data.nowSumGold / 100,
  334. nowTaskGold: result.data.nowTaskGold / 100,
  335. nowFreeJune: result.data.nowFreeJune / 100,
  336. nowFreeDecember: result.data.nowFreeDecember / 100,
  337. historySumGold: result.data.historySumGold / 100,
  338. historyPermanentGold: result.data.historyPermanentGold / 100,
  339. historyFreeGold: result.data.historyFreeGold / 100,
  340. historyTaskGold: result.data.historyTaskGold / 100
  341. };
  342. ElMessage.success("查询成功");
  343. // 检查sumGold是否有值,如果有则重新计算金币分配
  344. if (addConsume.value.sumGold) {
  345. const parsedSumGold = parseFloat(addConsume.value.sumGold);
  346. if (!isNaN(parsedSumGold) && parsedSumGold > 0) {
  347. const { free, permanent, task } = calculateCoins(parsedSumGold);
  348. addConsume.value.freeGold = free;
  349. addConsume.value.permanentGold = permanent;
  350. addConsume.value.taskGold = task;
  351. }
  352. }
  353. // 验证输入
  354. validateInput()
  355. } else if (!result.data) {
  356. ElMessage.warning("用户不存在");
  357. user.value.jwcode = null
  358. addConsume.value.jwcode = null
  359. // resetForm(); // 重置表单
  360. } else {
  361. ElMessage.warning(result.msg || "请检查查询参数");
  362. }
  363. } catch (error) {
  364. console.error("请求失败", error);
  365. ElMessage.error("查询失败,请检查网络连接或精网号是否正确");
  366. resetForm(); // 重置表单
  367. }
  368. };
  369. // 获取商品信息(三楼接口)
  370. const getGoods = async function () {
  371. try {
  372. // 发送POST请求
  373. const result = await request({
  374. // url: "/product", //
  375. // url: "http://39.101.133.168:8828/live_mall/api/product/all",
  376. url: "https://api.homilychart.com/live_mall/api/product/all",
  377. });
  378. // 将响应结果存储到响应式数据中
  379. console.log("请求成功", result);
  380. goods.value = result.data.map(item => ({
  381. id: item.id,
  382. label: item.name,
  383. value: item.name
  384. }));
  385. } catch (error) {
  386. console.log("请求失败", error);
  387. // 在这里可以处理错误逻辑,比如显示错误提示等
  388. }
  389. };
  390. /*
  391. ====================监听=================================
  392. */
  393. // 监听消费总金额变化,自动计算三类金币
  394. watch(
  395. () => addConsume.value.sumGold,
  396. (newValue) => {
  397. const parsedNewValue = parseFloat(newValue);
  398. if (!isNaN(parsedNewValue) && parsedNewValue > 0) {
  399. const { free, permanent, task } = calculateCoins(parsedNewValue);
  400. addConsume.value.freeGold = free;
  401. addConsume.value.permanentGold = permanent;
  402. addConsume.value.taskGold = task;
  403. } else {
  404. addConsume.value.freeGold = null;
  405. addConsume.value.permanentGold = null;
  406. addConsume.value.taskGold = null;
  407. }
  408. }
  409. );
  410. /*
  411. ====================挂载=================================
  412. */
  413. // 挂载
  414. onMounted(async function () {
  415. await getAdminData();
  416. await getGoods();
  417. });
  418. </script>
  419. <template>
  420. <div>
  421. <el-form :model="addConsume" ref="Ref" :rules="rules" style="max-width: 750px;">
  422. <div style="width:25vw">
  423. <el-form-item prop="jwcode" label="精网号" style="margin-top: 50px">
  424. <el-input v-model="addConsume.jwcode" style="width: 10vw;margin-left:45px" />
  425. <el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 10px">查询
  426. </el-button>
  427. </el-form-item>
  428. </div>
  429. <div style="width:25vw">
  430. <el-form-item prop="goodsName" label="商品名称" style="flex: 1; margin-right: 0px">
  431. <el-select v-model="addConsume.goodsName" placeholder="请选择商品" style="width: 10vw;margin-left:30px">
  432. <el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
  433. </el-select>
  434. </el-form-item>
  435. </div>
  436. <div style="width:25vw">
  437. <el-form-item prop="sumGold" label="消耗金币总数">
  438. <el-input v-model="addConsume.sumGold" style="width: 10vw;margin-left:2px" @input="validateInput()"
  439. @change="calculateCoins(addConsume.sumGold)" />
  440. </el-form-item>
  441. </div>
  442. <!-- 三类金币自动计算禁用状态不可编辑 -->
  443. <div style="width:25vw">
  444. <el-form-item prop="permanentGold" label="永久金币">
  445. <el-input v-model="addConsume.permanentGold" disabled style="width: 10vw;margin-left:40px">
  446. <template #default="scope">{{ scope.row.permanentGold }}</template>
  447. </el-input>
  448. <p style="margin-right: 0px">&nbsp;&nbsp;</p>
  449. </el-form-item>
  450. </div>
  451. <div style="width:25vw">
  452. <el-form-item prop="freeCoin" label="免费金币">
  453. <el-input disabled v-model="addConsume.freeGold" style="width: 10vw;margin-left:40px" />
  454. <p style="margin-right: 0px">&nbsp;&nbsp;</p>
  455. </el-form-item>
  456. </div>
  457. <div style="width:25vw">
  458. <el-form-item prop="taskGold" label="任务金币">
  459. <el-input disabled v-model="addConsume.taskGold" style="width: 10vw;margin-left:40px" />
  460. <p style="margin-right: 20px">&nbsp;&nbsp;</p>
  461. </el-form-item>
  462. </div>
  463. <div style="width:25vw">
  464. <el-form-item prop="remark" label="备注">
  465. <el-input v-model="addConsume.remark" style="width: 13.5vw;margin-left:70px" :rows="4" maxlength="100" show-word-limit
  466. type="textarea" />
  467. </el-form-item>
  468. </div>
  469. <el-button type="success" @click="resetForm()" style="margin-left: 200px;margin-top:10px">重置</el-button>
  470. <el-button type="primary" @click="addBefore" style="margin-top:10px"> 提交</el-button>
  471. </el-form>
  472. <!-- 客户信息栏 -->
  473. <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
  474. <el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
  475. <el-text size="large" style="margin-left: 20px">客户信息</el-text>
  476. <!-- 第一行姓名 + 历史金币 -->
  477. <el-row style="margin-top: 20px">
  478. <el-col :span="9">
  479. <el-form-item label="姓名">
  480. <p>{{ user.name }}</p>
  481. </el-form-item>
  482. </el-col>
  483. <el-col :span="14">
  484. <el-form-item label="历史金币总数">
  485. <!-- 检查 user.historySumGold 是否为有效的数字 -->
  486. <p style="color: #2fa1ff; margin-right: 5px" v-if="!isNaN(Number(user.historySumGold))">
  487. {{ Number(user.historySumGold) }}
  488. </p>
  489. <!-- 如果不是有效的数字显示默认值 -->
  490. <p v-else></p>
  491. </el-form-item>
  492. <el-form-item style="margin-top: -23px">
  493. <span style="display: inline; white-space: nowrap; color: #b1b1b1"
  494. v-if="user.historyPermanentGold !== undefined">(永久金币:{{ user.historyPermanentGold }};免费金币:{{
  495. (user.historyFreeGold)
  496. }};任务金币:{{ user.historyTaskGold }})</span>
  497. </el-form-item>
  498. </el-col>
  499. </el-row>
  500. <!-- 第二行精网号 + 当前金币独立行 -->
  501. <el-row>
  502. <el-col :span="9">
  503. <el-form-item label="精网号">
  504. <p>{{ user.jwcode }}</p>
  505. </el-form-item>
  506. </el-col>
  507. <el-col :span="14">
  508. <el-form-item label="当前金币总数" style="width: 500px">
  509. <span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{ user.nowSumGold
  510. }}</span>
  511. </el-form-item>
  512. <!-- 金币详情独立显示 -->
  513. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  514. <span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
  515. user.nowPermanentGold }};
  516. 免费金币:{{ user.nowFreeGold }};
  517. 任务金币:{{ user.nowTaskGold }})</span>
  518. </el-form-item>
  519. </el-col>
  520. </el-row>
  521. <!-- 第三行首次充值日期 + 充值次数 -->
  522. <el-row style="margin-top:-23px">
  523. <el-col :span="9">
  524. <el-form-item label="首次充值日期">
  525. <p v-if="user.firstRecharge">
  526. {{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
  527. </p>
  528. </el-form-item>
  529. </el-col>
  530. <el-col :span="14">
  531. <el-form-item label="充值次数">
  532. <p style="color: #2fa1ff">{{ user.rechargeNum }}</p>
  533. </el-form-item>
  534. </el-col>
  535. </el-row>
  536. <!-- 第四行消费次数 + 所属门店 -->
  537. <el-row>
  538. <el-col :span="9">
  539. <el-form-item label="消费次数">
  540. <p style="color: #2fa1ff">{{ user.consumeNum }}</p>
  541. </el-form-item>
  542. </el-col>
  543. <el-col :span="9">
  544. <el-form-item label="所属门店">
  545. <p>{{ user.market }}</p>
  546. </el-form-item>
  547. </el-col>
  548. </el-row>
  549. </el-form>
  550. </el-card>
  551. </div>
  552. <!-- 金币消耗明细的布局------------------------------------------------------- -->
  553. <!-- <div v-else-if="activeTab === 'detail'"> -->
  554. <!-- </div>
  555. </div> -->
  556. </template>
  557. <style scoped>
  558. p {
  559. margin: 0px;
  560. }
  561. .el-form-item {
  562. margin-left: 50px;
  563. }
  564. /* 上传图片的格式 */
  565. .avatar-uploader .avatar {
  566. width: 50px;
  567. height: 50px;
  568. display: block;
  569. }
  570. </style>
  571. <style>
  572. </style>