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.

750 lines
21 KiB

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