Browse Source

Merge branch 'refs/heads/lihuilin/feature-20250815155204-金币优化' into milestone-20250815-金币优化

# Conflicts:
#	src/views/consume/gold/addCoinConsume.vue
youhua/chongzhi
lihui 2 months ago
parent
commit
e82cff9ece
  1. 4
      .env.development
  2. 6
      src/router/index.js
  3. 5
      src/utils/menuTreePermission.js
  4. 3
      src/utils/menuUtils.js
  5. 1
      src/views/audit/gold/rechargeAudit.vue
  6. 7
      src/views/audit/gold/refundAudit.vue
  7. 250
      src/views/consume/gold/addCoinConsume.vue
  8. 144
      src/views/history/history.vue
  9. 17
      src/views/usergold/bean/userbean.vue
  10. 3
      stats.html

4
.env.development

@ -1,4 +1,4 @@
VITE_API_BASE='https://hwjb.homilychart.com/dev/admin'
# VITE_API_BASE='https://hwjb.homilychart.com/dev/admin'
# 测试环境
# VITE_API_BASE='http://54.255.212.181:10704/'
# 正式环境
@ -11,6 +11,6 @@ VITE_UPLOAD_URL=http://39.101.133.168:8828/hljw/api/aws/upload
# zhangyong
# VITE_API_BASE='http://192.168.3.83:8081/'
# 本地
# VITE_API_BASE='http://localhost:8081/'
VITE_API_BASE='http://localhost:8081/'
# sunjiabei
# VITE_API_BASE='http://192.168.0.113:8081/'

6
src/router/index.js

@ -240,6 +240,12 @@ const routes = [
component: () => import("../views/usergold/bean/userbean.vue"),
meta:{ permissionId: 45 }
},
{
path: '/history',
name: "history",
component: () => import("../views/history/history.vue"),
meta:{ permissionId: [56,57] }
},
// 权限管理
{

5
src/utils/menuTreePermission.js

@ -10,6 +10,8 @@ export const permissionMapping = {
Consumption_Management: 6, // 消耗管理
Refund_Management: 7, // 退款管理
Customer_Account_Details: 8, // 客户账户明细
History: 56, // 历史数据查询
Permission_Management: 9, // 权限管理
@ -61,6 +63,9 @@ export const permissionMapping = {
Recharge_Audit: 31, // 充值审核(金币) // audit页面
Refund_Audit: 32, // 退款审核(金币) // audit页面,
History_Query: 57, // 历史数据查询
// 新增的金豆相关权限
Submit_Golden_Bean_Recharge: 46, // 提交金豆充值
View_Golden_Bean_System_Recharge_Details: 47, // 查看金豆系统充值明细

3
src/utils/menuUtils.js

@ -61,6 +61,9 @@ export const getRoutePath = (menu) => {
'金币退款': '/coinRefund',
// '金豆退款': '/beanRefund',
'历史数据查询': '/history',
'权限管理': '/permissions',

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

@ -64,7 +64,6 @@
<el-table-column prop="market" label="所属地区" width="100px"/>
<el-table-column prop="activity" label="活动名称" width="100px" show-overflow-tooltip/>
<el-table-column prop="rateName" label="货币名称" width="110px"/>
<el-table-column prop="money" sortable="custom" label="充值金额" width="110px"/>
<el-table-column prop="money" label="充值金额" sortable="custom" width="110px">
<template #default="scope">{{ scope.row.permanentGold / 100 }}</template>
</el-table-column>

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

@ -46,8 +46,8 @@
<div>
总条数{{ format3(stats.totalNum) }}&nbsp;&nbsp;&nbsp;&nbsp;
退款总金币数{{ format3(stats.permanentGolds + stats.freeGolds + stats.taskGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(stats.permanentGolds) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
退款总金币数{{ format3((stats.permanentGolds + stats.freeGolds + stats.taskGolds).toFixed(2)) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
永久金币{{ format3(stats.permanentGolds.toFixed(2)) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
免费金币{{ format3(stats.freeGolds.toFixed(2)) }}金币&nbsp;&nbsp;&nbsp;&nbsp;
任务金币{{ format3(stats.taskGolds.toFixed(2)) }}金币
</div>
@ -101,7 +101,7 @@
</template>
</el-table-column>
<el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180" sortable="custom">
<el-table-column v-if="checkTab !== 'pending'" prop="auditTime" label="审核时间" width="180">
<template #default="{ row }">
{{ row.auditTime ? moment(row.auditTime).format('YYYY-MM-DD HH:mm:ss') : '--' }}
</template>
@ -303,7 +303,6 @@ const get = async function (val) {
//
const handleApprove = async (row) => {
if (findMenuById(menuTree.value, permissionMapping.Refund_Approval)) {
try {
const params = {
orderCode: row.orderCode,

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

@ -1,12 +1,14 @@
<script setup>
import { onMounted, reactive, ref, watch } from "vue";
import {ElIcon, ElMessage, ElMessageBox} from "element-plus";
import {Plus, WarnTriangleFilled} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from "element-plus";
import moment from "moment";
import request from "@/util/http.js"
import Cookies from 'js-cookie';
import dayjs from "dayjs";
import { useAdminStore } from "@/store/index.js";
import { storeToRefs } from "pinia";
const adminStore = useAdminStore();
const { adminData, menuTree } = storeToRefs(adminStore);
//
const trimJwCode = () => {
if (addConsume.value.jwcode) {
@ -29,6 +31,7 @@ const addDisabled = ref(false)
*/
//
const adminData = ref({});
//
const user = ref({
jwcode: null,
@ -181,6 +184,7 @@ function validateInput() {
}
//
const totalAvailableGold = (user.value.nowSumGold)
if (user.value.jwcode && sumGold > totalAvailableGold) {
@ -230,15 +234,6 @@ function calculateCoins(sumGold) {
return { free: freeUsed, permanent: permanentUsed, task: taskUsed };
}
// 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 () {
try {
@ -250,16 +245,7 @@ const add = async function () {
calculateCoins(addConsume.value.sumGold);
console.log("addConsume.value", addConsume.value)
// jwcode
// jwcode:permanentGold:freeGold
WriteCookies.value = `coinConsume:${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: '/'});
// POST
addDisabled.value = true
const result = await request({
// url: "/consume/add",
url: "/consume/add",
@ -275,7 +261,7 @@ const add = async function () {
remark: addConsume.value.remark
}
});
addDisabled.value = false
console.log("add请求", result);
//
handleResponse(result);
@ -336,65 +322,29 @@ function resetForm() {
}
}
//
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 proceedWithConsume = () => {
ElMessageBox.confirm('确认充值?')
//
const addBefore = () => {
Ref.value.validate(async (valid) => {
if (valid) {
ElMessageBox.confirm("确认添加?")
.then(() => {
console.log("这里是jwcode", addConsume.value.jwcode)
add();
console.log('添加成功');
console.log("添加成功",);
addConsume.value = {};
})
.catch(() => {
console.log('取消添加');
console.log("取消添加");
});
};
//
const addBefore = () => {
Ref.value.validate(async (valid) => {
// cookie
if (!valid) {
} else {
//
ElMessage({
type: 'error',
message: '请检查输入内容'
type: "error",
message: "请检查输入内容",
});
return;
}
ReadCookies.value = `coinConsume:${addConsume.value.jwcode}:${addConsume.value.goodsName}}`
// cookie
const cookie = Cookies.get(ReadCookies.value)
console.log("time", WriteCookiesTime.value)
//
ReadCookiesTime.value = moment(cookie).format('YYYY-MM-DD HH:mm:ss')
if (cookie) {
ConsumeDialogVisible.value = true;
} else {
proceedWithConsume();
}
});
};
//
const getUser = async function (jwcode) {
try {
@ -522,46 +472,60 @@ onMounted(async function () {
<template>
<div>
<el-form :model="addConsume" ref="Ref" :rules="rules" label-width="auto" style="max-width: 600px;"
label-position="right" class="add-form">
<el-form-item prop="jwcode" label="精网号">
<el-input v-model="addConsume.jwcode" style="width: 200px"/>
<el-form :model="addConsume" ref="Ref" :rules="rules" style="max-width: 750px;">
<div style="width:25vw">
<el-form-item prop="jwcode" label="精网号" style="margin-top: 50px">
<el-input v-model="addConsume.jwcode" style="width: 10vw;margin-left:45px" />
<el-button type="primary" @click="getUser(addConsume.jwcode)" style="margin-left: 10px">查询
</el-button>
</el-form-item>
<el-form-item prop="goodsName" label="商品名称">
<el-select v-model="addConsume.goodsName" placeholder="请选择商品" style="width: 200px">
</div>
<div style="width:25vw">
<el-form-item prop="goodsName" label="商品名称" style="flex: 1; margin-right: 0px">
<el-select v-model="addConsume.goodsName" placeholder="请选择商品" style="width: 10vw;margin-left:30px">
<el-option v-for="item in goods" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</div>
<div style="width:25vw">
<el-form-item prop="sumGold" label="消耗金币总数">
<el-input v-model="addConsume.sumGold" style="width: 100px" @input="validateInput()"
<el-input v-model="addConsume.sumGold" style="width: 10vw;margin-left:2px" @input="validateInput()"
@change="calculateCoins(addConsume.sumGold)" />
</el-form-item>
</div>
<!-- 三类金币自动计算禁用状态不可编辑 -->
<div style="width:25vw">
<el-form-item prop="permanentGold" label="永久金币">
<el-input v-model="addConsume.permanentGold" disabled style="width: 100px">
<el-input v-model="addConsume.permanentGold" disabled style="width: 10vw;margin-left:40px">
<template #default="scope">{{ scope.row.permanentGold }}</template>
</el-input>
<p style="margin-right: 0px">&nbsp;&nbsp;</p>
</el-form-item>
</div>
<div style="width:25vw">
<el-form-item prop="freeCoin" label="免费金币">
<el-input disabled v-model="addConsume.freeGold" style="width: 100px"/>
<el-input disabled v-model="addConsume.freeGold" style="width: 10vw;margin-left:40px" />
<p style="margin-right: 0px">&nbsp;&nbsp;</p>
</el-form-item>
</div>
<div style="width:25vw">
<el-form-item prop="taskGold" label="任务金币">
<el-input disabled v-model="addConsume.taskGold" style="width: 100px"/>
<el-input disabled v-model="addConsume.taskGold" style="width: 10vw;margin-left:40px" />
<p style="margin-right: 20px">&nbsp;&nbsp;</p>
</el-form-item>
</div>
<div style="width:25vw">
<el-form-item prop="remark" label="备注">
<el-input v-model="addConsume.remark" style="width: 300px" :rows="4" maxlength="100" show-word-limit
<el-input v-model="addConsume.remark" style="width: 13.5vw;margin-left:70px" :rows="4" maxlength="100" show-word-limit
type="textarea" />
</el-form-item>
</div>
<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-button type="primary" @click="addBefore" style="margin-top:10px"> 提交</el-button>
</el-form>
<!-- 客户信息栏 -->
<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">
@ -602,15 +566,13 @@ onMounted(async function () {
</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 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.nowPermanentGold }};
免费金币:{{ user.nowFreeGold }};
任务金币:{{ user.nowTaskGold }})</span>
</el-form-item>
@ -649,80 +611,6 @@ onMounted(async function () {
</el-form>
</el-card>
<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>
<!-- 金币消耗明细的布局------------------------------------------------------- -->
@ -739,12 +627,6 @@ p {
margin: 0px;
}
.add-form {
margin-top: 50px;
max-width: 50%;
float: left;
}
.el-form-item {
margin-left: 50px;
}
@ -755,41 +637,7 @@ p {
height: 50px;
display: block;
}
.customer-info {
max-width: 60%;
}
</style>
<style>
/* 标题居中 */
.el-dialog__header {
text-align: center;
}
.confirm-body {
width: 420px;
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;
}
</style>

144
src/views/history/history.vue

@ -0,0 +1,144 @@
<template>
<el-card>
<el-text size="large>">姓名</el-text>
<el-input v-model="searchObj.name" placeholder="请输入姓名" style="width: 12vw;margin-right:1vw"
clearable></el-input>
<el-text size="large>">精网号</el-text>
<el-input v-model="searchObj.jwcode" placeholder="请输入精网号" style="width: 12vw;margin-right:1vw"
clearable></el-input>
<el-text size="large" style="width: 80px">充值时间</el-text>
<el-date-picker v-model="dateRange" type="datetimerange" range-separator="" start-placeholder="开始时间"
end-placeholder="结束时间" style="width: 25vw;margin-right:1vw" @change="handleDatePickerChange" />
<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="resetSearch">重置</el-button>
<el-button type="primary" @click="get">查询</el-button>
</el-card>
<el-card style="margin-top:10px">
<el-table :data="tableData">
<el-table-column type="index" label="序号" width="55">
<template #default="scope">
{{ scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize }}
</template>
</el-table-column>
<el-table-column prop="name" label="客户姓名" width="120" show-overflow-tooltip />
<el-table-column prop="jwcode" label="精网号" width="120" />
<el-table-column prop="" label="数量" width="120" />
<el-table-column prop="" label="更新类型" width="120" />
<el-table-column prop="" label="免费金币" width="120" />
<el-table-column prop="" label="永久金币" width="120" />
<el-table-column prop="" label="任务金币" width="120" />
<el-table-column prop="" label="操作人" width="120" />
<el-table-column prop="" label="更新时间" width="120" />
<el-table-column prop="" label="备注" width="120" />
</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>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import API from '@/util/http'
import moment from 'moment'
import { useAdminStore } from "@/store/index.js"
import { storeToRefs } from "pinia"
const adminStore = useAdminStore()
const { adminData, menuTree } = storeToRefs(adminStore)
import { permissionMapping, findMenuById } from "@/utils/menuTreePermission.js"
const tableData = ref([])
const dateRange = ref([])
const searchObj = ref({
name: '',
jwcode: ''
})
const pagination = ref({
pageNum: 1,
pageSize: 50,
total: 0
})
const get = async function () {
if (findMenuById(menuTree.value, permissionMapping.History_Query)) {
try {
if (!searchObj.value.name && !searchObj.value.jwcode) {
const res = await API.get('/admin/history', {
params: {
...searchObj.value,
startTime: dateRange.value[0],
endTime: dateRange.value[1]
}
})
tableData.value = res.data
pagination.value.total = res.data.total
} else {
ElMessage.error('请输入姓名或精网号')
return
}
} catch (e) {
ElMessage.error(e.message)
}
}
}
const resetSearch = function () {
searchObj.value = {
name: '',
jwcode: ''
}
dateRange.value = []
get()
}
const getToday = function () {
const today = dayjs()
const startTime = today.startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = 'today'
get()
getStats()
}
const getYesterday = function () {
const today = dayjs()
const startTime = today.subtract(1, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.subtract(1, 'day').endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = 'yesterday'
get()
getStats()
}
const get7Days = function () {
const today = dayjs()
const startTime = today.subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss')
const endTime = today.endOf('day').format('YYYY-MM-DD HH:mm:ss')
dateRange.value = [startTime, endTime]
console.log('dateRange', dateRange.value)
activeTimeRange.value = '7days'
get()
getStats()
}
const handleDatePickerChange = () => {
activeTimeRange.value = ''
}
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
}
</script>
<scope lang="scss" scoped>
.pagination {
display: flex;
margin-top: 0.5vh;
}
</scope>

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

@ -9,7 +9,8 @@
<div style="float: right;">
<el-button type="success" @click="reset">重置</el-button>
<el-button type="primary" @click="get">查询</el-button>
<el-button type="primary" @click="search">查询</el-button>
</div>
</el-card>
@ -149,7 +150,7 @@ const handleSortChange = (column) => {
}
searchObj.value.sortOrder = column.order === 'ascending' ? 'asc' : 'desc'
console.log('排序字段:', searchObj.value.sortField,'排序方式:', searchObj.value.sortOrder)
get()
search()
}
const getmarkets = async () => {
try {
@ -168,23 +169,27 @@ const trim = () => {
const reset = function () {
searchObj.value.jwcode = ''
searchObj.value.dept = ''
get()
search()
}
const handlePageSizeChange = function (val) {
pagination.value.pageSize = val
get()
search()
}
const handleCurrentChange = function (val) {
pagination.value.pageNum = val
get()
search()
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
const search = () => {
get()
getStats()
}
onMounted(() => {
get()
search()
getmarkets()
getStats()
console.log('页面接收到的adminData:', adminData.value)

3
stats.html

@ -4925,7 +4925,6 @@ var drawChart = (function (exports) {
})({});
/*-->*/
</script>
<script>
/*<!--*/
@ -4942,7 +4941,7 @@ var drawChart = (function (exports) {
window.addEventListener('resize', run);
document.addEventListener('DOMContentLoaded', run);
/*-->*/
</script>
</body>
</html>

Loading…
Cancel
Save