68 Commits

Author SHA1 Message Date
zhangrenyuan 92a4b1beb0 Merge branch 'zhangrenyuan/feature-20250817091555-金币优化' into milestone-20250815-金币优化 4 weeks ago
zhangrenyuan e0eb9adf63 fix样式调整 4 weeks ago
lihuilin 328cc1bf45 提交人 4 weeks ago
lihuilin 2fffbf2351 默认全部退款 4 weeks ago
lihuilin 8dc4cfc111 Merge branch 'lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化 4 weeks ago
lihuilin 8b7fa3c507 表单 4 weeks ago
ZhangYong 7cb3ab0d30 金币退款 4 weeks ago
lihuilin 4a7c965e21 Merge branch 'lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化 4 weeks ago
lihuilin 3bd7e66db4 充值消费0110 4 weeks ago
lihui 2336c4a3c1 Merge remote-tracking branch 'origin/milestone-20250815-金币优化' into milestone-20250815-金币优化 4 weeks ago
lihuilin 9ad35b914b 订单状态 4 weeks ago
lihui 567a349569 金币充值明细 4 weeks ago
lihuilin 0c835a0259 Merge branch 'lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化 1 month ago
lihuilin 704e748d55 新增退款的表单 1 month ago
lihuilin 2c19a32d0f 等于等于等于 1 month ago
lihuilin 991ffda085 等于等于等于? 1 month ago
lihui da6f57dbfd feat:跳转不要啦 1 month ago
lihuilin 6978acbab5 选类型调商品 1 month ago
lihuilin 94a35fbe49 新增退款 1 month ago
zhangrenyuan f077870a9a Merge branch 'milestone-20250815-金币优化' of http://39.101.133.168:8807/huangqizhen/gold-vue into milestone-20250815-金币优化 1 month ago
zhangrenyuan 25baeb30f9 fix form and page栏 重叠bug 1 month ago
ZhangYong 46db912d85 退款审核退款类型 1 month ago
lihui 49a59b31fc Merge branch 'refs/heads/lihui/feature-20250815155204-金币优化' into milestone-20250815-金币优化 1 month ago
lihui 62f10ae4d1 feat:备注必填 1 month ago
ZhangYong 9ce97b1fcb Merge branch 'zhangyong/feature-20250815160302-金币优化' into milestone-20250815-金币优化 1 month ago
ZhangYong 07b8d81311 退款明细 1 month ago
lihui 8b18f60d10 feat:备注必填 1 month ago
ZhangYong 2259f9fa83 Merge branch 'zhangyong/feature-20250815160302-金币优化' into milestone-20250815-金币优化 1 month ago
ZhangYong bde42ecde7 1 1 month ago
lihui 7ffb8bba59 Merge remote-tracking branch 'origin/milestone-20250815-金币优化' into milestone-20250815-金币优化 1 month ago
lihui e9be442639 feat:新需求,金币退款 1 month ago
ZhangYong 4c4e4b9f87 Merge branch 'zhangyong/feature-20250815160302-金币优化' into milestone-20250815-金币优化 1 month ago
ZhangYong d248184c6f 金币客户明细排序 1 month ago
lihui fef8f95557 Merge remote-tracking branch 'origin/milestone-20250815-金币优化' into milestone-20250815-金币优化 1 month ago
lhl 2b288ec2ad Merge branch 'lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化 1 month ago
lihui 3ccb108d9a fix:两个驳回 1 month ago
lhl f6bcb51eac 专业改样式八百年 1 month ago
ZhangYong 3a8be6a3d6 金豆文章样式 1 month ago
ZhangYong f0775eda93 fix:金豆充值统计总数 1 month ago
ZhangYong 17f7232d90 导出 1 month ago
lihui d00292b7c1 Merge branch 'refs/heads/lihui/feature-20250815155204-金币优化' into milestone-20250815-金币优化 1 month ago
lihui cd045df378 feat:字体 1 month ago
lhl 6d64e584d7 样式 1 month ago
ZhangYong 482f116e2b 文章 1 month ago
ZhangYong 4e4441f0b5 文章 1 month ago
ZhangYong f82d1358e5 金豆消耗文章 1 month ago
ZhangYong 3cc952e837 金豆文章 1 month ago
lihui 48502b088f fix 多余第二次 1 month ago
lhl 66d27ec7b2 样式 1 month ago
lhl dd58c2f690 样式 1 month ago
lhl 8f9e64c5de 好多样式 1 month ago
ZhangYong befc4c3a97 金豆样式 1 month ago
lhl 7b80b360ad 工作台样式 1 month ago
lhl f176dfe431 Merge branch 'lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化 1 month ago
lhl 509ba436d8 工作台样式 1 month ago
zhangrenyuan de733fcccf Merge branch 'zhangrenyuan/feature-20250817091555-金币优化' into milestone-20250815-金币优化 2 months ago
zhangrenyuan 97d4f69835 fix 充值提交和传rateId 2 months ago
ZhangYong e04b707085 金币明细 2 months ago
ZhangYong fe85fa64a0 金币明细界面样式 2 months ago
lhl 3ce64b2d89 权限修改ack 2 months ago
lihui ee30771c25 Merge branch 'refs/heads/milestone-20250815-金币优化' into lihui/feature-20250815155204-金币优化 2 months ago
lihui 5e19fffcbe fix 商品模糊 2 months ago
zhangrenyuan 29a6085975 Merge branch 'zhangrenyuan/feature-20250817091555-金币优化' into milestone-20250815-金币优化 2 months ago
zhangrenyuan e744965c99 fix currency 2 months ago
zhangrenyuan a850fe5335 Merge branch 'zhangrenyuan/feature-20250817091555-金币优化' into milestone-20250815-金币优化 2 months ago
zhangrenyuan ce5d3130e9 feat currency 2 months ago
lihui 754ffc2be3 fix 消耗 2 months ago
lihui a26fb62511 fix 退款金币总数 充值金额 2 months ago
  1. 2
      .env.development
  2. 3
      src/global.css
  3. 1
      src/main.ts
  4. 6
      src/router/index.js
  5. 2
      src/util/request.js
  6. 3
      src/utils/menuTreePermission.js
  7. 14
      src/views/audit/bean/beanAudit.vue
  8. 2
      src/views/audit/gold/audit.vue
  9. 4
      src/views/audit/gold/rechargeAudit.vue
  10. 14
      src/views/audit/gold/refundAudit.vue
  11. 88
      src/views/consume/bean/addBeanConsume.vue
  12. 172
      src/views/consume/bean/articleVideo.vue
  13. 9
      src/views/consume/bean/beanConsume.vue
  14. 4
      src/views/consume/bean/dieHardFan.vue
  15. 144
      src/views/consume/bean/liveStream.vue
  16. 6
      src/views/consume/gold/addCoinConsume.vue
  17. 6
      src/views/consume/gold/coinConsumeDetail.vue
  18. 2
      src/views/history/history.vue
  19. 145
      src/views/home.vue
  20. 2
      src/views/managerecharge/rate.vue
  21. 44
      src/views/permissions/permissions.vue
  22. 2
      src/views/permissions/rolePermission.vue
  23. 387
      src/views/permissions/userPermission.vue
  24. 2
      src/views/recharge/bean/addBeanRecharge.vue
  25. 15
      src/views/recharge/bean/beanOnlineRecharge.vue
  26. 34
      src/views/recharge/bean/beanSystemRecharge.vue
  27. 76
      src/views/recharge/gold/addCoinRecharge.vue
  28. 7
      src/views/recharge/gold/coinRechargeDetail.vue
  29. 575
      src/views/refund/gold/addCoinRefund.vue
  30. 726
      src/views/refund/gold/addNewCoinRefund.vue
  31. 26
      src/views/refund/gold/coinRefund.vue
  32. 232
      src/views/refund/gold/coinRefundDetail.vue
  33. 9
      src/views/usergold/bean/userbean.vue
  34. 12
      src/views/usergold/gold/clientCount.vue
  35. 16
      src/views/usergold/gold/clientCountBalance.vue
  36. 10
      src/views/usergold/gold/clientCountDetail.vue
  37. 80
      src/views/workspace/index.vue
  38. 2
      stats.html

2
.env.development

@ -13,4 +13,4 @@ VITE_UPLOAD_URL=http://39.101.133.168:8828/hljw/api/aws/upload
# 本地
#VITE_API_BASE='http://localhost:8081/'
# sunjiabei
# VITE_API_BASE='http://192.168.0.115:8081/'
# VITE_API_BASE='http://192.168.0.34:8081/'

3
src/global.css

@ -0,0 +1,3 @@
body {
font-family: 'Microsoft YaHei UI';
}

1
src/main.ts

@ -15,6 +15,7 @@ import 'vxe-table/lib/style.css'
// 修正导入路径
import { useAdminStore } from './store'
import request from "@/util/request";
import "./global.css";
const app = createApp(App)
const pinia = createPinia()

6
src/router/index.js

@ -206,6 +206,12 @@ const routes = [
name: "coinRefundDetail",
component: () => import("../views/refund/gold/coinRefundDetail.vue"),
meta: {permissionId: 22} // 对应"查看金币退款明细"id=22
},
{
path: 'addRefund',
name: "addNewCoinRefund",
component: () => import("../views/refund/gold/addNewCoinRefund.vue"),
meta: {permissionId: 58}
}
]
},

2
src/util/request.js

@ -2,7 +2,7 @@ import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE,//.env.development
timeout: 10000,
timeout: 1000000,
headers: {
'Content-Type': 'application/json'
},

3
src/utils/menuTreePermission.js

@ -46,6 +46,9 @@ export const permissionMapping = {
Submit_Gold_Coin_Refund: 21, // 提交金币退款 // coinRefund页面
View_Gold_Coin_Refund_Details: 22, // 查看金币退款明细 // coinRefund页面
Submit_Gold_Coin_RefundS: 58, // 提交金币退款 // coinRefund页面
View_Gold_Coin_Details: 23, // 查看金币明细 //usergold页面
View_Gold_Coin_Balance: 24, // 查看金币余额 //usergold页面

14
src/views/audit/bean/beanAudit.vue

@ -1,10 +1,10 @@
<template>
<el-card style="margin-bottom: 0.5vh;width:82.8vw">
<el-card style="margin-bottom: 0.5vh;width:82vw">
<div style="margin-bottom: 1vh">
<el-text size="large">精网号</el-text>
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw" clearable />
<el-text class="mx-1" size="large">所属地区</el-text>
<el-text size="large">所属地区</el-text>
<el-cascader v-model="selectedMarkets" :options="marketOptions" placeholder="请选择所属地区" clearable
style="width: 12vw" @change="handleMarketChange" />
</div>
@ -206,9 +206,13 @@ const get = async function () {
searchForm.value.createStartTime = ''
searchForm.value.createEndTime = ''
}
// if (searchForm.value.market === '' || searchForm.value.market === '') {
// searchForm.value.market = '';
// }
if(checkTab.value === 'pending'){
sortField.value = 'createTime'
sortOrder.value = 'desc'
}else{
sortField.value = 'auditTime'
sortOrder.value = 'desc'
}
const params = {
pageNum: pagination.value.pageNum,//
pageSize: pagination.value.pageSize,//

2
src/views/audit/gold/audit.vue

@ -1,6 +1,6 @@
<template>
<div>
<div style="height:2vw;">
<div style="height:4vh;width:82vw;">
<el-button-group>
<el-button :type="activeTab === 'rechargeAudit' ? 'primary' : 'default'" @click="navigateTo('rechargeAudit')"
:disabled="!hasRecharge">

4
src/views/audit/gold/rechargeAudit.vue

@ -47,7 +47,7 @@
免费金币{{ format3(stats.freeGolds.toFixed(2)) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
</div>
</el-tabs>
<el-table :data="tableData" style="width: 100vw;height:55vh" @sort-change="handleSortChange"
<el-table :data="tableData" style="width: 82vw;height:54vh" @sort-change="handleSortChange"
:row-style="{ height: '50px' }">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
@ -60,7 +60,7 @@
<el-table-column prop="activity" label="活动名称" width="100px" show-overflow-tooltip/>
<el-table-column prop="rateName" label="货币名称" width="110px"/>
<el-table-column prop="money" label="充值金额" sortable="custom" width="110px">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
<template #default="scope">{{ scope.row.money / 100 }}</template>
</el-table-column>
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>

14
src/views/audit/gold/refundAudit.vue

@ -1,5 +1,5 @@
<template>
<el-card style="margin-bottom: 0.5vh;width:82.8vw">
<el-card style="margin-bottom: 0.5vh;">
<el-col style="margin-bottom: 0.5vh">
<el-text size="large">精网号</el-text>
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw" clearable/>
@ -10,10 +10,10 @@
<el-option v-for="item in refundGoodsOptions" :key="item" :label="item" :value="item"></el-option>
</el-select>
<el-text size="large">退款方式</el-text>
<el-select v-model="searchForm.refundModel" placeholder="请选择" style="width: 12vw;margin-right:1vw" clearable>
<el-option label="全部退款" value="0"/>
<el-option label="部分退款" value="1"/>
<el-text size="large">退款类型</el-text>
<el-select v-model="searchForm.refundType" placeholder="请选择" style="width: 12vw;margin-right:1vw" clearable>
<el-option label="商品退款" value="商品退款"/>
<el-option label="金币退款" value="金币退款"/>
</el-select>
<el-text size="large">所属地区</el-text>
@ -50,7 +50,7 @@
任务金币{{ format3(stats.taskGolds.toFixed(2)) }}金币
</div>
<el-table :data="tableData" style="height:55vh" @sort-change="handleSortChange">
<el-table :data="tableData" style="height:54vh;width:82vw" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="60"/>
<el-table-column prop="name" label="姓名" width="120" show-overflow-tooltip/>
<el-table-column prop="jwcode" label="精网号" width="120"/>
@ -64,7 +64,7 @@
</template>
</el-table-column>
<el-table-column prop="goodsName" label="退款商品" width="120" show-overflow-tooltip/>
<el-table-column prop="sumGold" label="退款金额" width="120" sortable="custom">
<el-table-column prop="sumGold" label="退款金币总数" width="160" sortable="custom">
<template #default="{ row }">
{{ row.sumGold / 100 }}
</template>

88
src/views/consume/bean/addBeanConsume.vue

@ -223,9 +223,6 @@ const throttledHandleConsumeFormt = _.throttle(handleConsumeForm, 5000, {
<el-input v-model="consumeForm.remark" style="width: 300px" :rows="5" maxlength="100" show-word-limit
type="textarea" />
</el-form-item>
<!-- <el-form-item prop="adminName" label="提交人">
<el-input style="width: 300px" :value="adminData.adminName" disabled placeholder="提交人姓名" />
</el-form-item> -->
<el-button @click="deleteConsumeForm" style="margin-left: 280px" type="success">重置</el-button>
<el-button type="primary" :disabled="addDisabled" @click="handleConsumeForm"> 提交 </el-button>
</el-form>
@ -233,64 +230,43 @@ const throttledHandleConsumeFormt = _.throttle(handleConsumeForm, 5000, {
<div class="right">
<!-- 客户信息栏 -->
<el-card v-if="user.jwcode" class="customer-info">
<el-form :model="user" label-width="auto" label-position="left">
<el-text size="large" style="min-width: 600px;">客户信息</el-text>
<el-form :model="user" label-width="auto" label-position="left">
<el-text size="large" style="margin-left: 7vw">客户信息</el-text>
<!-- 第一行姓名 + 当前付费金豆 -->
<div style="margin-top: 0.5vw;display:flex;">
<p style="width:6vw;">姓名:</p>
<p style="color: #2fa1ff;width:6vw;">{{ user.name }}</p>
<p style="width:7vw;">当前付费金豆:</p>
<p v-if="!isNaN(Number(user.permanentBean))" style="color: #2fa1ff;">{{ Number(user.permanentBean) }}</p>
<!-- 如果不是有效的数字显示默认值 -->
<p v-else></p>
</div>
<!-- 第一行姓名 + 当前付费金豆 -->
<el-row style="margin-top: 20px">
<el-col :span="9">
<el-form-item label="姓名:">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.name }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前付费金豆:">
<p style="color: #2fa1ff; margin-right: 5px" v-if="!isNaN(Number(user.permanentBean))">
{{ Number(user.permanentBean) }}
</p>
<!-- 如果不是有效的数字显示默认值 -->
<p v-else></p>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行精网号 + 免费金豆 -->
<div style="display:flex;margin-top: 2vw;">
<p style="width:6vw;">精网号:</p>
<p style="color: #2fa1ff;width:6vw;">{{ user.jwcode }}</p>
<p style="width:7vw;">当前免费金豆:</p>
<p v-if="user.freeBean !== undefined" style="color: #2fa1ff;">{{ user.freeBean }}</p>
</div>
<!-- 第二行精网号 + 免费金豆 -->
<el-row>
<el-col :span="9">
<el-form-item label="精网号">
<p style="color: #2fa1ff; margin-right: 5px">{{ user.jwcode }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前免费金豆:">
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.freeBean !== undefined">{{ user.freeBean }}
</span>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行消费次数 + 所属门店 -->
<el-row>
<el-col :span="9">
<el-form-item label="所属门店">
<p style="color: #2fa1ff">{{ user.market }}</p>
</el-form-item>
</el-col>
<el-col :span="9">
<el-form-item label="消耗金豆总数">
<p style="color: #2fa1ff; margin-right: 5px" v-if="user.consumeSum != null">{{ user.consumeSum }}</p>
<p style="color: #2fa1ff; margin-right: 5px" v-else>{{ 0 }}</p>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<!-- 第三行消费次数 + 所属门店 -->
<div style="display:flex;margin-top: 2vw;">
<p style="width:6vw;">所属门店:</p>
<p style="color: #2fa1ff;width:6vw;">{{ user.market }}</p>
<p style="width:7vw;">消耗金豆总数:</p>
<p style="color: #2fa1ff;" v-if="user.consumeSum != null">{{ user.consumeSum }}</p>
<p style="color: #2fa1ff;" v-else>{{ 0 }}</p>
</div>
</el-form>
</el-card>
</div>
</div>
</template>
<style scoped lang="scss">
.userAndform {
width: 80vw;
height: 85vh;
height: 80vh;
display: flex;
justify-content: center;
@ -306,12 +282,14 @@ const throttledHandleConsumeFormt = _.throttle(handleConsumeForm, 5000, {
.right {
flex: 1;
display: flex;
margin-left: 10%;
.customer-info {
width: 35vw;
height:28vh;
float: right;
height: 28vh;
margin-top: 5vh;
display: flex;
justify-content: center;
}
}
}

172
src/views/consume/bean/articleVideo.vue

@ -463,99 +463,109 @@ const getTagText = (state) => {
</script>
<template>
<el-card style="margin-bottom: 10px;margin-top:10px">
<el-col style="margin-bottom: 10px">
<el-card style="margin-bottom: 1vh">
<el-col style="margin-bottom: 1vh; ">
<div style="display:flex">
<div style="width: 13vw; display:flex; align-items:center; justify-content: center;">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeArticle.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px" clearable />
<el-input v-model="beanConsumeArticle.jwcode" style="width:8vw;margin-left: 0.5vw;" placeholder="请输入精网号" clearable />
</div>
<div style="width: 13vw; display:flex; align-items:center; justify-content: center;">
<el-text>地区</el-text>
<el-select v-model="beanConsumeArticle.dept" placeholder="请选择地区" style="width: 200px;margin-right: 20px" clearable>
<el-select v-model="beanConsumeArticle.dept" style="width:8vw; margin-left: 0.5vw;" placeholder="请选择地区" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
</div>
<div style="width: 13vw; display:flex;align-items:center; justify-content: center;">
<el-text>类型</el-text>
<el-select v-model="beanConsumeArticle.payMode" placeholder="请选择类型" style="width: 200px;margin-right: 20px" clearable
@change="handlePayModeChange">
<el-select v-model="beanConsumeArticle.payMode" style="width:8vw; margin-left: 0.5vw;" placeholder="请选择类型" clearable @change="handlePayModeChange">
<el-option label="打赏" value="0" />
<el-option label="付费购买" value="1" />
<el-option label="其他" value="2" />
</el-select>
<el-text>文章/视频ID</el-text>
<el-input v-model="beanConsumeArticle.articleId" placeholder="请输入文章/视频ID" style="width: 200px;margin-right: 20px" clearable />
<el-text>文章/视频标题</el-text>
<el-input v-model="beanConsumeArticle.articleName" placeholder="请输入文章/视频标题" style="width: 200px" clearable />
</el-col>
<el-col>
<el-text>作者</el-text>
<el-input v-model="beanConsumeArticle.author" placeholder="请输入作者" style="width: 200px;margin-right: 20px" clearable />
<el-text>付费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px;margin-right: 10px" @change="handleDatePickerChange"
value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</el-col>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" show-overflow-tooltip />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="type" label="类型" width="120px">
<template #default="scope">
{{
Array.isArray(consumeTypes)
? consumeTypes.find(item => item.value === Number(scope.row.type))?.label || '未知类型'
: '未知类型'
}}
</template>
</el-table-column>
<el-table-column prop="beanNum" label="金豆总数" sortable="custom" width="120px" />
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="articleId" label="文章/视频ID" width="150px" />
<el-table-column prop="articleName" label="文章/视频标题" width="150px" show-overflow-tooltip />
<el-table-column prop="author" label="作者" width="120px" show-overflow-tooltip />
<el-table-column prop="consumeTime" label="付费时间" sortable="custom" width="180px">
<template #default="scope">
{{ formatTime(scope.row.consumeTime) }}
</template>
</el-table-column>
</el-table>
<div style="width: 15vw; display:flex; align-items:center; justify-content: center;">
<el-text>文章/视频ID:</el-text>
<el-input v-model="beanConsumeArticle.articleId" style="width:8vw; margin-left: 0.5vw;" placeholder="请输入文章/视频ID" clearable />
</div>
<!-- 分页 -->
<div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
<div style="width: 16vw; display:flex; align-items:center; justify-content: center;">
<el-text>文章/视频标题</el-text>
<el-input v-model="beanConsumeArticle.articleName" style="width:9vw; margin-left: 0.5vw;" placeholder="请输入文章/视频标题" clearable />
</div>
<div style="width: 12vw; display:flex; align-items:center; justify-content: center;">
<el-text>作者</el-text>
<el-input v-model="beanConsumeArticle.author"style="width:8vw; margin-left: 0.5vw;" placeholder="请输入作者" clearable />
</div>
</el-card>
</div>
</el-col>
<el-col>
<div>
<el-text>付费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 20vw;margin-right: 10px" @change="handleDatePickerChange"
value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"></el-button>
<el-button @click="getYesterday()" :type="activeTimeRange === 'yesterday' ? 'primary' : ''"></el-button>
<el-button @click="get7Days()" :type="activeTimeRange === '7days' ? 'primary' : ''">近7天</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</div>
</el-col>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 82vw" height="57vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" show-overflow-tooltip />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="type" label="类型" width="120px">
<template #default="scope">
{{
Array.isArray(consumeTypes)
? consumeTypes.find(item => item.value === Number(scope.row.type))?.label || '未知类型'
: '未知类型'
}}
</template>
</el-table-column>
<el-table-column prop="beanNum" label="金豆总数" sortable="custom" width="120px" />
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="articleId" label="文章/视频ID" width="150px" />
<el-table-column prop="articleName" label="文章/视频标题" width="150px" show-overflow-tooltip />
<el-table-column prop="author" label="作者" width="120px" show-overflow-tooltip />
<el-table-column prop="consumeTime" label="付费时间" sortable="custom" width="180px">
<template #default="scope">
{{ formatTime(scope.row.consumeTime) }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">

9
src/views/consume/bean/beanConsume.vue

@ -1,5 +1,5 @@
<template>
<div>
<div class="father">
<el-button-group>
<el-button
:type="activeTab === 'addBeanConsume' ? 'primary' : 'default'"
@ -111,8 +111,11 @@ onMounted(() => {
});
</script>
<style scoped>
.father{
width:82vw;
height:4vh;
}
.content{
width: 90%;
height: 90%;
flex:1;
}
</style>

4
src/views/consume/bean/dieHardFan.vue

@ -468,7 +468,7 @@ const getTagText = (state) => {
</script>
<template>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-card style="margin-bottom: 1vh;">
<el-col style="margin-bottom: 10px">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeFan.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px"
@ -506,7 +506,7 @@ const getTagText = (state) => {
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table :data="tableData" style="width: 82vw" height="57vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{

144
src/views/consume/bean/liveStream.vue

@ -502,37 +502,42 @@ const getTagText = (state) => {
</script>
<template>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-col style="margin-bottom: 10px">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeLive.jwcode" placeholder="请输入精网号" style="width: 200px;margin-right: 20px"
clearable />
<el-text>地区</el-text>
<el-select v-model="beanConsumeLive.dept" placeholder="请选择地区" style="width: 200px;margin-right: 20px" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
<el-text>礼物名称</el-text>
<el-select v-model="beanConsumeLive.gift" placeholder="请选择礼物名称" style="width: 200px;margin-right: 20px" clearable
filterable allow-create default-first-option>
<el-option v-for="(item, index) in gifts" :key="index" :label="item" :value="item" />
</el-select>
<el-text>频道</el-text>
<el-select v-model="beanConsumeLive.liveChannel" placeholder="请选择频道" style="width: 200px;margin-right: 20px"
clearable filterable allow-create default-first-option>
<el-option v-for="(item, index) in channels" :key="index" :label="item" :value="item" />
</el-select>
<el-text>直播间</el-text>
<el-input v-model="beanConsumeLive.liveName" placeholder="请输入直播间" style="width: 200px;margin-right: 20px"
clearable />
</el-col>
<el-col>
<el-card style="margin-bottom: 1vh;">
<div style="margin-bottom: 1vh;display: flex;">
<div style="width: 18vw;margin-right: 1vw;display: flex;align-items: center;">
<el-text>精网号</el-text>
<el-input v-model="beanConsumeLive.jwcode" style="width:10vw;" placeholder="请输入精网号" clearable />
</div>
<div style="margin-bottom: 1vh;width:18vw;display: flex;align-items: center;">
<el-text>地区</el-text>
<el-select v-model="beanConsumeLive.dept" placeholder="请选择地区" style="width:10vw" clearable>
<el-option v-for="(item, index) in dept" :key="index" :label="item" :value="item" />
</el-select>
</div>
<div style="margin-bottom: 1vh;width:18vw;display: flex;align-items: center;">
<el-text>礼物名称</el-text>
<el-select v-model="beanConsumeLive.gift" placeholder="请选择礼物名称" style="width:10vw"
clearable filterable allow-create default-first-option>
<el-option v-for="(item, index) in gifts" :key="index" :label="item" :value="item" />
</el-select>
</div>
<div style="margin-bottom: 1vh;width:18vw;display: flex;align-items: center;">
<el-text>频道</el-text>
<el-select v-model="beanConsumeLive.liveChannel" placeholder="请选择频道" style="width:10vw"
clearable filterable allow-create default-first-option>
<el-option v-for="(item, index) in channels" :key="index" :label="item" :value="item" />
</el-select>
</div>
<div style="margin-bottom: 1vh;width:18vw;display: flex;align-items: center;">
<el-text>直播间</el-text>
<el-input v-model="beanConsumeLive.liveName" placeholder="请输入直播间" style="width:10vw"
clearable />
</div>
</div>
<div>
<el-text>消费时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px;margin-right: 20px;" @change="handleDatePickerChange"
end-placeholder="结束时间" style="width: 20vw;margin-right: 1vw;" @change="handleDatePickerChange"
:default-time="defaultTime" />
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''"> </el-button>
@ -543,48 +548,47 @@ const getTagText = (state) => {
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel()">导出excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</el-col>
</div>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 82vw" height="56vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" show-overflow-tooltip />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="gift" label="礼物" width="140px">
</el-table-column>
<el-table-column prop="beanNum" label="金豆数量" sortable="custom" width="120px" />
<el-table-column prop="isBackpack" label="背包礼物" width="120px">
<template #default="scope">
{{ scope.row.isBackpack == 1 ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="liveChannel" label="频道" width="120px" show-overflow-tooltip />
<el-table-column prop="liveName" label="直播间名称" width="160px" show-overflow-tooltip />
<el-table-column prop="consumeTime" label="消费时间" sortable="custom" width="180px" />
</el-table>
</div>
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" style="margin-top: 20px;"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</el-card>
<el-card>
<div>
金豆总数{{ format3(Math.abs(permanentBean + freeBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
付费金豆数{{ format3(Math.abs(permanentBean)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金豆数{{ format3(Math.abs(freeBean)) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="overflow-y: auto">
<el-table :data="tableData" style="width: 100%" height="550px" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<!-- 固定姓名列 -->
<el-table-column prop="name" label="姓名" width="150px" fixed="left" show-overflow-tooltip />
<!-- 固定精网号列 -->
<el-table-column prop="jwcode" label="精网号" width="110px" fixed="left" />
<el-table-column prop="dept" label="地区" width="110px" />
<el-table-column prop="gift" label="礼物" width="140px">
</el-table-column>
<el-table-column prop="beanNum" label="金豆数量" sortable="custom" width="120px" />
<el-table-column prop="isBackpack" label="背包礼物" width="120px">
<template #default="scope">
{{ scope.row.isBackpack == 1 ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="buyBean" label="付费金豆数" sortable="custom" width="120px" />
<el-table-column prop="freeBean" label="免费金豆数" sortable="custom" width="120px" />
<el-table-column prop="liveChannel" label="频道" width="120px" show-overflow-tooltip />
<el-table-column prop="liveName" label="直播间名称" width="160px" show-overflow-tooltip />
<el-table-column prop="consumeTime" label="消费时间" sortable="custom" width="180px" />
</el-table>
</div>
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]" style="margin-top: 20px;"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">

6
src/views/consume/gold/addCoinConsume.vue

@ -707,7 +707,7 @@ onMounted(async function () {
</el-dialog>
<el-dialog v-model="ConsumeDialogVisible" title="第二次操作确认" :before-close="ConsumeDialogVisiblehandleClose"
<el-dialog v-model="ConsumeDialogVisible" title="操作确认" :before-close="ConsumeDialogVisiblehandleClose"
:close-on-click-modal="false" width="480px">
<!-- 内容整体居中且收窄 -->
<div class="confirm-body">
@ -832,8 +832,8 @@ p {
}
.father1 {
width: 1000px;
height: 100%;
width: 82vw;
height: 80vh;
display: flex;
.left {

6
src/views/consume/gold/coinConsumeDetail.vue

@ -613,8 +613,8 @@ const getMarket = async function () {
免费金币{{ format3(Math.abs(freeGolds)) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(Math.abs(taskGolds)) }}
</div>
<div style="height: 55vh;">
<el-table :data="tableData" style="height: 95%" @sort-change="handleSortChange">
<div style="height: 58vh;">
<el-table :data="tableData" style="height: 57vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
@ -663,7 +663,7 @@ const getMarket = async function () {
<el-table-column prop="isRefund" label="订单状态" width="200px" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.isRefund == 0">正常</span>
<span v-else-if="scope.row.isRefund == 1">已退</span>
<span v-else-if="scope.row.isRefund == 1">已退</span>
<span v-else>未知状态</span>
</template>
</el-table-column>

2
src/views/history/history.vue

@ -22,7 +22,7 @@
</el-card>
<el-card style="margin-top:10px" v-show="tableData.length > 0">
<el-table :data="tableData" style="width: 100%" height="600">
<el-table :data="tableData" style="width: 82vw;height:60vh">
<el-table-column type="index" label="序号" width="100" header-align="center" align="center">
<template #default="scope">
{{ scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize }}

145
src/views/home.vue

@ -1,13 +1,13 @@
<script setup>
//
import {computed, ref} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {ElMessage} from 'element-plus'
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import dmmn from '../assets/link.png'
import ChangePassword from '@/components/changePassword.vue'
import {useAdminStore} from '@/store'
import {storeToRefs} from 'pinia'
import {filterMenu, getRoutePath} from "@/utils/menuUtils.js";
import { useAdminStore } from '@/store'
import { storeToRefs } from 'pinia'
import { filterMenu, getRoutePath } from "@/utils/menuUtils.js";
//
const menuList = ref([])
@ -15,7 +15,7 @@ const menuList = ref([])
//
const adminStore = useAdminStore()
// adminData menuTree
const {adminData, menuTree} = storeToRefs(adminStore)
const { adminData, menuTree } = storeToRefs(adminStore)
// ,menuTree
menuList.value = filterMenu(menuTree.value)
@ -76,7 +76,7 @@ const openChangePassword = () => {
showPasswordDialog.value = true
}
//
function onPwdDialogClosed () {
function onPwdDialogClosed() {
// resetFields
pwdRef.value?.resetFields()
}
@ -93,57 +93,32 @@ function logout() {
</script>
<template>
<div class="common-layout">
<div>
<el-container>
<el-aside style="
width: 15%;
max-width: 20%;
position: fixed; /* 固定位置 */
top: 0;
left: 0;
height: 100vh; /* 高度占满视口 */
z-index: 100; /* 确保侧边栏在其他元素之上 */
">
<el-aside >
<div class="logo">
<img src="../assets/新logo.png" alt="logo" style="width: 80px; height: 80px"/>
<!-- <div style="font-size: 16px; font-weight: bold; color: black; text-align: center;" ><h1>海外金币管理系统</h1></div> -->
<img src="../assets/新logo.png" alt="logo" style="width: 9vh; height: 9vh" />
</div>
<el-card style="min-height: 90%;">
<el-menu
:router="true"
class="el-menu-vertical-demo"
:default-active="activeMenu"
>
<el-menu :router="true" :default-active="activeMenu" style="min-height: 80vh;border:none;">
<!-- 递归渲染菜单层级 -->
<template v-for="menu in menuList" :key="menu.id">
<!-- 有子菜单的父级菜单menuType=2 且存在children -->
<el-sub-menu
v-if="menu.children && menu.children.length > 0"
:index="menu.id.toString()"
>
<el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.id.toString()">
<template #title>
<el-icon>
<Folder/>
<Folder />
</el-icon>
<span>{{ menu.menuName }}</span>
</template>
<!-- 子菜单 -->
<template v-for="child in menu.children" :key="child.id">
<!-- 子菜单为叶子节点无children -->
<el-menu-item
v-if="!child.children || child.children.length === 0"
:index="getRoutePath(child)"
>
<el-menu-item v-if="!child.children || child.children.length === 0" :index="getRoutePath(child)">
<span>{{ child.menuName }}</span>
</el-menu-item>
<!-- 子菜单有下级 -->
<el-sub-menu
v-else
:index="child.id.toString()"
>
<el-sub-menu v-else :index="child.id.toString()">
<template #title>
<span>{{ child.menuName }}</span>
</template>
@ -158,49 +133,31 @@ function logout() {
</el-sub-menu>
<!-- 无子菜单的一级菜单 -->
<el-menu-item
v-else
:index="getRoutePath(menu)"
>
<el-menu-item v-else :index="getRoutePath(menu)">
<el-icon>
<Folder/>
<Folder />
</el-icon>
<span>{{ menu.menuName }}</span>
</el-menu-item>
</template>
</el-menu>
</el-card>
</el-aside>
<el-container style="margin-left: 15%; min-width: 180px">
<!-- 修改 el-header 样式 -->
<el-header style="
position: fixed;
top: 0;
left: 15%;
right: 0;
z-index: 80;
background: white;
">
<el-container style="margin-left: 15vw;">
<el-header class="header">
<el-menu class="el-menu-demo" mode="horizontal" :ellipsis="false">
<el-sub-menu index="1" class="admin">
<el-sub-menu index="1">
<template #title>
<el-image :src="imgrule1" alt="错误" style="width: 50px; height: 50px"/>
<el-image :src="imgrule1" style="width: 50px; height: 50px" />
<span style="margin-left: 10px">{{ adminData.name }}</span>
</template>
<el-menu-item @click="message()">查看个人信息</el-menu-item>
<el-menu-item @click="openChangePassword">修改密码</el-menu-item>
<el-menu-item @click="logout">退出登录</el-menu-item>
</el-sub-menu>
</el-menu>
</el-header>
<el-main style="margin-top: 60px">
<!-- 60px el-header 的大致高度可根据实际情况调整 -->
<el-main style="margin-top: 6vh;height: auto;">
<router-view></router-view>
</el-main>
</el-container>
@ -223,68 +180,50 @@ function logout() {
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<div>
<el-button text @click="closeMessage()">关闭</el-button>
</div>
</template>
</el-dialog>
<!-- 自定义密码修改弹窗组件 -->
<el-dialog
v-model="showPasswordDialog"
:center="true"
width="470px"
@closed="onPwdDialogClosed"
>
<ChangePassword
ref="pwdRef"
@confirm="showPasswordDialog = false"
/>
<el-dialog v-model="showPasswordDialog" :center="true" width="470px" @closed="onPwdDialogClosed">
<ChangePassword ref="pwdRef" @confirm="showPasswordDialog = false" />
</el-dialog>
</div>
</template>
<style scoped>
.header {
position: fixed;
right: 0;
z-index: 80;
background: white;
height: 8vh;
}
.message-font {
font-size: 16px;
font-weight: bold;
}
.item {
margin-top: 20px;
margin-right: 40px;
}
.admin {
margin-left: auto;
}
.el-aside {
min-height: 100vh;
width: 200px;
width: 15vw;
position: fixed;
z-index: 100;
height: 90vh;
}
/* background-color: #BFD8D2; */
.logo {
margin: 20px 0px 20px 20px;
display: flex;
align-items: center;
justify-content: center;
height: 12vh;
}
.el-menu-demo {
border: none;
/* 去除边框 */
padding: 0;
/* 去除内边距 */
float: right;
/* 将菜单向右浮动 */
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 240px;
min-height: 400px;
border: none;
/* 去除边框 */
}
</style>
</style>

2
src/views/managerecharge/rate.vue

@ -242,7 +242,7 @@ onMounted(async function () {
</script>
<template>
<el-card style="width:82.8vw;height:85vh">
<el-card style="width:82vw;height:85vh">
<el-table :data="tableData" v-if="(tableData.flag = 1)">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">

44
src/views/permissions/permissions.vue

@ -1,39 +1,31 @@
<template>
<div>
<!-- 这里放置标签切换的按钮 -->
<div class="father">
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'userPermission' ? 'primary' : 'default'"
@click="navigateTo('userPermission')"
:disabled="!hasDetail"
>
<el-button :type="activeTab === 'userPermission' ? 'primary' : 'default'" @click="navigateTo('userPermission')"
:disabled="!hasDetail">
用户管理
</el-button>
<el-button
:type="activeTab === 'rolePermission' ? 'primary' : 'default'"
@click="navigateTo('rolePermission')"
:disabled="!hasBalance"
>
<el-button :type="activeTab === 'rolePermission' ? 'primary' : 'default'" @click="navigateTo('rolePermission')"
:disabled="!hasBalance">
角色管理
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
</div>
<div>
<router-view></router-view>
</div>
</template>
<script setup>
import {onMounted, ref, watch} from 'vue';
import {useRoute, useRouter} from 'vue-router';
import {storeToRefs} from "pinia";
import {useAdminStore} from "@/store/index.js";
import {hasMenuPermission, permissionMapping} from "@/utils/menuTreePermission.js";
import { onMounted, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { storeToRefs } from "pinia";
import { useAdminStore } from "@/store/index.js";
import { hasMenuPermission, permissionMapping } from "@/utils/menuTreePermission.js";
const router = useRouter();
const route = useRoute();
const adminStore = useAdminStore();
const {menuTree} = storeToRefs(adminStore);
const { menuTree } = storeToRefs(adminStore);
const activeTab = ref('');
const hasDetail = ref(false);
@ -41,7 +33,7 @@ const hasBalance = ref(false);
//
const navigateTo = (name) => {
activeTab.value = name;
router.push({name});
router.push({ name });
};
@ -86,4 +78,10 @@ onMounted(() => {
}
}
});
</script>
</script>
<style>
.father{
width:82vw;
height:4vh;
}
</style>

2
src/views/permissions/rolePermission.vue

@ -580,7 +580,7 @@ onMounted(async function () {
</script>
<template>
<div>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-card style="margin-bottom: 1vh;">
<div style="display: flex;">
<el-text size="large">角色名称</el-text>
<el-input v-model="role.name" style="width: 240px" placeholder="请输入角色名称" clearable />

387
src/views/permissions/userPermission.vue

@ -1,7 +1,7 @@
<script setup>
import {computed, onMounted, ref} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {InfoFilled} from '@element-plus/icons-vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { InfoFilled } from '@element-plus/icons-vue'
import _ from 'lodash'
import request from '@/util/http'
import API from '@/util/http'
@ -26,27 +26,37 @@ const getObj = ref({
pageNum: 1,
pageSize: 10
})
//
const ackVisible = ref(false)
const currentStatusRow = ref(null)
const statusLoading = ref({})
const showStatusConfirm = (row, targetStatus) => {
currentStatusRow.value = {
...row,//
targetStatus//
}
ackVisible.value = true
}
//
const addUserRules = {
account: [
{required: true, message: '请输入OA号', trigger: 'blur'},
{pattern: /^\d+$/, message: 'OA号必须为数字', trigger: 'blur'},
{max: 20, message: '长度不能超过20位', trigger: 'blur'}
{ required: true, message: '请输入OA号', trigger: 'blur' },
{ pattern: /^\d+$/, message: 'OA号必须为数字', trigger: 'blur' },
{ max: 20, message: '长度不能超过20位', trigger: 'blur' }
],
name: [
{required: true, message: '请输入用户名', trigger: 'blur'},
{max: 20, message: '长度不能超过20位', trigger: 'blur'}
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ max: 20, message: '长度不能超过20位', trigger: 'blur' }
],
market: [
{required: true, message: '请选择所属地区', trigger: 'change'}
{ required: true, message: '请选择所属地区', trigger: 'change' }
],
permission: [
{required: true, message: '请选择角色名称', trigger: 'change'}
{ required: true, message: '请选择角色名称', trigger: 'change' }
],
postiton: [
{required: true, message: '请输入职位', trigger: 'blur'},
{max: 20, message: '长度不能超过20位', trigger: 'blur'}
{ required: true, message: '请输入职位', trigger: 'blur' },
{ max: 20, message: '长度不能超过20位', trigger: 'blur' }
],
machineIds: [
{
@ -93,7 +103,7 @@ const adminData = ref({})
// todo
const getAdminData = async function () {
try {
const result = await API({url: '/admin/userinfo', data: {}})
const result = await API({ url: '/admin/userinfo', data: {} })
adminData.value = result
// console.log('', result)
console.log('管理员用户信息', adminData.value)
@ -214,8 +224,8 @@ const getArea = async function () {
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
? transformTree([child]) //
: null;
return {
value: child.name,
@ -320,7 +330,7 @@ const permissionList = ref([])
const getRoles = async function () {
try {
const res = await API({url: '/role/selectAll'})
const res = await API({ url: '/role/selectAll' })
permissionList.value = res.data.map(item => ({
label: item.roleName,
value: item.id
@ -436,7 +446,7 @@ const getUserLists = async function (selectedRoleId) {
parentRoleTip.value = '';
const parentRes = await request({
url: '/role/selectFather',
data: {id: selectedRoleId} // ID
data: { id: selectedRoleId } // ID
});
const parentId = parentRes.data.fatherId;
const parentName = parentRes.data.parentName;
@ -454,7 +464,7 @@ const getUserLists = async function (selectedRoleId) {
// }
const res = await API({
url: '/menu/tree',
data: {id: roleId}
data: { id: roleId }
})
data.value = processTreeData(res.data)
permissionEditObj.value.checkedKeys = collectIds(res.data) || [];
@ -469,11 +479,11 @@ const getUserLists = async function (selectedRoleId) {
//
const editAdminRules = {
market: [
{required: true, message: '请选择所属地区', trigger: 'change'}
{ required: true, message: '请选择所属地区', trigger: 'change' }
],
postiton: [
{required: true, message: '请输入职位', trigger: ['blur', 'change']},
{max: 20, message: '长度不能超过20位', trigger: ['blur', 'change']}
{ required: true, message: '请输入职位', trigger: ['blur', 'change'] },
{ max: 20, message: '长度不能超过20位', trigger: ['blur', 'change'] }
],
machineIds: [
{
@ -494,7 +504,7 @@ const editAdminRules = {
};
//
const permissionEdit = async function () {
let {adminName: userName, roleName: oldRole, roleId: newRoleId} = permissionEditObj.value;
let { adminName: userName, roleName: oldRole, roleId: newRoleId } = permissionEditObj.value;
if (oldRole == null) {
oldRole = '暂未分配角色'
}
@ -510,15 +520,15 @@ const permissionEdit = async function () {
});
});
await ElMessageBox.confirm(
`确认修改权限角色?<br>您正在将【${userName}】的权限角色从【${oldRole}】修改为【${newRole}】<br>变更后,该用户的可操作权限将同步更新为新角色配置,涉及数据访问、功能操作等权限变化,请谨慎确认。`,
'警告',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: "warning",
lockScroll: false,
dangerouslyUseHTMLString: true // HTML
}
`确认修改权限角色?<br>您正在将【${userName}】的权限角色从【${oldRole}】修改为【${newRole}】<br>变更后,该用户的可操作权限将同步更新为新角色配置,涉及数据访问、功能操作等权限变化,请谨慎确认。`,
'警告',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: "warning",
lockScroll: false,
dangerouslyUseHTMLString: true // HTML
}
)
const result = await request({
url: '/permission/updateAdmin',
@ -537,12 +547,12 @@ const permissionEdit = async function () {
console.log('编辑最后提交数据', permissionEditObj.value);
if (result.code === 200) {
await ElMessageBox.alert(
`用户${userName}的权限角色已更改为【${newRole}`,
'成功',
{
confirmButtonText: '确定',
type: 'success' // success
}
`用户${userName}的权限角色已更改为【${newRole}`,
'成功',
{
confirmButtonText: '确定',
type: 'success' // success
}
);
getPermission();
closeUserEditVisible();
@ -584,13 +594,16 @@ const delConfirm = async function () {
}
//
const editStatus = async function (row) {
const { id, account, targetStatus, ...restRow } = currentStatusRow.value
try {
console.log(row)
statusLoading.value[id] = true
console.log(row)
permissionEditObj.value = {}
permissionEditObj.value.id = row.id
permissionEditObj.value.account = row.account
permissionEditObj.value.adminStatus = row.adminStatus
permissionEditObj.value.id = id
permissionEditObj.value.account = account
permissionEditObj.value.adminStatus = targetStatus
console.log('修改用户权限状态', permissionEditObj.value)
const result = await request({
@ -598,9 +611,12 @@ const editStatus = async function (row) {
data: permissionEditObj.value
})
console.log('请求成功2', result)
ElMessage.success(
if (result.code === 200) {
ElMessage.success(
permissionEditObj.value.adminStatus === 1 ? '启用成功' : '禁用成功'
)
)
statusLoading.value[id] = false
}
permissionEditObj.value = {}
getPermission()
} catch (error) {
@ -652,8 +668,8 @@ const handleMarketChangeAddUser = (value) => {
if (Array.isArray(value) && value.length > 0) {
//
const selectedMarkets = value
.map(path => Array.isArray(path) && path.length > 0 ? path[path.length - 1] : null)
.filter(Boolean);
.map(path => Array.isArray(path) && path.length > 0 ? path[path.length - 1] : null)
.filter(Boolean);
//
const hasHeadquarters = selectedMarkets.includes('总部');
@ -676,8 +692,8 @@ const handleMarketChangeEditUser = (value) => {
if (Array.isArray(value) && value.length > 0) {
//
const selectedMarkets = value
.map(path => Array.isArray(path) && path.length > 0 ? path[path.length - 1] : null)
.filter(Boolean);
.map(path => Array.isArray(path) && path.length > 0 ? path[path.length - 1] : null)
.filter(Boolean);
//
const hasHeadquarters = selectedMarkets.includes('总部');
@ -881,158 +897,140 @@ onMounted(async function () {
<template>
<div>
<el-card style="margin-bottom: 20px;margin-top:10px">
<div class="head-card">
<div class="head-card-element">
<el-text class="mx-1" size="large">OA号</el-text>
<el-input v-model="admin.account" style="width: 240px" placeholder="请输入OA号" clearable/>
</div>
<div class="head-card-element" style="margin-left: 50px">
<el-text class="mx-1" size="large">所属地区</el-text>
<el-cascader
v-model="admin.market"
:options="marketsTree"
placeholder="请选择所属地区"
clearable
style="width:180px"
@change="handleMarketChange"
/>
<el-card style="margin-bottom: 1vh;">
<div class="head-card">
<div class="head-card-element">
<el-text class="mx-1" size="large">OA号</el-text>
<el-input v-model="admin.account" style="width: 240px" placeholder="请输入OA号" clearable />
</div>
<div class="head-card-element" style="margin-left: 50px">
<el-text class="mx-1" size="large">所属地区</el-text>
<el-cascader v-model="admin.market" :options="marketsTree" placeholder="请选择所属地区" clearable style="width:180px"
@change="handleMarketChange" />
</div>
<div class="head-card-element" style="margin-left: 50px">
<el-text class="mx-1" size="large">职位名称</el-text>
<el-select v-model="admin.postiton" placeholder="请选择职位名称" style="width: 240px" clearable>
<el-option v-for="item in postiton" :key="item" :label="item" :value="item"/>
</el-select>
</div>
</div>
<div class="head-card-element" style="margin-left: 50px">
<el-text class="mx-1" size="large">职位名称</el-text>
<el-select v-model="admin.postiton" placeholder="请选择职位名称" style="width: 240px" clearable>
<el-option v-for="item in postiton" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div class="head-card-btn">
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
</div>
</div>
</el-card>
<el-card>
<!-- 展示表单 -->
<div class="add-item">
<el-button style="color: #048efb; border: 1px solid #048efb" @click="userAddInit()">新增用户</el-button>
</div>
<div class="head-card-btn">
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
</div>
</div>
</el-card>
<el-card>
<!-- 展示表单 -->
<div class="add-item">
<el-button style="color: #048efb; border: 1px solid #048efb" @click="userAddInit()">新增用户</el-button>
</div>
<div>
<el-table :data="tableData" style="width: 100%" show-overflow-tooltip>
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="account" label="OA号"/>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="market" label="所属地区"/>
<el-table-column prop="postiton" label="职位"/>
<el-table-column prop="roleName" label="部门权限">
</el-table-column>
<el-table-column prop="remark" label="备注"/>
<el-table-column prop="operation" label="操作" width="280px">
<template #default="scope">
<el-button type="warning" text @click="resetPassword(scope.row)">
重置密码
<div>
<el-table :data="tableData" style="width: 82vw;height:60vh" show-overflow-tooltip>
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="account" label="OA号" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="market" label="所属地区" />
<el-table-column prop="postiton" label="职位" />
<el-table-column prop="roleName" label="部门权限">
</el-table-column>
<el-table-column prop="remark" label="备注" />
<el-table-column prop="operation" label="操作" width="280px">
<template #default="scope">
<el-button type="warning" text @click="resetPassword(scope.row)">
重置密码
</el-button>
<el-button type="primary" text @click="permissionEditInit(scope.row)"
:disabled="scope.row.adminStatus === 0 || scope.row.account === adminData.account">
修改权限
</el-button>
<el-popconfirm title="确定将此用户删除吗?" @confirm="delConfirm">
<template #reference>
<el-button type="danger" text @click="del(scope.row)"
:disabled="scope.row.adminStatus === 0 || scope.row.account === adminData.account">
删除
</el-button>
<el-button type="primary" text @click="permissionEditInit(scope.row)"
:disabled="scope.row.adminStatus === 0 || scope.row.account === adminData.account">
修改权限
</template>
<template #actions="{ confirm, cancel }">
<el-button size="small" @click="cancel">取消</el-button>
<el-button type="primary" size="small" @click="confirm">
确定
</el-button>
<el-popconfirm title="确定将此用户删除吗?" @confirm="delConfirm">
<template #reference>
<el-button type="danger" text @click="del(scope.row)"
:disabled="scope.row.adminStatus === 0 || scope.row.account === adminData.account">
删除
</el-button>
</template>
<template #actions="{ confirm, cancel }">
<el-button size="small" @click="cancel">取消</el-button>
<el-button type="primary" size="small" @click="confirm">
确定
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
<el-table-column prop="adminStatus" label="状态">
<template #default="scope">
<el-switch v-model="scope.row.adminStatus" :active-value="1" :inactive-value="0" size="large"
:disabled="scope.row.account === adminData.account" @change="editStatus(scope.row)" style="
</el-popconfirm>
</template>
</el-table-column>
<el-table-column prop="adminStatus" label="状态">
<template #default="scope">
<el-switch :model-value="scope.row.adminStatus" :active-value="1" :inactive-value="0" size="large"
:disabled="scope.row.account === adminData.account || statusLoading[scope.row.id]"
@change="(targetStatus) => showStatusConfirm(scope.row, targetStatus)" style="
--el-switch-on-color: #13ce66;
--el-switch-off-color: #ff4949;
" active-text="启用" inactive-text="禁用" inline-prompt/>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
" active-text="启用" inactive-text="禁用" inline-prompt />
</template>
</el-table-column>
</el-table>
<div class="pagination" style="margin-top: 1vh;">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</div>
</div>
</el-card>
</div>
<!-- 新增用户权限 -->
<el-dialog v-model="userAddVisible" title="新增用户权限" width="800px" :close-on-click-modal="false"
@close="handleDialogClose">
@close="handleDialogClose">
<template #footer>
<el-form ref="Ref" :rules="addUserRules" :model="addAdmin" label-width="auto"
style="max-width: 600px; align-items: center">
style="max-width: 600px; align-items: center">
<el-form-item prop="account" label="OA号:" required clearable>
<el-input v-model="addAdmin.account" placeholder="请输入OA号" style="width: 220px"/>
<el-input v-model="addAdmin.account" placeholder="请输入OA号" style="width: 220px" />
</el-form-item>
<el-form-item prop="name" label="用户名:" required clearable>
<el-input v-model="addAdmin.name" placeholder="请输入用户名" style="width: 220px"/>
<el-input v-model="addAdmin.name" placeholder="请输入用户名" style="width: 220px" />
</el-form-item>
<el-form-item prop="market" label="所属地区:" required clearable>
<el-cascader
v-model="addAdmin.market"
:options="marketsTree"
placeholder="请选择所属地区"
clearable
collapse-tags
collapse-tags-tooltip
style="width:220px"
@change="handleMarketChangeAddUser"
:max-collapse-tags="2"
:props="addUserProps"
/>
<el-cascader v-model="addAdmin.market" :options="marketsTree" placeholder="请选择所属地区" clearable collapse-tags
collapse-tags-tooltip style="width:220px" @change="handleMarketChangeAddUser" :max-collapse-tags="2"
:props="addUserProps" />
</el-form-item>
<el-form-item prop="permission" label="角色名称:" required>
<el-select v-model="addAdmin.permission" placeholder="请选择角色名称" style="width: 220px" clearable>
<el-option v-for="item in permissionList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="postiton" label="职位:" required>
<el-input v-model="addAdmin.postiton" placeholder="请输入职位" style="width: 220px" clearable/>
<el-input v-model="addAdmin.postiton" placeholder="请输入职位" style="width: 220px" clearable />
</el-form-item>
<el-form-item prop="machineIds" label="机器码:" required>
<div style="display: flex; align-items: center; flex-wrap: wrap; gap: 10px;">
<!-- 动态添加的机器码输入框 -->
<div v-for="(item, index) in addAdmin.machineIds" :key="index">
<el-input v-model="addAdmin.machineIds[index]" placeholder="请输入机器码"
style="width: 220px; margin-right: 10px;"/>
style="width: 220px; margin-right: 10px;" />
</div>
<el-button type="primary" @click="addMachineIdInput">添加</el-button>
</div>
</el-form-item>
<el-form-item prop="remark" label="备注">
<el-input v-model="addAdmin.remark" style="width: 300px" :rows="2" maxlength="100" show-word-limit
type="textarea"/>
type="textarea" />
</el-form-item>
</el-form>
@ -1048,50 +1046,38 @@ onMounted(async function () {
<!-- 这是编辑用户权限弹窗 -->
<el-dialog v-model="userEditVisible" title="编辑用户权限" width="800px" :close-on-click-modal="false">
<el-form ref="Ref" :rules="editAdminRules" :model="permissionEditObj" label-width="auto"
style="max-width: 600px; align-items: center">
style="max-width: 600px; align-items: center">
<el-form-item prop="account" label="用户账号:" clearable>
<el-input v-model="permissionEditObj.account" placeholder="请输入OA号" style="width: 220px" disabled/>
<el-input v-model="permissionEditObj.account" placeholder="请输入OA号" style="width: 220px" disabled />
</el-form-item>
<el-form-item prop="name" label="用户名称:">
<el-input v-model="permissionEditObj.adminName" placeholder="请输入用户名" style="width: 220px" disabled/>
<el-input v-model="permissionEditObj.adminName" placeholder="请输入用户名" style="width: 220px" disabled />
</el-form-item>
<el-form-item prop="market" label="所属地区:" clearable>
<el-cascader
v-model="permissionEditObj.market"
:options="marketsTree"
placeholder="请选择所属地区"
clearable
collapse-tags
collapse-tags-tooltip
style="width:220px"
@change="handleMarketChangeEditUser"
:max-collapse-tags="2"
:props="editUserProps"
/>
<el-cascader v-model="permissionEditObj.market" :options="marketsTree" placeholder="请选择所属地区" clearable
collapse-tags collapse-tags-tooltip style="width:220px" @change="handleMarketChangeEditUser"
:max-collapse-tags="2" :props="editUserProps" />
</el-form-item>
<el-form-item prop="postiton" label="职位:">
<el-input v-model="permissionEditObj.postiton" placeholder="请输入职位" style="width: 220px" clearable/>
<el-input v-model="permissionEditObj.postiton" placeholder="请输入职位" style="width: 220px" clearable />
</el-form-item>
<el-form-item prop="roleName" label="角色名称:">
<el-select v-model="permissionEditObj.roleId" placeholder="请选择角色" style="width: 220px"
@change="getUserLists">
<el-select v-model="permissionEditObj.roleId" placeholder="请选择角色" style="width: 220px" @change="getUserLists">
<el-option v-for="item in permissionList" :key="item.value" :label="item.label"
:value="item.value"></el-option>
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="parentName" label="上级角色:">
<el-select v-model="permissionEditObj.parentId" placeholder="无上级角色" :disabled="!!parentRoleTip"
style="width: 220px">
<el-option v-if="parentRoleTip" :key="0" :label="parentRoleTip" :value="null" disabled/>
style="width: 220px">
<el-option v-if="parentRoleTip" :key="0" :label="parentRoleTip" :value="null" disabled />
<el-option v-else v-for="item in permissionList" :key="item.value" :label="item.label" disabled
:value="item.value"></el-option>
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="permissionSelect" label="权限列表:">
<el-tree v-if="data.length > 0" :data="data" :disabled="true" show-checkbox node-key="id"
:props="{ label: 'menuName', children: 'children' }"
:default-checked-keys="permissionEditObj.checkedKeys">
:props="{ label: 'menuName', children: 'children' }" :default-checked-keys="permissionEditObj.checkedKeys">
</el-tree>
<div v-else style="display: flex; align-items: center; gap: 8px;">
<span style="color: #999;">暂无数据</span>
@ -1102,7 +1088,7 @@ onMounted(async function () {
<!-- 动态添加的机器码输入框 -->
<div v-for="(item, index) in permissionEditObj.machineIds" :key="index">
<el-input v-model="permissionEditObj.machineIds[index]" placeholder=""
style="width: 220px; margin-right: 10px;"/>
style="width: 220px; margin-right: 10px;" />
</div>
<el-button type="primary" @click="UseraddMachineIdInput">添加</el-button>
</div>
@ -1122,11 +1108,11 @@ onMounted(async function () {
<!-- 重置密码确认弹窗 -->
<el-dialog v-model="resetConfirmVisible" width="500px" :close-on-click-modal="false"
:before-close="cancelResetPassword">
:before-close="cancelResetPassword">
<el-row>
<el-col :span="4" style="margin-top: 20px">
<el-icon class="dialog-icon" color="#10AEFF" size="50">
<InfoFilled/>
<InfoFilled />
</el-icon>
</el-col>
<el-col :span="20">
@ -1143,7 +1129,30 @@ onMounted(async function () {
</template>
</el-dialog>
<el-dialog v-model="ackVisible" :title="currentStatusRow?.adminStatus === 1 ? '确认禁用' : '确认启用'" width="300px"
:close-on-click-modal="false"
@close="() => { if (currentStatusRow) currentStatusRow.adminStatus = currentStatusRow.adminStatus === 1 ? 0 : 1 }">
<div class="status-confirm-content">
确定要{{ currentStatusRow?.adminStatus === 1 ? '禁用' : '启用' }}该用户吗
<br>
</div>
<template #footer>
<div style="display: flex;">
<el-button @click="() => {
currentStatusRow.adminStatus = currentStatusRow.adminStatus === 1 ? 0 : 1
ackVisible = false
}">
取消
</el-button>
<el-button type="primary" @click="() => {
editStatus(currentStatusRow)
ackVisible = false
}">
确认
</el-button>
</div>
</template>
</el-dialog>
</template>
<style scoped>

2
src/views/recharge/bean/addBeanRecharge.vue

@ -270,7 +270,7 @@ const handleAddForm = async () => {
float: left;
.customer-info {
width: 30vw;
width: 35vw;
height: 28vh;
margin-top: 5vh;
display: flex;

15
src/views/recharge/bean/beanOnlineRecharge.vue

@ -33,9 +33,16 @@ const getTotalBeans = async () => {
}
})
if (result.code == 200) {
permanentBeans.value = result.data.permanentBean
num.value = result.data.num
money.value = result.data.money
if(result.data){
permanentBeans.value = result.data.permanentBean
num.value = result.data.num
money.value = result.data.money
}else{
permanentBeans.value = 0
num.value = 0
money.value = 0
}
console.log('金豆总数获取成功');
}
console.log('获取金豆总数失败:', result.msg);
@ -384,7 +391,7 @@ onMounted(async function () {
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div>
<el-table :data="tableData" style="width:80vw;" height="55vh" @sort-change="handleSortChange">
<el-table :data="tableData" style="width:80vw;" height="60vh" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{

34
src/views/recharge/bean/beanSystemRecharge.vue

@ -41,10 +41,18 @@ const getTotalBeans = async () => {
}
})
if (result.code == 200) {
permanentBeans.value = result.data.permanentBean
freeBean.value = result.data.freeBean
beanNum.value = result.data.beanNum
money.value = result.data.money
if (result.data) {
permanentBeans.value = result.data.permanentBean
freeBean.value = result.data.freeBean
beanNum.value = result.data.beanNum
money.value = result.data.money
}else{
permanentBeans.value = 0
freeBean.value = 0
beanNum.value = 0
money.value = 0
}
console.log('金豆总数获取成功');
}
console.log('获取金豆总数失败:', result.msg);
@ -337,7 +345,7 @@ onMounted(async function () {
<el-option v-for="item in market" :key="item" :label="item" :value="item" />
</el-select>
</div>
<div style="margin-top: 10px;">
<div style="margin-top: 1vh;">
<el-text size="large">充值时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 20vw" @change="handleDatePickerChange"
@ -357,8 +365,6 @@ onMounted(async function () {
<el-button type="primary" style="width: 80px;" @click="exportExcel()">导出Excel</el-button>
<el-button type="primary" style="width: 95px;" @click="openExportList">查看导出列表</el-button>
</div>
</el-card>
<el-card>
<div>
@ -367,12 +373,12 @@ onMounted(async function () {
免费金豆数{{ format3(freeBean) }}
</div>
<div>
<el-table :data="tableData" style="width: 82.8vw;height:60vh;" @sort-change="handleSortChange">
<el-table :data="tableData" style="width: 82vw;height:60vh;" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
}}</span>
</template>
</el-table-column>
<el-table-column fixed="left" prop="name" label="姓名" min-width="100" show-overflow-tooltip />
@ -428,4 +434,12 @@ onMounted(async function () {
</template>
</el-dialog>
</template>
<style scoped></style>
<style scoped>
.condition {
height: 6vw;
margin-bottom: 0.5vh;
display: flex;
justify-content: space-between;
align-items: center;
}
</style>

76
src/views/recharge/gold/addCoinRecharge.vue

@ -230,10 +230,11 @@ const addBefore = () => {
return;
}
// rateId
const selectedRate = rateName.find(item => item.value === recharge.value.rateName);
// rateId
const selectedRate = rateName.value.find(item => item.value === recharge.value.rateName);
if (selectedRate) {
recharge.value.rateId = selectedRate.rateId;
recharge.value.rateId = selectedRate.value;
recharge.value.rateName = selectedRate.label; // rateName
}
//
@ -475,53 +476,27 @@ const activity = ref([])
//
const rateName = [
{
value: 'USD',
label: 'USD',
rateId: 1
},
{
value: 'HKD',
label: 'HKD',
rateId: 2
},
{
value: 'THB',
label: 'THB',
rateId: 3
},
{
value: 'VND',
label: 'VND',
rateId: 4
},
{
value: 'CAD',
label: 'CAD',
rateId: 5
},
{
value: 'MYR',
label: 'MYR',
rateId: 6
},
{
value: 'KRW',
label: 'KRW',
rateId: 7
},
{
value: 'JPY',
label: 'JPY',
rateId: 8
},
{
value: 'CNY',
label: 'CNY',
rateId: 9
const rateName = ref([])
const fetchRateData = async () => {
try {
const result = await API({
url: '/general/getRate',
data: {}
});
console.log('response', result);
if (result.code === 200) {
rateName.value = result.data.map(item => ({
value: item.id,
label: item.rateName,
}));
}
console.log('货币信息', rateName.value);
} catch (error) {
console.error('获取货币信息失败:', error);
}
]
};
//
const customUpload = async (options) => {
@ -648,6 +623,7 @@ const deleteRecharge = function () {
onMounted(async function () {
await getAdminData()
await fetchRateData()
// await getCurrency()
// await getActivity()//
@ -989,4 +965,4 @@ p {
height: 50px;
text-align: center;
}
</style>
</style>

7
src/views/recharge/gold/coinRechargeDetail.vue

@ -656,6 +656,13 @@ const getTagText = (state) => {
<el-table-column prop="payPlatform" label="充值平台" width="110px" />
<el-table-column prop="payModel" label="支付方式" width="100px" />
<el-table-column prop="remark" label="备注" width="150px" show-overflow-tooltip />
<el-table-column prop="isRefund" label="订单状态" width="200px" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.isRefund == 0">正常</span>
<span v-else-if="scope.row.isRefund == 1">已退款</span>
<span v-else>未知状态</span>
</template>
</el-table-column>
<el-table-column prop="adminName" label="提交人" width="100px" />
<el-table-column prop="auditTime" sortable label="充值时间" width="200px">
<template #default="scope">

575
src/views/refund/gold/addCoinRefund.vue

@ -2,47 +2,43 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import API from '@/util/http.js'
import moment from 'moment'
// import _ from 'lodash'
const addRe = ref({
typeR: '0'
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
import { findMenuById, permissionMapping } from "@/utils/menuTreePermission.js"
import { e } from 'mathjs';
const adminStore = useAdminStore();
const { adminData, menuTree } = storeToRefs(adminStore);
const tableData = ref([])
const pagination = ref({
pageNum: 1,
pageSize: 10,
total: 0
})
//
const adminData = ref({})
const getAdminData = async function () {
try {
const result = await API({ url: '/admin/userinfo', data: {} })
adminData.value = result
addRefund.value.adminId = adminData.value.id
console.log('请求成功', result)
console.log('用户信息', user.value)
} catch (error) {
console.log('请求失败', error)
}
}
//
const trimJwCode = () => {
if (addRefund.value.jwcode) {
addRefund.value.jwcode = addRefund.value.jwcode.replace(/\s/g, '');
}
}
// 退
const addRefund = ref({
jwcode: '',
goodsName: '',
refundType: '',
refundModel: 0,
refundModel: 0, // 0:退, 1:退
permanentGold: '',
freeGold: '',
taskGold: '',
sumGold: 0,
remark: '',
adminId: null,
orderCode: ''
adminId: adminData.value.id,
adminName: adminData.value.name,
orderCode: '',
type: 0
})
//
@ -60,32 +56,83 @@ const cancel = function () {
adminId: adminData.value.id,
orderCode: '',
}
addRe.value.typeR = '0'
}
const getRefund = async function () {
if (!addRefund.value.jwcode) {
ElMessage.error('请输入精网号')
return
}
addRefund.value.orderCode = ''
addRefund.value.goodsName = ''
addRefund.value.permanentGold = ''
addRefund.value.freeGold = ''
addRefund.value.taskGold = ''
let type = null
if (addRefund.value.refundType === '商品退款') {
type = 1
} else {
type = 0
}
const params = {
pageNum: pagination.value.pageNum,
pageSize: pagination.value.pageSize,
goldDetail: {
jwcode: addRefund.value.jwcode,
flag: 0,
type: type,
markets:[]
}
}
try {
const res = await API({
url: '/goldDetail/getGoldDetail',
data: {
...params
}
})
tableData.value = res.data.list
pagination.value.total = res.data.total
if (Array.isArray(res.data.list) && res.data.list.length > 0) {
orderCodes.value = res.data.list.map(item => ({
label: item.orderCode,
value: item.orderCode
}))
console.log("看看订单号们", orderCodes.value)
} else {
ElMessage.info("未查询到相关订单")
}
} catch (error) {
console.log('goldDetail有错误', error)
ElMessage.error('请求失败')
tableData.value = []
pagination.value.total = 0
}
}
// 退
const add = async function () {
try {
// refundModel
addRefund.value.refundModel = parseInt(addRe.value.typeR);
if (addRefund.value.refundType === '商品退款') {
addRefund.value.type = 1
} else {
addRefund.value.type = 0
}
// 100
const processedRefund = {
...addRefund.value,
permanentGold: (Number(addRefund.value.permanentGold) || 0) * 100,
freeGold: (Number(addRefund.value.freeGold) || 0) * 100,
taskGold: (Number(addRefund.value.taskGold) || 0) * 100,
sumGold: (Number(addRefund.value.sumGold) || 0) * 100
permanentGold: addRefund.value.permanentGold * 100,
freeGold: addRefund.value.freeGold * 100,
taskGold: addRefund.value.taskGold * 100,
sumGold: addRefund.value.sumGold * 100
}
console.log('提交的退款信息', processedRefund)
addDisabled.value = true
// POST
const result = await API({
url: '/refund/add',
data: processedRefund
})
addDisabled.value = false
if (result.code === 0) {
ElMessage.error(result.msg)
@ -97,38 +144,27 @@ const add = async function () {
cancel()
} catch (error) {
console.log('请求失败', error)
//
ElMessage.error('请求失败,请重试')
addDisabled.value = false
}
}
//
//
const addDisabled = ref(false)
// 退
const addBefore = () => {
Ref.value.validate(async (valid) => {
if (valid) {
// try {
// await getUser(addRefund.value.jwcode);
// if (!user.value.jwcode) {
// ElMessage.error('');
// return;
// }
// } catch (error) {
// ElMessage.error('');
// return;
// }
ElMessageBox.confirm('确认退款?')
.then(() => {
add()
console.log('退款成功')
})
.catch(() => {
console.log('取消退款')
})
} else {
//
//
ElMessage({
type: 'error',
message: '请检查输入内容'
@ -138,10 +174,7 @@ const addBefore = () => {
}
//
//
const Ref = ref(null)
const startChange = (val) => {
}
const validateJwCode = (rule, value, callback) => {
if (!value) {
callback(new Error('精网号不能为空'));
@ -177,32 +210,12 @@ const rules = reactive({
}
]
})
//
const cancelExceptJwcode = function () {
addRefund.value = {
jwcode: addRefund.value.jwcode, //
goodsName: '',
refundType: '',
refundModel: 0,
permanentGold: '',
freeGold: '',
taskGold: '',
sumGold: 0,
remark: '',
adminId: adminData.value.id
}
addRe.value.typeR = '0'
}
//
const user = ref({
firstRechargeTime: ''
})
const getUser = async function (jwcode) {
trimJwCode();
// cancelExceptJwcode();
//
if (!jwcode) {
ElMessage.warning('精网号不能为空');
@ -243,114 +256,69 @@ const getUser = async function (jwcode) {
}
user.value = processedData;
console.log("用户信息", user.value);
//ElMessage.success(result.msg);
tableData.value = []
addRefund.value.refundType = ''
}
} catch (error) {
console.log("请求失败", error);
ElMessage.error("精网号错误");
}
}
// 退退
const refundType = ref([{ value: '商品退款', label: '商品退款' }]);
// 退
// const getRefundTypes = async function () {
// try {
// // 退
// const result = await API({
// url: '/refund/refundType', //退
// data: {} })
// console.log('退', result)
// //
// if (Array.isArray(result.data)) {
// // { value, label }
// refundType.value = result.data.map(item => ({ value: item, label: item }));
// } else {
// console.error('退', result)
// ElMessage.error('退')
// }
// console.log('退', refundType.value)
// } catch (error) {
// console.log('退', error)
// }
// }
//
const goodsName = ref([]) //
const orderCodes = ref([]) //
const getGoods = async function (jwcode) {
trimJwCode();
if (!/^\d{1,9}$/.test(jwcode)) {
ElMessage.warning('精网号必须为数字且不超过九位');
return;
}
const refundType = ref([
{ value: '商品退款', label: '商品退款' },
{ value: '金币退款', label: '金币退款' }
])
if (!addRefund.value.jwcode) {
goodsName.value = []
orderCodes.value = []
return;
}
//
const goodsName = ref([])
const orderCodes = ref([])
try {
const result = await API({
url: '/refund/selectGoods',
data: { jwcode: addRefund.value.jwcode }
})
if (Array.isArray(result.data)) {
//
goodsName.value = result.data.map(item => ({
orderCode: item.orderCode,
goodsName: item.goodsName,
permanentGold: Math.abs(Number((item.permanentGold) || 0) / 100),
freeGold: Math.abs(Number((item.freeGold) || 0) / 100),
taskGold: Math.abs(Number((item.taskGold) || 0) / 100),
}))
// [{label, value}]
orderCodes.value = result.data.map(item => ({
label: item.orderCode,
value: item.orderCode
}))
//
const handleOrderChange = (orderCode) => {
addRefund.value.refundModel = 0
addRefund.value.goodsName = ''
addRefund.value.permanentGold = ''
addRefund.value.freeGold = ''
addRefund.value.taskGold = ''
const order = tableData.value.find(item => item.orderCode === orderCode)
if (order) {
addRefund.value.goodsName = order.goodsName
if (addRefund.value.refundType === '金币退款') {
selectedGoodsGold.value = {
permanentGold: Number(order.permanentGold) || 0,
freeGold: Number(order.freeGold) || 0,
taskGold: Number(order.taskGold) || 0
}
console.log('看看金币退款参数', selectedGoodsGold.value)
} else {
ElMessage.error('商品数据格式错误,请联系管理员')
selectedGoodsGold.value = {
permanentGold: -Number(order.permanentGold) || 0,
freeGold: -Number(order.freeGold) || 0,
taskGold: -Number(order.taskGold) || 0
}
console.log('看看商品退款参数', selectedGoodsGold.value)
}
} catch (error) {
console.log('请求失败', error)
ElMessage.error('查询商品失败,请检查精网号是否正确')
goodsName.value = []
orderCodes.value = []
}
}
//
const handleOrderChange = (orderCode) => {
const selected = goodsName.value.find(item => item.orderCode === orderCode)
if (selected) {
addRefund.value.goodsName = selected.goodsName
// 退退
if (addRe.value.typeR === '0') {
addRefund.value.permanentGold = selected.permanentGold
addRefund.value.freeGold = selected.freeGold
addRefund.value.taskGold = selected.taskGold
// 退
if (addRefund.value.refundModel === 0) {
addRefund.value.permanentGold = selectedGoodsGold.value.permanentGold.toString()
addRefund.value.freeGold = selectedGoodsGold.value.freeGold.toString()
addRefund.value.taskGold = selectedGoodsGold.value.taskGold.toString()
}
//
} else {
addRefund.value.goodsName = ''
selectedGoodsGold.value = {
permanentGold: selected.permanentGold,
freeGold: selected.freeGold,
taskGold: selected.taskGold
permanentGold: 0,
freeGold: 0,
taskGold: 0
}
} else {
restGoods()
}
}
//
const selectedGoodsGold = ref({
permanentGold: 0,
@ -367,37 +335,16 @@ const restGoods = () => {
addRefund.value.taskGold = ''
}
// 退退退退
// 退
const handleRefundModelChange = () => {
if (addRe.value.typeR === '0') {
// 退
addRefund.value.permanentGold = selectedGoodsGold.value.permanentGold;
addRefund.value.freeGold = selectedGoodsGold.value.freeGold;
addRefund.value.taskGold = selectedGoodsGold.value.taskGold;
// 退
if (addRefund.value.refundModel === 0) {
addRefund.value.permanentGold = selectedGoodsGold.value.permanentGold.toString();
addRefund.value.freeGold = selectedGoodsGold.value.freeGold.toString();
addRefund.value.taskGold = selectedGoodsGold.value.taskGold.toString();
}
}
const handleSelectionChange = (selectedOption) => {
if (selectedOption) {
//
addRefund.value.goodsName = selectedOption.value;
//
addRefund.value.permanentGold = selectedOption.permanentGold || 0;
addRefund.value.freeGold = selectedOption.freeGold || 0;
addRefund.value.taskGold = selectedOption.taskGold || 0;
//
selectedGoodsGold.value.permanentGold = Number(selectedOption.permanentGold) || 0;
selectedGoodsGold.value.freeGold = Number(selectedOption.freeGold) || 0;
selectedGoodsGold.value.taskGold = Number(selectedOption.taskGold) || 0;
} else {
//
}
console.log('选择的商品', selectedOption);
}
//
const handleGoldInput = (type, value) => {
// 1.
@ -464,37 +411,40 @@ const calculatedRechargeGoods = computed(() => {
watch(calculatedRechargeGoods, (newVal) => {
addRefund.value.sumGold = newVal
console.log('计算的总金币', newVal)
})
//
onMounted(async function () {
await getAdminData()
// await getRefundTypes()
// await getGoods()
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
getRefund()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
getRefund()
}
onMounted(() =>{
console.log(adminData.value)
})
</script>
<template>
<div>
<div class="father">
<div class="userAndForm">
<div class="left">
<el-form :model="addRefund" ref="Ref" :rules="rules" label-width="auto" label-position="right"
style="min-width: 420px" class="add-form">
<el-form-item prop="jwcode" label="精网号">
<el-input v-model="addRefund.jwcode" style="width: 220px" @change="getGoods(addRefund.jwcode)" />
<el-input v-model="addRefund.jwcode" style="width: 220px" />
<el-button type="primary" @click="getUser(addRefund.jwcode)" style="margin-left: 20px">查询
</el-button>
</el-form-item>
<el-form-item prop="refundType" label="退款类型">
<el-select v-model="addRefund.refundType" placeholder="请选择" style="width: 220px">
<el-select v-model="addRefund.refundType" placeholder="请选择" style="width: 220px"
@change="getRefund(addRefund.jwcode)">
<el-option v-for="item in refundType" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="orderCode" label="订单号">
<el-select v-model="addRefund.orderCode" placeholder="请选择订单号" style="width: 220px;" clearable filterable
:filter-method="handleFilter" @change="handleOrderChange">
@change="handleOrderChange">
<el-option v-for="(item, index) in orderCodes" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
@ -503,31 +453,33 @@ onMounted(async function () {
<el-input v-model="addRefund.goodsName" style="width: 220px" disabled />
</el-form-item>
<el-form-item prop="refundModel" label="退款方式:">
<el-radio-group v-model="addRe.typeR" @change="handleRefundModelChange">
<el-radio value="0">全部退款</el-radio>
<el-radio value="1">部分退款</el-radio>
<el-radio-group v-model="addRefund.refundModel" @change="handleRefundModelChange">
<el-radio :value="0">全部退款</el-radio>
<el-radio :value="1">部分退款</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="permanentGold" label="永久金币">
<el-input v-model="addRefund.permanentGold" style="width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handlePermanentGoldInput($event)" type="number">
<el-input v-model="addRefund.permanentGold" style="width: 220px" :disabled="addRefund.refundModel === 0"
@input="handlePermanentGoldInput($event)" type="number">
</el-input>&nbsp;&nbsp;
</el-form-item>
<template>
</template>
</el-form-item>
<el-form-item prop="freeGold" label="免费金币">
<el-input v-model="addRefund.freeGold" style="float: left; width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handleFreeGoldInput($event)" type="number" />
:disabled="addRefund.refundModel === 0" @input="handleFreeGoldInput($event)" type="number" />
&nbsp;&nbsp;
</el-form-item>
<div>
<el-form-item prop="taskGold" label="任务金币">
<el-input v-model="addRefund.taskGold" style="float: left; width: 220px"
:disabled="addRe.typeR === '0' ? true : false" @input="handleTaskGoldInput($event)" type="number" />
:disabled="addRefund.refundModel === 0" @input="handleTaskGoldInput($event)" type="number" />
&nbsp;&nbsp;
</el-form-item>
</div>
@ -538,7 +490,6 @@ onMounted(async function () {
</el-form-item>
</div>
<div>
<el-form-item prop="remark" label="备注">
<el-input v-model="addRefund.remark" style="width: 220px" :rows="3" maxlength="100" show-word-limit
type="textarea" />
@ -549,81 +500,104 @@ onMounted(async function () {
<el-button type="primary" :disabled="addDisabled" @click="addBefore"> 提交</el-button>
</el-form>
</div>
</div>
<div class="right">
<div class="right">
<div class="right-up">
<!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 历史金币 -->
<el-row style="margin-top: 20px">
<el-col :span="9">
<el-form-item label="姓名">
<p>{{ user.name }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前金币总数" style="width: 500px">
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{
user.nowSumGold
}}</span>
</el-form-item>
<!-- 金币详情独立显示 -->
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold
}};
免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span>
</el-form-item>
</el-col>
<el-card v-if="user.jwcode" class="customer-info">
<el-form :model="user" label-width="auto" label-position="left">
<span style="margin-left: 30%;font-size: larger;">客户信息</span>
<el-row style="margin-top: 1vh;">
<div class="line">
<span style="width:5vw;">姓名</span>
<span style="width:10vw;">{{ user.name }}</span>
<span style="width:10vw;">当前金币总数</span>
<span style="color: #2fa1ff;width: 25vw;" v-if="user.nowSumGold !== undefined">{{
user.nowSumGold
}}</span>
</div>
</el-row>
<el-row style="height: 3vh;width: 100%;">
<span style="height:3vh;color: #b1b1b1;font-size: small;margin-left:50%;"
v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold
}};
免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span>
</el-row>
<!-- 第二行精网号 + 当前金币独立行 -->
<el-row>
<el-col :span="9">
<el-form-item label="精网号">
<p>{{ user.jwcode }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="消费次数">
<p style="color: #2fa1ff">{{ user.consumeNum }} </p>
</el-form-item>
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<p style="font-size: small; color: #b1b1b1">(仅统计2025-01-01后的数据)</p>
</el-form-item>
</el-col>
<!-- 第二行精网号 + 消费次数 -->
<div class="line">
<span style="width:5vw;">精网号</span>
<span style="width: 10vw;">{{ user.jwcode }}</span>
<span style="width:10vw;">消费次数</span>
<span style="width: 10vw;color: #2fa1ff;">{{ user.consumeNum }}</span>
</div>
</el-row>
<el-row>
<span style="height:3vh;color: #b1b1b1;font-size: small;margin-left:50%;">(仅统计2025-01-01后的数据)</span>
</el-row>
<!-- 第三行首次充值日期 + 充值次数 -->
<!-- <el-row >
<el-col :span="9">
<el-form-item label="首次充值日期">
<p v-if="user.firstRecharge">
{{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
</p>
</el-form-item>
</el-col>
</el-row> -->
<!-- 第四行消费次数 + 所属门店 -->
<!-- 第四行所属门店 -->
<el-row>
<el-col :span="9">
<el-form-item label="所属门店">
<p>{{ user.market }}</p>
</el-form-item>
</el-col>
<div class="line">
<span style="width:5vw;">所属门店</span>
<span style="width: 10vw;">{{ user.market }}</span>
</div>
</el-row>
</el-form>
</el-card>
</div>
<div>
<el-card class="card" v-if="tableData.length > 0">
<el-table :data="tableData" style="height:43vh;width:50vw">
<el-table-column type="index" label="序号" width="80">
<template #default="scope">
<span>{{
scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="type" label="类型" width="100">
<template #default="{ row }">
{{ row.type === 0 ? '充值' : '消费' }}
</template>
</el-table-column>
<el-table-column prop="goodsName" label="商品名称" width="120" show-overflow-tooltip />
<el-table-column prop="orderCode" label="订单号" width="200px" show-overflow-tooltip />
<el-table-column prop="permanentGold" label="永久金币" width="120">
<template #default="{ row }">
{{ row.permanentGold }}
</template>
</el-table-column>
<el-table-column prop="freeGold" label="免费金币" width="120">
<template #default="{ row }">
{{ row.freeGold }}
</template>
</el-table-column>
<el-table-column prop="taskGold" label="任务金币" width="120">
<template #default="{ row }">
{{ row.taskGold }}
</template>
</el-table-column>
<el-table-column prop="isRefund" label="允许退款" width="120">
<template #default="{ row }">
{{ row.isRefund === 1 ? '否' : '是' }}
</template>
</el-table-column>
</el-table>
<el-pagination class="pagination" v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange"></el-pagination>
</el-card>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@ -631,35 +605,58 @@ p {
margin: 0px;
}
.father {
width: 75vw;
height: 70vh;
display: flex;
}
.userAndForm {
width: 1150px;
width: 25vw;
height: 100%;
display: flex;
align-items: center;
}
.left {
width: 35%;
display: flex;
.left {
width: 35vw;
display: flex;
.add-form {
width: 100%;
margin-top: 50px;
}
.add-form {
width: 100%;
margin-top: 30px;
}
}
.right {
flex: 1;
margin-left: 20px;
.right-up {
width: 50vw;
height: 20vh;
display: flex;
justify-content: center;
align-items: center;
margin-left: 5vw;
.customer-info {
height: 21vh;
padding-left: 5%;
display: flex;
align-items: center;
.customer-info {
width: 90%;
.line {
display: flex;
margin-left: 20px;
justify-content: center;
align-items: center;
padding: 0 10px;
height: 3vh;
}
}
}
.card {
width: 50vw;
height: 50vh;
margin-left: 5vw;
margin-top: 1vh;
}
.pagination {
display: flex;
margin-top: 0.5vh;
}
</style>

726
src/views/refund/gold/addNewCoinRefund.vue

@ -0,0 +1,726 @@
<script setup>
import {onMounted, reactive, ref, watch} from "vue";
import {ElMessage} from "element-plus";
import moment from "moment";
import request from "@/util/http.js";
import Cookies from 'js-cookie';
import {useAdminStore} from "@/store/index.js";
import {storeToRefs} from "pinia";
import {WarnTriangleFilled} from "@element-plus/icons-vue";
import dayjs from "dayjs";
const adminStore = useAdminStore();
const { adminData, menuTree } = storeToRefs(adminStore);
const addDisabled = ref(false);
const user = ref({
jwcode: null,
name: "",
market: "",
historySumGold: null,
historyPermanentGold: null,
historyFreeGold: null,
historyTaskGold: null,
rechargeNum: null,
consumeNum: null,
firstRecharge: "",
nowPermanentGold: null,
nowFreeJune: null,
nowTaskGold: null,
nowFreeDecember: null,
nowFreeGold: null,
nowSumGold: null
});
const addConsume = ref({
jwcode: null,
goodsName: "金币退款",
sumGold: null,
freeGold: null,
permanentGold: null,
taskGold: null,
remark: "",
refundModel: null,
adminId: adminData.value.id,
adminName: adminData.value.adminName
});
const Ref = ref(null);
const rules = reactive({
jwcode: [
{ required: true, message: "请输入精网号", trigger: "blur" },
],
remark: [
{ required: true, message: "请输入备注", trigger: "blur" },
],
permanentGold: [
{ required: true, message: "请输入永久金币数", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value === null || value === undefined || !Number.isInteger(value) || value < 0) {
callback(new Error("永久金币必须为非负整数"));
} else {
callback();
}
},
trigger: "blur"
}
],
freeGold: [
{ required: true, message: "请输入免费金币数", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value === null || value === undefined || !Number.isInteger(value) || value < 0) {
callback(new Error("免费金币必须为非负整数"));
} else {
callback();
}
},
trigger: "blur"
}
],
taskGold: [
{ required: true, message: "请输入任务金币数", trigger: "blur" },
{
validator: (rule, value, callback) => {
if (value === null || value === undefined || !Number.isInteger(value) || value < 0) {
callback(new Error("任务金币必须为非负整数"));
} else {
callback();
}
},
trigger: "blur"
}
],
sumGold: [
{
validator: (rule, value, callback) => {
if ((value || 0) <= 0) {
callback(new Error("三类金币总数必须大于0"));
} else {
callback();
}
},
trigger: "blur"
}
]
});
// watch
watch(
() => [addConsume.value.freeGold, addConsume.value.permanentGold, addConsume.value.taskGold],
([free, permanent, task]) => {
const sum = (parseInt(free) || 0) + (parseInt(permanent) || 0) + (parseInt(task) || 0);
addConsume.value.sumGold = sum;
},
{ deep: true }
);
// sumGold > 0
function validateInputToFreeGold() {
if (!user.value.jwcode) {
ElMessage.warning("请先查询用户信息");
resetForm()
return false;
}
const { freeGold, permanentGold, taskGold, sumGold } = addConsume.value;
if(freeGold > user.value.nowFreeGold){
ElMessage.warning("该用户免费金币不足,请重新输入!");
addConsume.value.freeGold = null;
return
}
return true;
}
// sumGold > 0
function validateInputToPermanentGold() {
if (!user.value.jwcode) {
ElMessage.warning("请先查询用户信息");
resetForm()
return false;
}
const { freeGold, permanentGold, taskGold, sumGold } = addConsume.value;
if(permanentGold > user.value.nowPermanentGold){
ElMessage.warning("该用户永久金币不足,请重新输入!");
addConsume.value.permanentGold = null;
return
}
return true;
}
// sumGold > 0
function validateInputToTaskGold() {
if (!user.value.jwcode) {
ElMessage.warning("请先查询用户信息");
resetForm()
return false;
}
const { freeGold, permanentGold, taskGold, sumGold } = addConsume.value;
if(taskGold > user.value.nowTaskGold){
ElMessage.warning("该用户任务金币不足,请重新输入!");
addConsume.value.taskGold = null;
return
}
return true;
}
// sumGold > 0
function validateInputTOSumGold() {
if (!user.value.jwcode) {
ElMessage.warning("请先查询用户信息");
resetForm()
return false;
}
const { freeGold, permanentGold, taskGold, sumGold } = addConsume.value;
if (sumGold <= 0) {
ElMessage.warning("三类金币总数必须大于0");
resetForm()
return false;
}
return true;
}
//
const ConsumeDialogVisible = ref(false);
//
const ConsumeDialogVisiblehandleClose = () => {
ConsumeDialogVisible.value = false;
//
resetForm()
user.value = {}
};
``
// 使cookie
const ConsumeDialogVisibleContinue = () => {
ConsumeDialogVisible.value = false;
add();
};
const ConsumeDialogVisibleCancel = () => {
ConsumeDialogVisible.value = false
resetForm()
user.value = {}
};
//
//
const FirstConsumeDialogVisible = ref(false);
//
const FirstConsumeDialogVisiblehandleClose = () => {
FirstConsumeDialogVisible.value = false;
//
resetForm()
user.value = {}
};
//
const FirstConsumeDialogVisibleContinue = () => {
FirstConsumeDialogVisible.value = false;
add();
};
const FirstConsumeDialogVisibleCancel = () => {
FirstConsumeDialogVisible.value = false
resetForm()
user.value = {}
};
//
const goods = ref([]);
// cookie key
const WriteCookies = ref(null)
// cookie value
const WriteCookiesTime = ref(null)
// cookie key
const ReadCookies = ref(null)
// cookie value
const ReadCookiesTime = ref(null)
//
const add = async function () {
addDisabled.value = true;
try {
const result = await request({
url: "/consume/addRefund",
data: {
jwcode: addConsume.value.jwcode,
adminId: adminData.value.id,
sumGold: addConsume.value.sumGold *100 ,
freeGold: addConsume.value.freeGold *100 ,
taskGold: addConsume.value.taskGold *100 ,
permanentGold: addConsume.value.permanentGold *100 ,
remark: addConsume.value.remark,
adminName: adminData.value.adminName
}
});
// jwcode
// jwcode:permanentGold:freeGold
WriteCookies.value = `coinRefund:${addConsume.value.jwcode}:${addConsume.value.goodsName}`
//value
WriteCookiesTime.value = dayjs().format("YYYY-MM-DD HH:mm:ss");
// cookies,jwcodekeyvaluejwcode1
Cookies.set(WriteCookies.value, WriteCookiesTime.value, {
expires:
1, path: '/'
});
addDisabled.value = false;
if (result.code === 200) {
ElMessage.success("添加成功");
resetForm();
} else {
ElMessage.error(result.msg || "添加失败");
}
} catch (err) {
console.error(err);
ElMessage.error("添加失败,请检查网络");
addDisabled.value = false;
}
};
//
function resetForm() {
Ref.value.resetFields();
addConsume.value.sumGold = null;
addConsume.value.freeGold = null;
addConsume.value.permanentGold = null;
addConsume.value.taskGold = null;
addConsume.value.sumGold = null;
addConsume.value.remark = "";
addConsume.value.jwcode = null;
user.value = {
jwcode: null,
name: "",
market: "",
historySumGold: null,
historyPermanentGold: null,
historyFreeGold: null,
historyTaskGold: null,
rechargeNum: null,
consumeNum: null,
firstRecharge: "",
nowPermanentGold: null,
nowFreeJune: null,
nowTaskGold: null,
nowFreeDecember: null,
nowFreeGold: null,
nowSumGold: null
};
}
//
const getGoods = async function () {
try {
const result = await request({ url: "https://api.homilychart.com/live_mall/api/product/all" });
goods.value = result.data.map(item => ({ id: item.id, label: item.name, value: item.name }));
} catch (err) {
console.error(err);
}
}
//
const addBefore = () => {
Ref.value.validate(async (valid) => {
// cookie
if (!valid) {
ElMessage({
type: 'error',
message: '请检查输入内容'
});
return;
}
// sumGold > 0
if (!validateInputTOSumGold()) return;
ReadCookies.value = `coinRefund:${addConsume.value.jwcode}:${addConsume.value.goodsName}`
// cookie
const cookie = Cookies.get(ReadCookies.value)
console.log("cookie++++++++++++++++++++++", cookie)
console.log("time", WriteCookiesTime.value)
//
ReadCookiesTime.value = moment(cookie).format('YYYY-MM-DD HH:mm:ss')
console.log("cookie========", cookie)
if (cookie) {
ConsumeDialogVisible.value = true;
} else {
FirstConsumeDialogVisible.value = true;
}
});
};
//
const getUser = async function (jwcode) {
if (!jwcode || !/^\d{1,9}$/.test(jwcode)) {
ElMessage.warning("精网号不能为空或格式不正确");
return;
}
try {
const result = await request({ url: "/user/selectUser", data: { jwcode } });
if (result.code === 200 && result.data) {
user.value = { ...result.data };
ElMessage.success("查询成功");
} else {
ElMessage.warning(result.msg || "用户不存在");
}
} catch (err) {
console.error(err);
ElMessage.error("查询失败,请检查网络或精网号");
}
};
onMounted(async function () {
await getGoods();
});
</script>
<template>
<div class="father1">
<div class="left">
<el-form :model="addConsume" ref="Ref" :rules="rules" style="min-width: 420px;" class="add-form"
label-width="auto" label-position="right">
<el-form-item prop="jwcode" label="精网号" style="margin-top: 50px">
<el-input v-model="addConsume.jwcode" style="width: 200px;" />
<el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 20px">查询
</el-button>
</el-form-item>
<el-form-item prop="goodsName" label="商品名称" >
<el-input v-model="addConsume.goodsName" disabled style="width: 120px" />
</el-form-item>
<!-- 三类金币可编辑 -->
<el-form-item prop="permanentGold" label="永久金币">
<el-input v-model.number="addConsume.permanentGold" style="width: 120px" @input="validateInputToPermanentGold()"/>
<p>&nbsp;&nbsp;</p>
</el-form-item>
<el-form-item prop="freeGold" label="免费金币">
<el-input v-model.number="addConsume.freeGold" style="width: 120px" @input="validateInputToFreeGold()" />
<p>&nbsp;&nbsp;</p>
</el-form-item>
<el-form-item prop="taskGold" label="任务金币">
<el-input v-model.number="addConsume.taskGold" style="width: 120px" @input="validateInputToTaskGold()" />
<p>&nbsp;&nbsp;</p>
</el-form-item>
<el-form-item prop="sumGold" label="消耗金币总数">
<el-input v-model.number="addConsume.sumGold" style="width: 120px" disabled />
</el-form-item>
<el-form-item prop="remark" label="备注">
<el-input v-model="addConsume.remark" style="width: 250px" :rows="4" maxlength="100" show-word-limit
type="textarea" />
</el-form-item>
<el-button type="success" @click="resetForm()" style="margin-left: 200px;margin-top:10px">重置</el-button>
<el-button type="primary" :disabled="addDisabled" @click="addBefore" style="margin-top:10px"> 提交</el-button>
</el-form>
</div>
<div class="right">
<!-- 客户信息栏 -->
<el-card v-if="user.jwcode" style="width: 800px; float: right" class="customer-info">
<el-form :model="user" label-width="auto" style="max-width: 1000px" label-position="left">
<el-text size="large" style="margin-left: 20px">客户信息</el-text>
<!-- 第一行姓名 + 历史金币 -->
<el-row style="margin-top: 20px">
<el-col :span="9">
<el-form-item label="姓名">
<p>{{ user.name }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="当前金币总数" style="width: 500px">
<span style="color: #2fa1ff; margin-right: 5px" v-if="user.nowSumGold !== undefined">{{
user.nowSumGold
}}</span>
</el-form-item>
<!-- 金币详情独立显示 -->
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<span style="color: #b1b1b1; margin-left: 0px" v-if="user.nowPermanentGold !== undefined">(永久金币:{{
user.nowPermanentGold
}};
免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行精网号 + 当前金币独立行 -->
<el-row>
<el-col :span="9">
<el-form-item label="精网号">
<p>{{ user.jwcode }}</p>
</el-form-item>
</el-col>
<el-col :span="14">
<el-form-item label="消费次数">
<p style="color: #2fa1ff">{{ user.consumeNum }} </p>
</el-form-item>
<el-form-item style="margin-top: -23px"> <!-- 负边距减少间距 -->
<p style="font-size: small; color: #b1b1b1">(仅统计2025-01-01后的数据)</p>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行首次充值日期 + 充值次数 -->
<!-- <el-row >
<el-col :span="9">
<el-form-item label="首次充值日期">
<p v-if="user.firstRecharge">
{{ moment(user.firstRecharge).format('YYYY-MM-DD HH:mm:ss') }}
</p>
</el-form-item>
</el-col>
</el-row> -->
<!-- 第四行消费次数 + 所属门店 -->
<el-row>
<el-col :span="9">
<el-form-item label="所属门店">
<p>{{ user.market }}</p>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<el-dialog v-model="FirstConsumeDialogVisible" title="操作确认" :before-close="FirstConsumeDialogVisiblehandleClose"
:close-on-click-modal="false" width="480px">
<!-- 内容整体居中且收窄 -->
<div class="confirm-body">
<!-- 用户信息 -->
<div>
<div class="field-label">用户信息</div>
<el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
</div>
<!-- 活动名称 -->
<div class="field">
<div class="field-label">商品名称</div>
<el-input v-model="addConsume.goodsName" disabled />
</div>
<!--金币总数 -->
<div class="field">
<div class="field-label">金币总数</div>
<el-input v-model="addConsume.sumGold" disabled />
</div>
<!-- 金币详细信息同一行左右排列 -->
<el-row :gutter="20" class="coins-row">
<el-col :span="8">
<div class="field">
<div class="field-label">永久金币</div>
<el-input v-model="addConsume.permanentGold" disabled />
</div>
</el-col>
<el-col :span="8">
<div class="field">
<div class="field-label">免费金币</div>
<el-input v-model="addConsume.freeGold" disabled />
</div>
</el-col>
<el-col :span="8">
<div class="field">
<div class="field-label">任务金币</div>
<el-input v-model="addConsume.taskGold" disabled />
</div>
</el-col>
</el-row>
<div class="field">
<div class="field-label">备注</div>
<el-input v-model="addConsume.remark" disabled />
</div>
</div>
<!-- 底部按钮居中 -->
<template #footer>
<div class="dialog-footer-center">
<el-button @click="FirstConsumeDialogVisibleCancel"> </el-button>
<el-button type="primary" @click="FirstConsumeDialogVisibleContinue">确认退款</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="ConsumeDialogVisible" title="操作确认" :before-close="ConsumeDialogVisiblehandleClose"
:close-on-click-modal="false" width="480px">
<!-- 内容整体居中且收窄 -->
<div class="confirm-body">
<!-- 用户信息 -->
<div>
<div class="field-label">用户信息</div>
<el-input :model-value="user.jwcode + (user.name ? '' + user.name + '' : '')" disabled />
</div>
<!-- 活动名称 -->
<div class="field">
<div class="field-label">商品名称</div>
<el-input v-model="addConsume.goodsName" disabled />
</div>
<!--金币总数 -->
<div class="field">
<div class="field-label">金币总数</div>
<el-input v-model="addConsume.sumGold" disabled />
</div>
<!-- 金币详细信息同一行左右排列 -->
<el-row :gutter="20" class="coins-row">
<el-col :span="8">
<div class="field">
<div class="field-label">永久金币</div>
<el-input v-model="addConsume.permanentGold" disabled />
</div>
</el-col>
<el-col :span="8">
<div class="field">
<div class="field-label">免费金币</div>
<el-input v-model="addConsume.freeGold" disabled />
</div>
</el-col>
<el-col :span="8">
<div class="field">
<div class="field-label">任务金币</div>
<el-input v-model="addConsume.taskGold" disabled />
</div>
</el-col>
</el-row>
<!-- 风险提示 -->
<div style="display: flex; align-items: center; margin-top: 20px;">
<el-icon :size="24" color="#FFD700">
<WarnTriangleFilled />
</el-icon>
<p>重复购买风险提示</p>
</div>
<!-- 记录 + 虚线分隔 -->
<div>
<el-divider border-style="dashed" />
<p>检测到该用户近期有相似退款记录</p>
· {{ ReadCookiesTime }} 退款 {{ addConsume.goodsName }}(操作人: {{ adminData.adminName }})
</div>
<div style="margin-top: 10px">
<p>是否继续操作</p>
</div>
</div>
<!-- 底部按钮居中 -->
<template #footer>
<div class="dialog-footer-center">
<el-button @click="ConsumeDialogVisibleCancel"> </el-button>
<el-button type="primary" @click="ConsumeDialogVisibleContinue">确认退款</el-button>
</div>
</template>
</el-dialog>
</div>
</div>
</template>
<style scoped lang="scss">
p {
margin: 0px;
}
/* 上传图片的格式 */
.avatar-uploader .avatar {
width: 50px;
height: 50px;
display: block;
}
.add-form {
width: 400px;
float: left;
}
/* 标题居中 */
.el-dialog__header {
text-align: center;
}
.confirm-body {
width: 350px;
margin: 0 auto;
}
/* 字段块与标签样式 */
.field {
margin-bottom: 14px;
}
.field-label {
font-size: 14px;
color: #606266;
margin-bottom: 6px;
}
/* 金币行紧凑 */
.coins-row .field {
margin-bottom: 0;
}
/* 底部按钮居中 */
.dialog-footer-center {
display: flex;
justify-content: center;
gap: 12px;
}
.father1 {
width: 82vw;
height: 80vh;
display: flex;
.left {
width: 500px;
float: left;
display: flex;
}
.right {
flex: 1;
height: 50vh;
display: flex;
align-items: center;
.customer-info {
width: 300px;
margin-left: 20px;
display: flex;
justify-content: center;
align-items: center;
}
}
}
</style>

26
src/views/refund/gold/coinRefund.vue

@ -1,24 +1,20 @@
<template>
<div>
<div class="father">
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
<el-button
:type="activeTab === 'addCoinRefund' ? 'primary' : 'default'"
@click="navigateTo('addCoinRefund')"
:disabled="!hasAdd"
>
<el-button :type="activeTab === 'addCoinRefund' ? 'primary' : 'default'" @click="navigateTo('addCoinRefund')"
:disabled="!hasAdd">
新增退款
</el-button>
<el-button
:type="activeTab === 'coinRefundDetail' ? 'primary' : 'default'"
@click="navigateTo('coinRefundDetail')"
:disabled="!hasDetail"
>
<el-button :type="activeTab === 'coinRefundDetail' ? 'primary' : 'default'"
@click="navigateTo('coinRefundDetail')" :disabled="!hasDetail">
金币退款明细
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
</div>
<div>
<router-view></router-view>
</div>
</template>
@ -86,4 +82,10 @@ onMounted(() => {
}
}
});
</script>
</script>
<style lang="css">
.father {
width: 82vw;
height: 4vh;
}
</style>

232
src/views/refund/gold/coinRefundDetail.vue

@ -164,8 +164,9 @@ const getSelectBy = async function (val) {
}
})
// refundUser.value
const detailWithoutSort = { ...refundUser.value,
flag: showEmployeeData.value ? 0 : 1
const detailWithoutSort = {
...refundUser.value,
flag: showEmployeeData.value ? 0 : 1
}
delete detailWithoutSort.sortField
delete detailWithoutSort.sortOrder
@ -190,13 +191,13 @@ const getSelectBy = async function (val) {
//
tableData.value = result.data.list
tableData.value = tableData.value.map(item => ({
...item,
sumGold: (Number(item.sumGold) || 0) ,
permanentGold: (Number(item.permanentGold) || 0) ,
freeGold: (Number(item.freeGold) || 0) ,
taskGold: (Number(item.taskGold) || 0)
sumGold: (Number(item.sumGold) || 0),
permanentGold: (Number(item.permanentGold) || 0),
freeGold: (Number(item.freeGold) || 0),
taskGold: (Number(item.taskGold) || 0)
}))
console.log('tableData', tableData.value)
//
@ -515,119 +516,110 @@ const getMarket = async function () {
</script>
<template>
<el-card style="margin-bottom: 20px;margin-top:10px">
<el-row style="margin-bottom: 10px">
<el-col :span="5">
<div class="head-card-element">
<el-text>精网号</el-text>
<el-input v-model="refundUser.jwcode" placeholder="请输入精网号" style="width: 150px" clearable />
</div>
</el-col>
<el-col :span="5">
<div class="head-card-element">
<el-text>商品名称</el-text>
<el-select v-model="refundUser.goodsName" placeholder="请选择商品名称" style="width: 180px" clearable>
<el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</el-col>
<el-col :span="5">
<el-text size="large">所属地区</el-text>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable
style="width:180px" @change="handleMarketChange" />
</el-col>
<el-col :span="5">
<div class="head-card-element">
<el-text>退款类型</el-text>
<el-select v-model="refundUser.refundType" placeholder="请选择退款类型" style="width: 180px" clearable>
<!-- todo 这需要改-->
<el-option v-for="item in refundType" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</el-col>
<el-col :span="3">
<div class="head-card-element">
<el-checkbox v-model="showEmployeeData" @change="search()">员工数据</el-checkbox>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="head-card-element">
<el-text>退款时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 400px" @change="handleDatePickerChange"
:default-time="defaultTime" :disabled-date="disabledDate"/>
<el-button @click="getToday()" style="margin-left: 10px"
:type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px"
:type="activeTimeRange === '7days' ? 'primary' : ''"> 近7天
</el-button>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</div>
</el-col>
</el-row>
</el-card>
<el-card>
<el-card style="margin-bottom: 1vh;">
<div class="condition">
<div style="display: flex;align-items: center;width:18vw">
<el-text>精网号</el-text>
<el-input v-model="refundUser.jwcode" placeholder="请输入精网号" style="width: 10vw;" clearable />
</div>
<div style="display: flex;align-items: center;width:18vw">
<el-text>商品名称</el-text>
<el-select v-model="refundUser.goodsName" placeholder="请选择商品名称" style="width: 10vw;" clearable filterable>
<el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div style="display: flex;align-items: center;width:18vw">
<el-text size="large">所属地区</el-text>
<el-cascader v-model="selectedMarketPath" :options="market" placeholder="请选择所属地区" clearable style="width:10vw"
@change="handleMarketChange" />
</div>
<div style="display: flex;align-items: center;width:18vw">
<el-text>退款类型</el-text>
<el-select v-model="refundUser.refundType" placeholder="请选择退款类型" style="width: 10vw" clearable>
<el-option v-for="item in refundType" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div>
<el-checkbox v-model="showEmployeeData" @change="search()">员工数据</el-checkbox>
</div>
</div>
<div>
<el-col :span="24">
<div>
退款金币总数{{ format3(Math.abs(sumGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(Math.abs(permanentGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(Math.abs(freeGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(Math.abs(taskGolds).toFixed(2)) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 520px; overflow-y: auto;margin-top:10px">
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="520px">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" fixed="left" width="130px" show-overflow-tooltip />
<el-table-column prop="jwcode" label="精网号" fixed="left" width="110px" />
<el-table-column prop="market" label="所属地区" width="110px" />
<el-table-column prop="orderCode" label="订单号" width="260px" show-overflow-tooltip />
<el-table-column prop="goodsName" label="商品名称" width="110px" show-overflow-tooltip />
<el-table-column prop="refundType" label="退款类型" width="100px" />
<el-table-column prop="sumGold" label="退款金币总数" width="150px" sortable="custom" />
<el-table-column prop="refundModel" label="退款方式" width="110px">
<template #default="scope">
{{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
</template>
</el-table-column>
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom" />
<el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110px" />
<el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110px" />
<el-table-column prop="remark" label="退款原因" width="160px" show-overflow-tooltip />
<el-table-column prop="adminName" label="提交人" width="100px" />
<el-table-column prop="auditTime" sortable="custom" label="退款时间" width="180px">
<template #default="scope">
{{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<el-text>退款时间</el-text>
<el-date-picker v-model="getTime" type="datetimerange" range-separator="" start-placeholder="起始时间"
end-placeholder="结束时间" style="width: 20vw;" @change="handleDatePickerChange" :default-time="defaultTime"
:disabled-date="disabledDate" />
<el-button @click="getToday()" style="margin-left: 10px" :type="activeTimeRange === 'today' ? 'primary' : ''">
</el-button>
<el-button @click="getYesterday()" style="margin-left: 10px"
:type="activeTimeRange === 'yesterday' ? 'primary' : ''">
</el-button>
<el-button @click="get7Days()" style="margin-left: 10px" :type="activeTimeRange === '7days' ? 'primary' : ''">
近7天
</el-button>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px;display: flex;">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange" @jump="checkPageNumber"></el-pagination>
<el-button type="success" @click="reset()">重置</el-button>
<el-button type="primary" @click="search()">查询</el-button>
<el-button type="primary" @click="exportExcel">导出Excel</el-button>
<el-button type="primary" @click="openExportList">查看导出列表</el-button>
</div>
</el-card>
</el-col>
</div>
</el-card>
<el-card>
<div>
退款金币总数{{ format3(Math.abs(sumGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(Math.abs(permanentGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(Math.abs(freeGolds).toFixed(2)) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(Math.abs(taskGolds).toFixed(2)) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 58vh; overflow-y: auto;">
<el-table :data="tableData" style="width: 82vw;" @sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="80px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" fixed="left" width="130px" show-overflow-tooltip />
<el-table-column prop="jwcode" label="精网号" fixed="left" width="110px" />
<el-table-column prop="market" label="所属地区" width="110px" />
<el-table-column prop="orderCode" label="订单号" width="260px" show-overflow-tooltip />
<el-table-column prop="goodsName" label="商品名称" width="110px" show-overflow-tooltip />
<el-table-column prop="refundType" label="退款类型" width="100px" />
<el-table-column prop="refundModel" label="退款方式" width="110px">
<template #default="scope">
{{ scope.row.refundModel === 0 ? '全部退款' : scope.row.refundModel === 1 ? '部分退款' : '' }}
</template>
</el-table-column>
<el-table-column prop="sumGold" label="退款金币总数" width="150px" sortable="custom" />
<el-table-column prop="permanentGold" label="永久金币" width="110px" sortable="custom" />
<el-table-column prop="freeGold" sortable="custom" label="免费金币" width="110px" />
<el-table-column prop="taskGold" sortable="custom" label="任务金币" width="110px" />
<el-table-column prop="remark" label="退款原因" width="160px" show-overflow-tooltip />
<el-table-column prop="adminName" label="提交人" width="100px" />
<el-table-column prop="auditTime" sortable="custom" label="退款时间" width="180px">
<template #default="scope">
{{ moment(scope.row.auditTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px;display: flex;">
<el-pagination background :page-size="getObj.pageSize" :page-sizes="[5, 10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handlePageSizeChange"
@current-change="handleCurrentChange" @jump="checkPageNumber"></el-pagination>
</div>
</el-card>
<!-- 导出弹窗 -->
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
@ -663,7 +655,9 @@ const getMarket = async function () {
</template>
<style scoped>
.head-card-element {
margin-right: 20px;
.condition{
display: flex;
width:82vw;
margin-bottom: 0.5vh;
}
</style>

9
src/views/usergold/bean/userbean.vue

@ -1,5 +1,5 @@
<template>
<el-card style="margin-bottom: 20px;">
<el-card style="margin-bottom: 1vh;">
<el-text size="large">精网号</el-text>
<el-input v-model="searchObj.jwcode" placeholder="请输入精网号" style="width: 240px" clearable />
<el-text size="large" style="margin-left:20px">地区</el-text>
@ -10,7 +10,6 @@
<div style="float: right;">
<el-button type="success" @click="reset">重置</el-button>
<el-button type="primary" @click="search">查询</el-button>
</div>
</el-card>
@ -21,7 +20,7 @@
免费金豆数{{ format3(stats.freeBean) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
消费金豆总数{{ format3(stats.consumeSum) }}金豆&nbsp;&nbsp;&nbsp;&nbsp;
</div>
<el-table :data="tableData" height="650px" @sort-change="handleSortChange" :row-style="{ height: '60px' }">
<el-table :data="tableData" height="65vh" @sort-change="handleSortChange" :row-style="{ height: '60px' }">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{
@ -197,8 +196,4 @@ onMounted(() => {
</script>
<style scoped>
/* .stats-card {
background-color: #EBEEF5;
padding: 5px;
} */
</style>

12
src/views/usergold/gold/clientCount.vue

@ -1,5 +1,5 @@
<template>
<div>
<div class="father">
<!-- 这里放置标签切换的按钮 -->
<el-button-group>
<!-- 切换后状态显示 primary 样式否则是默认样式 -->
@ -19,6 +19,8 @@
</el-button>
</el-button-group>
<!-- 渲染子路由组件 -->
</div>
<div>
<router-view></router-view>
</div>
</template>
@ -86,4 +88,10 @@ onMounted(() => {
}
}
});
</script>
</script>
<style>
.father{
width:82vw;
height:4vh;
}
</style>

16
src/views/usergold/gold/clientCountBalance.vue

@ -386,9 +386,7 @@ const format3 = (num) => {
</script>
<template>
<el-row>
<el-col>
<el-card style="margin-bottom: 20px;margin-top: 10px">
<el-card style="margin-bottom: 1vh;">
<div class="head-card">
<div class="head-card-element">
<el-text class="mx-1" size="large">精网号</el-text>
@ -415,10 +413,6 @@ const format3 = (num) => {
</div>
<!-- </div> -->
</el-card>
</el-col>
</el-row>
<el-row>
<el-col>
<el-card>
<div>
金币总数{{ format3(goldtotal || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
@ -427,8 +421,8 @@ const format3 = (num) => {
任务金币{{ format3(taskGold || 0) }}
</div>
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 626px; overflow-y: auto">
<el-table :data="tableData" @cellClick="cellClick" style="width: 100%" height="626px"
<div style="height: 60vh; overflow-y: auto">
<el-table :data="tableData" @cellClick="cellClick" style="width: 82vw; height:60vh"
@sort-change="handleSortChange">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
@ -450,7 +444,7 @@ const format3 = (num) => {
}}</span>
</template> -->
</el-table-column>
<el-table-column prop="currentPermanentGold" label="永久金币" sortable="custom" width="160">
<el-table-column prop="currentPermanentGold" label="永久金币" sortable="custom" width="150">
<template #default="scope">
<span>{{ (scope.row.currentPermanentGold || 0) }}</span>
</template>
@ -527,8 +521,6 @@ const format3 = (num) => {
@current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<el-dialog v-model="exportListVisible" title="导出列表" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" label="文件名"/>

10
src/views/usergold/gold/clientCountDetail.vue

@ -312,7 +312,7 @@ const handleSortChange = (column) => {
} else if (column.prop === 'taskGold') {
sortField.value = 'task_gold'
} else if (column.prop === 'freeGold') {
sortField.value = 'free_gold'
sortField.value = 'freeGold'
} else if (column.prop === 'auditTime') {//creatTime
sortField.value = 'audit_time'
}
@ -493,7 +493,7 @@ const format3 = (num) => {
</script>
<template>
<div>
<el-card style="margin-bottom: 20px;margin-top: 10px;">
<el-card style="margin-bottom: 1vh;">
<el-row style="margin-bottom: 10px">
<el-col :span="5">
<div class="head-card-element">
@ -565,10 +565,10 @@ const format3 = (num) => {
永久金币{{ format3(totalPermanentGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(totalFreeGold || 0) }}&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(totalTaskGold || 0) }}&nbsp;&nbsp;
<a style="color:#b1b1b1;">(当前合计仅显示筛选范围内的金币净变化充值总额 - 消费总额 + 退款总额)</a>
<a style="color:#b1b1b1; font-size: small;">当前合计仅显示筛选范围内的金币净变化充值总额 - 消费总额 + 退款总额)</a>
</div>
<div style="height: 584px; overflow-y: auto">
<el-table :data="tableData" style="width: 100%" @sort-change="handleSortChange" height="584px">
<div style="height: 55vh; overflow-y: auto">
<el-table :data="tableData" style="width: 82vw" @sort-change="handleSortChange" height="55vh">
<el-table-column type="index" label="序号" width="100px" fixed="left">
<template #default="scope">
<span>{{

80
src/views/workspace/index.vue

@ -1,6 +1,6 @@
<template>
<div class="top">
<el-card style="width:20%" class="center-card">数据总览</el-card>
<el-card style="width:10vw" class="center-card">数据总览</el-card>
<span class="text">
最后更新时间{{
workDataUpdateTime && workDataUpdateTime !== '1970-01-01 08:00:00' ? workDataUpdateTime : '该地区暂无数据'
@ -15,7 +15,7 @@
<div class="card-title">当前金币余量</div>
<div>
<span style="font-weight: bold">{{ currentGold / 100
}}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;较前一日
}}</span>&nbsp;&nbsp;&nbsp;&nbsp;较前一日
{{ dailyChange / 100 }}
<template v-if="dailyChange > 0">
<el-icon style="color:red">
@ -60,7 +60,7 @@
<el-card class="card-item">
<div class="card-title">全年累计消费金币数</div>
<div class="card-title">{{ yearlyReduce / 100 }}</div>
<div style="padding-left: 30%;">{{ yearlyConsume / 100 }}</div>
<div style="padding-left: 30%;">{{ yearlyConsume / 100 }}</div>
<div style="padding-left: 30%;">退款{{ yearlyRefund / 100 }}</div>
<template #footer>
<div style="margin-bottom:0.5%;padding-left: 30%;">昨日新增消费{{ dailyConsume / 100 }}</div>
@ -115,7 +115,7 @@
</div>
<div class="graph">
<el-card style="width:100%;">
<el-card style="width:84vw;">
<div>
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane label="金币充值" name="recharge"></el-tab-pane>
@ -123,29 +123,30 @@
</el-tabs>
</div>
<div class="condition">
<div style="display:flex;">合计&nbsp;&nbsp;
<span>永久金币</span> {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }}&nbsp;&nbsp;
<span>免费金币</span> {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }}&nbsp;&nbsp;
<span>任务金币</span> {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }}&nbsp;&nbsp;
<div v-if="activeTab === 'consume'">合计 {{ sumConsume / 100 }}</div>
<div class="stats">
<div v-if="activeTab === 'consume'">合计{{ sumConsume / 100 }}</div>&nbsp;&nbsp;
永久金币: {{ activeTab === 'recharge' ? sumRechargePermanent / 100 : sumConsumePermanent / 100 }}&nbsp;&nbsp;
免费金币: {{ activeTab === 'recharge' ? sumRechargeFree / 100 : sumConsumeFree / 100 }}&nbsp;&nbsp;
任务金币: {{ activeTab === 'recharge' ? sumRechargeTask / 100 : sumConsumeTask / 100 }}&nbsp;&nbsp;
</div>
<div style="margin-left:auto;margin-right: 0.5%;">
<el-button @click="getYes()" :type="activeTimeRange === 'yes' ? 'primary' : ''">昨天
<div>
<el-button @click="getYes()" size="small" :type="activeTimeRange === 'yes' ? 'primary' : ''">昨天
</el-button>
<el-button @click="getToday()" :type="activeTimeRange === 'today' ? 'primary' : ''">今天
<el-button @click="getToday()" size="small" :type="activeTimeRange === 'today' ? 'primary' : ''">今天
</el-button>
<el-button @click="getWeek()" :type="activeTimeRange === 'week' ? 'primary' : ''">本周
<el-button @click="getWeek()" size="small" :type="activeTimeRange === 'week' ? 'primary' : ''">本周
</el-button>
<el-button @click="getMonth()" :type="activeTimeRange === 'month' ? 'primary' : ''">本月
<el-button @click="getMonth()" size="small" :type="activeTimeRange === 'month' ? 'primary' : ''">本月
</el-button>
<el-button @click="getYear()" :type="activeTimeRange === 'year' ? 'primary' : ''">本年
<el-button @click="getYear()" size="small" :type="activeTimeRange === 'year' ? 'primary' : ''">本年
</el-button>
</div>
<div>
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss" style="width:20vw" value-format="YYYY-MM-DD HH:mm:ss"
:default-time="defaultTime" :disabled-date="disabledDate" @change="handleDatePickerChange"/>
<el-button type="primary" style="margin-left: 5px" @click="getChartData">查询</el-button>
<el-date-picker size="small" v-model="dateRange" type="datetimerange" range-separator=""
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss"
style="width:20vw;margin-left:0.5vw;" value-format="YYYY-MM-DD HH:mm:ss" :default-time="defaultTime"
:disabled-date="disabledDate" @change="handleDatePickerChange" />
<el-button type="primary" size="small" style="margin-left: 0.5vw" @click="getChartData">查询</el-button>
</div>
</div>
@ -701,7 +702,7 @@ const updateChart = (chartData) => {
grid: {
left: '3%',
right: '4%',
bottom: '15%',
bottom: '10%',
containLabel: true
},
xAxis: {
@ -805,23 +806,25 @@ onUnmounted(() => {
})
</script>
<style scoped>
<style scoped lang="scss">
.top {
height: 5.5%;
width:100%;
height: 5.5vh;
width: 80vw;
display: flex;
margin-bottom: 0.2%;
.text{
margin-left:0.2%;
width:50%;
display: flex;
align-items: center;
margin-bottom: 0.5vh;
.text {
margin-left: 2vw;
width: 20vw;
display: flex;
align-items: center;
font-size: 18px;
}
}
.card {
height: 28%;
height: 28vh;
margin-bottom: 0.5vh;
display: flex;
justify-content: center;
}
@ -836,6 +839,13 @@ onUnmounted(() => {
height: 1%;
display: flex;
align-items: center;
.stats {
display: flex;
align-items: center;
width: 35vw;
font-size: 15px;
}
}
.graph-content {
@ -850,7 +860,7 @@ onUnmounted(() => {
.right {
flex: 1;
padding: 0.5% 2%;
padding: 0.5vw 2vh;
}
}
}
@ -862,21 +872,21 @@ onUnmounted(() => {
}
.margin-bottom {
margin-bottom: 0.5%;
margin-bottom: 0.5vh;
}
.card-item {
width: 25%;
height: 98%;
height: 28vh;
display: flex;
flex-direction: column;
justify-content: center;
margin-right: 0.25%;
margin-right: 0.25vw;
}
.card-title {
font-weight: bold;
margin-bottom: 10px;
margin-bottom: 1vh;
display: flex;
justify-content: center;
align-items: center;

2
stats.html
File diff suppressed because it is too large
View File

Loading…
Cancel
Save