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.

695 lines
19 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位
  135. if (addConsume.value.sumGold) {
  136. const sumGoldStr = addConsume.value.sumGold.toString();
  137. if (sumGoldStr.includes('.')) {
  138. const decimalPart = sumGoldStr.split('.')[1];
  139. if (decimalPart.length > 2) {
  140. // 截断到两位小数并提示
  141. const truncatedValue = parseFloat(sumGoldStr.slice(0, sumGoldStr.indexOf('.') + 3));
  142. addConsume.value.sumGold = truncatedValue;
  143. ElMessage.info('最多允许输入两位小数');
  144. }
  145. }
  146. }
  147. // 验证金币总和
  148. const totalAvailableGold = (user.value.nowSumGold)
  149. if (user.value.jwcode && sumGold > totalAvailableGold) {
  150. ElMessage.error("消费金币总数超过可用金币总和");
  151. // 将sumGold设置为null
  152. addConsume.value.sumGold = null;
  153. return false;
  154. }
  155. return true;
  156. }
  157. // 消耗金币计算函数
  158. function calculateCoins(sumGold) {
  159. console.log("消耗金币计算函数:计算金币", sumGold);
  160. const parsedSumGold = parseFloat(sumGold);
  161. if (isNaN(parsedSumGold) || parsedSumGold <= 0 || !user.value.jwcode) {
  162. return {free: 0, permanent: 0, task: 0};
  163. }
  164. const {nowFreeGold, nowPermanentGold, nowTaskGold} = user.value;
  165. let remaining = parsedSumGold;
  166. let freeUsed = 0, permanentUsed = 0, taskUsed = 0;
  167. // 优先消耗免费金币
  168. if (nowFreeGold > 0) {
  169. freeUsed = Math.min(parseFloat(nowFreeGold.toFixed(4)), remaining);
  170. remaining = parseFloat((remaining - freeUsed).toFixed(4));
  171. }
  172. // 其次消耗永久金币
  173. if (remaining > 0 && nowPermanentGold > 0) {
  174. permanentUsed = Math.min(parseFloat(nowPermanentGold.toFixed(4)), remaining);
  175. remaining = parseFloat((remaining - permanentUsed).toFixed(4));
  176. }
  177. // 最后消耗任务金币
  178. if (remaining > 0 && nowTaskGold > 0) {
  179. taskUsed = parseFloat(remaining.toFixed(4));
  180. }
  181. // 更新金币值
  182. addConsume.value.freeGold = freeUsed;
  183. addConsume.value.permanentGold = permanentUsed;
  184. addConsume.value.taskGold = taskUsed;
  185. return {free: freeUsed, permanent: permanentUsed, task: taskUsed};
  186. }
  187. // 这是添加消费信息的接口
  188. const add = async function () {
  189. try {
  190. // 验证输入数据 再验证一次
  191. if (!validateInput()) {
  192. return;
  193. }
  194. // 计算金币使用情况
  195. calculateCoins(addConsume.value.sumGold);
  196. console.log("addConsume.value", addConsume.value)
  197. // 发送POST请求
  198. const result = await request({
  199. // url: "/consume/add",
  200. url: "/consume/add",
  201. data: {
  202. ...addConsume.value,
  203. jwcode: addConsume.value.jwcode,
  204. adminId: addConsume.value.adminId,
  205. sumGold: addConsume.value.sumGold * 100,
  206. freeGold: addConsume.value.freeGold * 100,
  207. taskGold: addConsume.value.taskGold * 100,
  208. permanentGold: addConsume.value.permanentGold * 100,
  209. goodsName: addConsume.value.goodsName,
  210. remark: addConsume.value.remark
  211. }
  212. });
  213. console.log("add请求", result);
  214. // 处理响应
  215. handleResponse(result);
  216. // 重置表单
  217. resetForm();
  218. } catch (error) {
  219. console.error("请求失败", error);
  220. ElMessage.error("添加失败,请检查网络连接或联系管理员");
  221. }
  222. };
  223. // 响应处理函数
  224. function handleResponse(result) {
  225. console.log("响应结果", result)
  226. if (result.code === 200) {
  227. ElMessage.success("添加成功");
  228. console.log("请求成功", result);
  229. } else {
  230. ElMessage.error(result.msg || "添加失败,未知错误");
  231. }
  232. }
  233. // 重置表单函数
  234. function resetForm() {
  235. // 清空表单数据
  236. addConsume.value = {
  237. jwcode: null,
  238. goodsName: "",
  239. sumGold: null,
  240. freeGold: null,
  241. permanentGold: null,
  242. taskGold: null,
  243. remark: "",
  244. adminId: adminData.value.id,
  245. adminName: adminData.value.adminName,
  246. };
  247. console.log("重置表单")
  248. user.value = {
  249. jwcode: null,
  250. name: "",
  251. market: "",
  252. historySumGold: null,
  253. historyPermanentGold: null,
  254. historyFreeGold: null,
  255. historyTaskGold: null,
  256. rechargeNum: null,
  257. consumeNum: null,
  258. firstRecharge: "",
  259. nowPermanentGold: null,
  260. nowFreeJune: null,
  261. nowTaskGold: null,
  262. nowFreeDecember: null,
  263. nowFreeGold: null,
  264. nowSumGold: null
  265. }
  266. }
  267. // 添加前验证
  268. const addBefore = () => {
  269. Ref.value.validate(async (valid) => {
  270. if (valid) {
  271. ElMessageBox.confirm("确认添加?")
  272. .then(() => {
  273. console.log("这里是jwcode", addConsume.value.jwcode)
  274. add();
  275. console.log("添加成功",);
  276. addConsume.value = {};
  277. })
  278. .catch(() => {
  279. console.log("取消添加");
  280. });
  281. } else {
  282. //提示
  283. ElMessage({
  284. type: "error",
  285. message: "请检查输入内容",
  286. });
  287. }
  288. });
  289. };
  290. // 查询客户信息(通过精网号)
  291. const getUser = async function (jwcode) {
  292. trimJwCode();
  293. try {
  294. // 发送POST请求
  295. const result = await request({
  296. // url: "user/selectUser",
  297. url: "/user/selectUser",
  298. data: {
  299. // 只需要传精网号
  300. jwcode: addConsume.value.jwcode,
  301. },
  302. });
  303. console.log("请求成功", result);
  304. if (result.code === 200 && result.data !== null) {
  305. user.value = result.data;
  306. user.value.nowPermanentGold = result.data.nowPermanentGold / 100;
  307. user.value.nowFreeGold = result.data.nowFreeGold / 100;
  308. user.value.nowSumGold = result.data.nowSumGold / 100;
  309. user.value.nowTaskGold = result.data.nowTaskGold / 100;
  310. user.value.nowFreeJune = (result.data.nowFreeJune) / 100;
  311. user.value.nowFreeDecember = (result.data.nowFreeDecember) / 100;
  312. user.value.historySumGold = (result.data.historySumGold) / 100;
  313. user.value.historyPermanentGold = (result.data.historyPermanentGold) / 100;
  314. user.value.historyFreeGold = (result.data.historyFreeGold) / 100;
  315. user.value.historyTaskGold = (result.data.historyTaskGold) / 100;
  316. } else if (result.data == null) {
  317. ElMessage.warning("用户不存在");
  318. // 重置表单
  319. resetForm();
  320. } else if (result.code === 0) {
  321. ElMessage.warning("请检查查询参数")
  322. } else {
  323. console.log("用户信息", user.value);
  324. ElMessage.success(result.msg);
  325. }
  326. } catch (error) {
  327. console.log("请求失败", error);
  328. ElMessage.error("查询失败,请检查精网号是否正确");
  329. // 重置表单
  330. resetForm();
  331. // 在这里可以处理错误逻辑,比如显示错误提示等
  332. }
  333. };
  334. // 获取商品信息(三楼接口)
  335. const getGoods = async function () {
  336. try {
  337. // 发送POST请求
  338. const result = await request({
  339. // url: "/product", //
  340. // url: "http://39.101.133.168:8828/live_mall/api/product/all",
  341. url: "https://api.homilychart.com/live_mall/api/product/all",
  342. });
  343. // 将响应结果存储到响应式数据中
  344. console.log("请求成功", result);
  345. goods.value = result.data.map(item => ({
  346. id: item.id,
  347. label: item.name,
  348. value: item.name
  349. }));
  350. } catch (error) {
  351. console.log("请求失败", error);
  352. // 在这里可以处理错误逻辑,比如显示错误提示等
  353. }
  354. };
  355. /*
  356. ====================监听=================================
  357. */
  358. // 监听消费总金额变化,自动计算三类金币
  359. watch(
  360. () => addConsume.value.sumGold,
  361. (newValue) => {
  362. const parsedNewValue = parseFloat(newValue);
  363. if (!isNaN(parsedNewValue) && parsedNewValue > 0) {
  364. const {free, permanent, task} = calculateCoins(parsedNewValue);
  365. addConsume.value.freeGold = free;
  366. addConsume.value.permanentGold = permanent;
  367. addConsume.value.taskGold = task;
  368. } else {
  369. addConsume.value.freeGold = null;
  370. addConsume.value.permanentGold = null;
  371. addConsume.value.taskGold = null;
  372. }
  373. }
  374. );
  375. /*
  376. ====================挂载=================================
  377. */
  378. // 挂载
  379. onMounted(async function () {
  380. await getAdminData();
  381. await getGoods();
  382. });
  383. </script>
  384. <template>
  385. <div>
  386. <!-- 根据activeTab切换显示内容 -->
  387. <!-- 新增消耗的布局---------------------------------------------------------- -->
  388. <!-- <div v-if="activeTab === 'addConsume'"> -->
  389. <!-- <div style="margin-bottom: 20px; font-weight: bolder">新增消费</div> -->
  390. <el-form
  391. :model="addConsume"
  392. ref="Ref"
  393. :rules="rules"
  394. label-width="auto"
  395. style="max-width: 750px;"
  396. class="form-style"
  397. >
  398. <el-form-item prop="jwcode" label="精网号">
  399. <el-input
  400. v-model="addConsume.jwcode"
  401. style="width: 220px"
  402. @blur="getUser(addConsume.jwcode)"
  403. />
  404. <el-button
  405. type="primary"
  406. @click="getUser(addConsume.jwcode)"
  407. style="margin-left: 20px"
  408. >查询
  409. </el-button
  410. >
  411. </el-form-item>
  412. <div style="display: flex; align-items: center; gap: 20px;">
  413. <el-form-item prop="goodsName" label="商品名称" style="flex: 1; margin-right: 0px">
  414. <el-select
  415. v-model="addConsume.goodsName"
  416. placeholder="请选择商品"
  417. style="width: 450px"
  418. >
  419. <el-option
  420. v-for="item in goods"
  421. :key="item.value"
  422. :label="item.label"
  423. :value="item.value"
  424. />
  425. </el-select>
  426. </el-form-item>
  427. </div>
  428. <el-form-item prop="sumGold" label="消费金币总数">
  429. <el-input
  430. v-model="addConsume.sumGold"
  431. style="width: 100px"
  432. @input="validateInput()"
  433. @change="calculateCoins(addConsume.sumGold)"
  434. />
  435. </el-form-item>
  436. <!-- 三类金币自动计算禁用状态不可编辑 -->
  437. <div style="display: flex; align-items: center">
  438. <el-form-item prop="permanentGold" label="永久金币" style="float: left">
  439. <el-input
  440. v-model="addConsume.permanentGold"
  441. disabled
  442. style="width: 100px; margin-left: -5px"
  443. >
  444. <template #default="scope">{{ scope.row.permanentGold }}</template>
  445. </el-input>
  446. <p style="margin-right: 0px"></p>
  447. </el-form-item>
  448. <el-form-item
  449. prop="freeCoin"
  450. label="免费金币"
  451. style="float: left; margin-left: -20px"
  452. >
  453. <el-input
  454. disabled
  455. v-model="addConsume.freeGold"
  456. style="width: 100px; margin-left: -5px"
  457. />
  458. <p style="margin-right: 0px"></p>
  459. </el-form-item>
  460. <el-form-item prop="taskGold" label="任务金币" style="margin-left: -20px">
  461. <el-input
  462. disabled
  463. v-model="addConsume.taskGold"
  464. style="width: 100px; margin-left: -5px"
  465. />
  466. <p style="margin-right: 20px"></p>
  467. </el-form-item>
  468. </div>
  469. <el-form-item prop="remark" label="备注">
  470. <el-input
  471. v-model="addConsume.remark"
  472. style="width: 300px"
  473. :rows="2"
  474. maxlength="100"
  475. show-word-limit
  476. type="textarea"
  477. />
  478. </el-form-item>
  479. <el-form-item prop="commitName" label="提交人">
  480. <el-input
  481. style="width: 300px"
  482. :value="adminData.adminName"
  483. disabled
  484. placeholder="提交人姓名"
  485. />
  486. </el-form-item>
  487. <el-button type="success" @click="resetForm()" style="margin-left: 280px">重置</el-button>
  488. <el-button type="primary" @click="addBefore"> 提交</el-button>
  489. </el-form>
  490. <!-- 客户信息栏 -->
  491. <el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
  492. <el-form
  493. :model="user"
  494. label-width="auto"
  495. style="max-width: 1000px"
  496. label-position="left"
  497. >
  498. <el-text size="large" style="margin-left: 20px">客户信息</el-text>
  499. <!-- 第一行姓名 + 历史金币 -->
  500. <el-row style="margin-top: 20px">
  501. <el-col :span="9">
  502. <el-form-item label="姓名">
  503. <p>{{ user.name }}</p>
  504. </el-form-item>
  505. </el-col>
  506. <el-col :span="14">
  507. <el-form-item label="历史金币总数">
  508. <p v-if="!isNaN(Number(user.historySumGold))">
  509. {{ Number(user.historySumGold) }}
  510. </p>
  511. </el-form-item>
  512. </el-col>
  513. </el-row>
  514. <!-- 第二行精网号 + 当前金币独立行 -->
  515. <el-row>
  516. <el-col :span="9">
  517. <el-form-item label="精网号">
  518. <p>{{ user.jwcode }}</p>
  519. </el-form-item>
  520. </el-col>
  521. <el-col :span="14">
  522. <el-form-item label="当前金币总数" style="width: 500px">
  523. <span
  524. style="color: #2fa1ff; margin-right: 5px"
  525. v-if="user.nowSumGold !== undefined"
  526. >{{ user.nowSumGold }}</span>
  527. </el-form-item>
  528. <!-- 金币详情独立显示 -->
  529. <el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
  530. <span
  531. style="color: #b1b1b1; margin-left: 0px"
  532. v-if="user.nowPermanentGold !== undefined"
  533. >(永久金币:{{ user.nowPermanentGold }};
  534. 免费金币:{{ user.nowFreeGold }};
  535. 任务金币:{{ user.nowTaskGold }})</span>
  536. </el-form-item>
  537. </el-col>
  538. </el-row>
  539. <!-- 第三行首次充值日期 + 充值次数 -->
  540. <el-row style="margin-top:-23px">
  541. <el-col :span="9">
  542. <el-form-item label="首次充值日期">
  543. <p v-if="user.firstRecharge">
  544. {{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
  545. </p>
  546. </el-form-item>
  547. </el-col>
  548. <el-col :span="14">
  549. <el-form-item label="充值次数">
  550. <p style="color: #2fa1ff">{{ user.rechargeNum }}</p>
  551. </el-form-item>
  552. </el-col>
  553. </el-row>
  554. <!-- 第四行消费次数 + 所属门店 -->
  555. <el-row>
  556. <el-col :span="9">
  557. <el-form-item label="消费次数">
  558. <p style="color: #2fa1ff">{{ user.consumeNum }}</p>
  559. </el-form-item>
  560. </el-col>
  561. <el-col :span="9">
  562. <el-form-item label="所属门店">
  563. <p>{{ user.market }}</p>
  564. </el-form-item>
  565. </el-col>
  566. </el-row>
  567. </el-form>
  568. </el-card>
  569. </div>
  570. <!-- 金币消耗明细的布局------------------------------------------------------- -->
  571. <!-- <div v-else-if="activeTab === 'detail'"> -->
  572. <!-- </div>
  573. </div> -->
  574. </template>
  575. <style scoped>
  576. p {
  577. margin: 0px;
  578. }
  579. .el-form-item {
  580. margin-left: 50px;
  581. }
  582. /* 上传图片的格式 */
  583. .avatar-uploader .avatar {
  584. width: 50px;
  585. height: 50px;
  586. display: block;
  587. }
  588. </style>
  589. <style>
  590. .avatar-uploader .el-upload {
  591. border: 1px dashed var(--el-border-color);
  592. border-radius: 6px;
  593. cursor: pointer;
  594. position: relative;
  595. overflow: hidden;
  596. transition: var(--el-transition-duration-fast);
  597. }
  598. .avatar-uploader .el-upload:hover {
  599. border-color: var(--el-color-primary);
  600. }
  601. .el-icon.avatar-uploader-icon {
  602. font-size: 28px;
  603. color: #8c939d;
  604. width: 50px;
  605. height: 50px;
  606. text-align: center;
  607. }
  608. .form-style {
  609. margin-top: 50px;
  610. max-width: 50%;
  611. float: left;
  612. }
  613. .form-style2 {
  614. max-width: 60%;
  615. }
  616. p {
  617. font-size: 13px;
  618. transform: scale(1);
  619. }
  620. </style>