feat(custom): 订单列表改版

This commit is contained in:
2023-11-14 20:43:38 +08:00
parent 4babc88fcd
commit 6ce72fdfd6
24 changed files with 5221 additions and 11119 deletions

5
.env.sentry-build-plugin Normal file
View File

@@ -0,0 +1,5 @@
# DO NOT commit this file to your repository!
# The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
# It's used for authentication when uploading source maps.
# You can also set this env variable in your own `.env` files and remove this file.
SENTRY_AUTH_TOKEN="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"

View File

@@ -30,5 +30,8 @@
"files.associations": { "files.associations": {
"*.env.*": "dotenv", "*.env.*": "dotenv",
"*.css": "postcss" "*.css": "postcss"
},
"[dockerfile]": {
"editor.defaultFormatter": "ms-azuretools.vscode-docker"
} }
} }

View File

@@ -29,7 +29,7 @@ export function createVitePlugins(viteEnv, isBuild) {
if (isBuild) { if (isBuild) {
plugins.push( plugins.push(
visualizer({ visualizer({
open: true, open: false,
gzipSize: true, gzipSize: true,
brotliSize: true, brotliSize: true,
}) })

View File

@@ -4,6 +4,7 @@ import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers' import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import { FileSystemIconLoader } from 'unplugin-icons/loaders' import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver' import IconsResolver from 'unplugin-icons/resolver'
// import { sentryVitePlugin } from '@sentry/vite-plugin'
/** /**
* * unplugin-icons插件自动引入iconify图标 * * unplugin-icons插件自动引入iconify图标
@@ -43,4 +44,10 @@ export default [
inject: 'body-last', inject: 'body-last',
customDomId: '__CUSTOM_SVG_ICON__', customDomId: '__CUSTOM_SVG_ICON__',
}), }),
// sentryVitePlugin({
// authToken: process.env.SENTRY_AUTH_TOKEN,
// org: 'sentry',
// project: 'jdt-admin',
// url: 'https://w.huakk.top',
// }),
] ]

View File

@@ -33,6 +33,8 @@
] ]
}, },
"dependencies": { "dependencies": {
"@sentry/vite-plugin": "^2.9.0",
"@sentry/vue": "^7.77.0",
"@unocss/eslint-config": "^0.55.7", "@unocss/eslint-config": "^0.55.7",
"@vueuse/core": "^10.5.0", "@vueuse/core": "^10.5.0",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",

15547
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ import { setupRouterGuard } from './guard'
import { basicRoutes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes' import { basicRoutes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes'
import { getToken, isNullOrWhitespace } from '@/utils' import { getToken, isNullOrWhitespace } from '@/utils'
import { usePermissionStore } from '@/store' import { usePermissionStore } from '@/store'
import * as Sentry from '@sentry/vue'
const isHash = true const isHash = true
export const router = createRouter({ export const router = createRouter({
@@ -14,6 +15,23 @@ export const router = createRouter({
export async function setupRouter(app) { export async function setupRouter(app) {
await addDynamicRoutes() await addDynamicRoutes()
setupRouterGuard(router) setupRouterGuard(router)
Sentry.init({
app,
dsn: 'https://589c2c58683b4e8fa87a87609fd95e3b@w.huakk.top/2',
integrations: [
new Sentry.BrowserTracing({
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
tracePropagationTargets: ['localhost', /^https:\/\/yourserver\.io\/api/],
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
}),
new Sentry.Replay(),
],
// Performance Monitoring
tracesSampleRate: 1.0, // Capture 100% of the transactions
// Session Replay
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
})
app.use(router) app.use(router)
} }

View File

@@ -56,21 +56,20 @@ function filterAsyncRoutes(routes = [], firstRoute = true) {
order: route.sort, order: route.sort,
} }
let redirect = ''
if (route.route === '/' && firstRoute) {
// 重定向
redirect = route.subMenu[0].route
}
const curRoute = { const curRoute = {
path: route.route, path: route.route,
name: route.name, name: route.name,
isHidden, isHidden,
meta, meta,
redirect,
children: [], children: [],
} }
if (route.route === '/' && firstRoute) {
curRoute['redirect'] = route.subMenu[0].route
} else if (route.subMenu && route.type === 1) {
curRoute['redirect'] = `${route.subMenu[0].route}`
}
if (route.subMenu && route.subMenu.length) { if (route.subMenu && route.subMenu.length) {
curRoute.children = filterAsyncRoutes(route.subMenu, false) curRoute.children = filterAsyncRoutes(route.subMenu, false)
} else { } else {

View File

@@ -11,6 +11,8 @@
:pagination="pagination" :pagination="pagination"
:bordered="false" :bordered="false"
remote remote
:row-key="rowKey"
children-key="Classify"
/> />
<n-modal v-model:show="showModal"> <n-modal v-model:show="showModal">
<n-card <n-card
@@ -55,6 +57,10 @@ const vPerms = resolveDirective('perms')
const loading = ref(false) const loading = ref(false)
const rowKey = (row) => {
return row.Classify || []
}
const columns = ref([ const columns = ref([
{ {
title: 'ID', title: 'ID',

View File

@@ -82,9 +82,9 @@
<n-form-item label="商户地址:" path="address"> <n-form-item label="商户地址:" path="address">
<n-input v-model:value="formValue.address" placeholder="请输入商户地址" /> <n-input v-model:value="formValue.address" placeholder="请输入商户地址" />
</n-form-item> </n-form-item>
<n-form-item label="经营类目:" path="classId"> <n-form-item label="经营类目:" path="store_class_id">
<n-select <n-select
v-model:value="formValue.classId" v-model:value="formValue.store_class_id"
label-field="name" label-field="name"
value-field="ID" value-field="ID"
clearable clearable
@@ -98,16 +98,16 @@
<n-form-item v-else label="修改密码:" path="password"> <n-form-item v-else label="修改密码:" path="password">
<n-input v-model:value="formValue.password" placeholder="不修改密码请留空" /> <n-input v-model:value="formValue.password" placeholder="不修改密码请留空" />
</n-form-item> </n-form-item>
<n-form-item label="商户类型:" path="bType"> <!-- <n-form-item label="商户类型:" path="bType">-->
<n-select <!-- <n-select-->
v-model:value="formValue.bType" <!-- v-model:value="formValue.bType"-->
label-field="name" <!-- label-field="name"-->
value-field="ID" <!-- value-field="ID"-->
placeholder="请选择商户类型" <!-- placeholder="请选择商户类型"-->
clearable <!-- clearable-->
:options="typeOptions" <!-- :options="typeOptions"-->
/> <!-- />-->
</n-form-item> <!-- </n-form-item>-->
<n-form-item label="手续费收取类型:" path="scaleType"> <n-form-item label="手续费收取类型:" path="scaleType">
<n-select <n-select
v-model:value="formValue.scaleType" v-model:value="formValue.scaleType"
@@ -152,6 +152,7 @@
import { onMounted, ref, h, withDirectives, resolveDirective } from 'vue' import { onMounted, ref, h, withDirectives, resolveDirective } from 'vue'
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
import api from './api' import api from './api'
const vPerms = resolveDirective('perms') const vPerms = resolveDirective('perms')
const isEdit = computed(() => drawerTitle.value === '编辑商户') const isEdit = computed(() => drawerTitle.value === '编辑商户')
@@ -162,18 +163,10 @@ const columns = ref([
align: 'center', align: 'center',
key: 'name', key: 'name',
}, },
{
title: '商户类型',
align: 'center',
key: 'type',
render(row) {
return h('span', row.bType === 1 ? '供应商' : '兑换商')
},
},
{ {
title: '状态', title: '状态',
align: 'center', align: 'center',
key: 'status', slot: 'status',
render(row) { render(row) {
return h('span', row.status === 1 ? '正常' : '禁用') return h('span', row.status === 1 ? '正常' : '禁用')
}, },
@@ -265,10 +258,9 @@ let formValue = ref({
phone: '', phone: '',
mobile: '', mobile: '',
address: '', address: '',
classId: null, store_class_id: null,
local: '', local: '',
password: '', password: '',
bType: null,
scaleType: null, scaleType: null,
scale: null, scale: null,
status: 2, status: 2,
@@ -305,7 +297,7 @@ const rules = {
message: '请搜索商户经纬度', message: '请搜索商户经纬度',
trigger: 'blur', trigger: 'blur',
}, },
classId: { store_class_id: {
required: true, required: true,
type: 'number', type: 'number',
message: '请选择经营类目', message: '请选择经营类目',
@@ -316,12 +308,6 @@ const rules = {
// message: '请输入商户密码', // message: '请输入商户密码',
// trigger: 'blur', // trigger: 'blur',
// }, // },
bType: {
required: true,
type: 'number',
message: '请选择商户类型',
trigger: 'change',
},
scaleType: { scaleType: {
required: true, required: true,
type: 'number', type: 'number',
@@ -388,8 +374,8 @@ const handleValidateClick = (e) => {
await api.addMer(formValue.value) await api.addMer(formValue.value)
$message.success('成功') $message.success('成功')
handleClearValidateClick() handleClearValidateClick()
getMertype() await getMertype()
getList() await getList()
showModal.value = false showModal.value = false
} catch (error) { } catch (error) {
$message.error(error.msg) $message.error(error.msg)

View File

@@ -17,7 +17,7 @@
<div mt-10>联系电话:{{ nowRow.phone }}</div> <div mt-10>联系电话:{{ nowRow.phone }}</div>
<div mt-10>开户行:{{ nowRow.bank }}</div> <div mt-10>开户行:{{ nowRow.bank }}</div>
<div mt-10>银行卡号:{{ nowRow.bank_card }}</div> <div mt-10>银行卡号:{{ nowRow.bank_card }}</div>
<div mt-10>商户类型:{{ atype.name }}</div> <!-- <div mt-10>商户类型:{{ atype.name }}</div>-->
<div mt-10>经营类目:{{ btype.name }}</div> <div mt-10>经营类目:{{ btype.name }}</div>
<div mt-10> <div mt-10>
<div>营业执照:</div> <div>营业执照:</div>
@@ -59,6 +59,7 @@ import { h, withDirectives, resolveDirective } from 'vue'
import api from './api' import api from './api'
import api1 from '../mer_list/api' import api1 from '../mer_list/api'
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
const vPerms = resolveDirective('perms') const vPerms = resolveDirective('perms')
const loading = ref(false) const loading = ref(false)
@@ -110,6 +111,7 @@ const columns = ref([
...row, ...row,
img: row.img.split(','), img: row.img.split(','),
} }
console.log(nowRow.value)
active.value = true active.value = true
}, },
}, },
@@ -150,23 +152,23 @@ const getData = async () => {
} }
const classOptions = ref([]) const classOptions = ref([])
const typeOptions = ref([]) // const typeOptions = ref([])
const getMertype = async () => { const getMertype = async () => {
const res = await api1.getMerType() const res = await api1.getMerType()
classOptions.value = res.data.class classOptions.value = res.data.class
typeOptions.value = res.data.type // typeOptions.value = res.data.type
} }
const atype = computed(() => { // const atype = computed(() => {
return typeOptions.value.find((item) => { // return typeOptions.value.find((item) => {
if (item.ID === nowRow.value.bType) return item // if (item.ID === nowRow.value.bType) return item
}) // })
}) // })
const btype = computed(() => { const btype = computed(() => {
return classOptions.value.find((item) => { return classOptions.value.find((item) => {
if (item.ID === nowRow.value.classId) return item if (item.ID === nowRow.value.store_class_id) return item
}) })
}) })

View File

@@ -1,3 +1,4 @@
<!-- eslint-disable vue/no-v-html -->
<template> <template>
<CommonPage show-footer :title="$route.title"> <CommonPage show-footer :title="$route.title">
<n-data-table <n-data-table
@@ -37,20 +38,20 @@
> >
<n-form ref="formRef" :model="nowRow" :rules="rules" label-placement="left"> <n-form ref="formRef" :model="nowRow" :rules="rules" label-placement="left">
<n-grid :cols="24"> <n-grid :cols="24">
<n-form-item-gi :span="20" label="商品赠送豆子" path="pulse_number"> <n-form-item-gi :span="20" label="商品赠送豆子" path="pulse">
<n-input-number <n-input-number
v-model:value="nowRow.pulse_number" v-model:value="nowRow.pulse"
clearable clearable
:precision="2"
placeholder="请输入赠送豆子数量...." placeholder="请输入赠送豆子数量...."
:min="0"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="20" label="商品赠送积分" path="integral"> <n-form-item-gi :span="20" label="商品赠送积分" path="integral">
<n-input-number <n-input-number
v-model:value="nowRow.integral" v-model:value="nowRow.integral"
clearable clearable
:precision="2"
placeholder="请输入赠送积分数量...." placeholder="请输入赠送积分数量...."
:min="0"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="18" label="商品分佣类型" path="commission_type"> <n-form-item-gi :span="18" label="商品分佣类型" path="commission_type">
@@ -75,13 +76,15 @@
v-model:value="nowRow.commission" v-model:value="nowRow.commission"
clearable clearable
placeholder="请输入分佣比例...." placeholder="请输入分佣比例...."
:min="0"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="20" label="豆子过期时间" path="expiration"> <n-form-item-gi :span="20" label="豆子过期时间" path="expiration">
<n-input-number <n-input-number
v-model:value="nowRow.expiration" v-model:value="nowRow.expiration"
clearable clearable
placeholder="请输入豆子过期时间...." placeholder="请输入豆子过期时间"
:min="0"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="12"> <n-form-item-gi :span="12">
@@ -169,7 +172,7 @@ const notesVal = ref('')
const formRef = ref(null) const formRef = ref(null)
const rules = { const rules = {
pulse_number: { pulse: {
required: true, required: true,
type: 'number', type: 'number',
message: '请输入赠送豆子数量', message: '请输入赠送豆子数量',
@@ -234,8 +237,17 @@ const columns = ref([
}, },
{ {
title: '商品分类', title: '商品分类',
key: 'class_name', slot: 'Classify',
align: 'center', align: 'center',
render(row) {
return h(
'div',
{},
{
default: () => row.Classify.name,
}
)
},
}, },
{ {
title: '商品价格(元)', title: '商品价格(元)',
@@ -249,7 +261,7 @@ const columns = ref([
}, },
{ {
title: '赠送豆子', title: '赠送豆子',
key: 'pulse_number', key: 'pulse',
align: 'center', align: 'center',
}, },
{ {

View File

@@ -21,15 +21,15 @@ export default {
order: 10, order: 10,
}, },
}, },
{ // {
name: 'PointList', // name: 'PointList',
path: 'point_list', // path: 'point_list',
component: () => import('./point/index.vue'), // component: () => import('./point/index.vue'),
meta: { // meta: {
title: '积分商品', // title: '积分商品',
icon: 'mdi:account-multiple', // icon: 'mdi:account-multiple',
order: 10, // order: 10,
}, // },
}, // },
], ],
} }

View File

@@ -26,4 +26,18 @@ export default {
// 全部投注用户 // 全部投注用户
allUser: () => request.post('/all/draw/user', {}), allUser: () => request.post('/all/draw/user', {}),
// 吹气球相关
// 游戏状态
getisBalloonStart: () => request.post('/getisBalloonStart'),
// 修改游戏状态
setisBalloonStart: (data) => request.post('/isBalloonStart', data),
// 全部开奖记录
getBalloonList: () => request.post('/balloon/draw'),
// 本局投注记录
getBalloonUser: () => request.post('/now/balloon/draw/user'),
// 全部投注记录
getAllBalloonUser: () => request.post('/all/balloon/draw/user'),
// 统计全部投注和中奖列表
getUserList: () => request.post('/user/balloon/list'),
} }

View File

@@ -0,0 +1,206 @@
<template>
<CommonPage show-footer :title="$route.title">
<div flex items-center>
<div mr-20 flex>
<div>游戏状态</div>
<n-switch
v-model:value="gameStatus"
:checked-value="1"
:unchecked-value="2"
@update:value="handleUpdateValue"
/>
</div>
<div ml-20 flex items-center>
<div>本局记录</div>
<n-button type="primary" @click="openModal(1)">预览</n-button>
</div>
<div ml-20 flex items-center>
<div>全部记录</div>
<n-button type="primary" @click="openModal(2)">预览</n-button>
</div>
</div>
<n-modal v-model:show="showModal">
<n-card
style="width: 900px"
:title="modalTitle"
:bordered="false"
size="huge"
role="dialog"
aria-modal="true"
>
<template v-if="keyModal === 1">
<div>
<span>
总投注(豆子):
<span text-red>{{ nowData.total_number }}</span>
</span>
<span ml-20>
总积分:
<span text-red>{{ nowData.total_integral }}</span>
</span>
</div>
<n-data-table
:loading="nowData.Loading"
:columns="nowData.Columns"
:data="nowData.data"
:max-height="700"
:pagination="false"
:bordered="false"
remote
@update:sorter="nowData.handleSorterChange"
/>
</template>
<template v-else>
<div>
<span>
总投注(豆子):
<span text-red>{{ allData.total_number }}</span>
</span>
<span ml-20>
总积分:
<span text-red>{{ allData.total_integral }}</span>
</span>
</div>
<n-data-table
:loading="allData.Loading"
:columns="allData.Columns"
:data="allData.data"
:max-height="700"
:pagination="false"
:bordered="false"
remote
@update:sorter="allData.handleSorterChange"
/>
</template>
</n-card>
</n-modal>
</CommonPage>
</template>
<script setup>
import api from '../../api'
const gameStatus = ref(2)
onMounted(() => {
get_status()
})
const get_status = async () => {
const res = await api.getisBalloonStart()
gameStatus.value = res.data.balloonStart
}
const handleUpdateValue = async (e) => {
await api.setisBalloonStart({
Start: e,
})
$message.success('修改成功')
get_status()
}
const showModal = ref(false)
const keyModal = ref(null)
const modalTitle = ref('')
const openModal = (type) => {
keyModal.value = type
showModal.value = true
modalTitle.value = type === 1 ? '本局记录' : '全部记录'
if (type === 1) return fetchData(nowData)
fetchData(allData)
}
const { value: tempCol } = ref([
{
title: '昵称',
key: 'User',
align: 'center',
},
{
title: '电话',
key: 'Phone',
align: 'center',
},
{
title: '期数',
key: 'Periods',
align: 'center',
},
{
title: '下注豆子',
key: 'TotalCount',
align: 'center',
sorter: true,
sortOrder: false,
},
{
title: '赢积分',
key: 'NumberSum',
align: 'center',
sorter: true,
sortOrder: false,
},
{
title: '购买秒数',
key: 'DrawTime',
align: 'center',
},
])
const { value: nowData } = ref({
Loading: false,
Columns: [...tempCol],
data: [],
total_number: 0,
total_integral: 0,
handleSorterChange: (sorter) => sortData(sorter, nowData),
api: api.getBalloonUser,
})
const { value: allData } = ref({
Loading: false,
Columns: [...tempCol],
data: [],
total_number: 0,
total_integral: 0,
handleSorterChange: (sorter) => sortData(sorter, allData),
api: api.getAllBalloonUser,
})
const fetchData = async (target) => {
target.Loading = true
const res = await target.api()
target.data = res.data.data || []
target.total_integral = res.data.total_integral
target.total_number = res.data.total_number
target.Loading = false
}
const sortData = (sorter, target) => {
if (!target.Loading) {
target.Loading = true
switch (sorter.columnKey) {
case 'TotalCount':
target.Columns[3].sortOrder = !sorter ? false : sorter.order
target.data = target.data.sort((a, b) => {
if (sorter.order === 'descend') return b.TotalCount - a.TotalCount
return a.TotalCount - b.TotalCount
})
break
case 'NumberSum':
target.Columns[4].sortOrder = !sorter ? false : sorter.order
target.data = target.data.sort((a, b) => {
if (sorter.order === 'descend') return b.NumberSum - a.NumberSum
return a.NumberSum - b.NumberSum
})
break
}
target.Loading = false
}
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,214 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-grid class="mb-10" x-gap="12" :cols="4">
<n-gi>
<n-date-picker
v-model:formatted-value="range"
value-format="yyyy-MM-dd"
type="daterange"
clearable
/>
</n-gi>
<n-gi>
<n-button type="primary" @click="getList">搜索</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</n-gi>
</n-grid>
<div w-full flex items-center>
<Echarts :loading="loading" :option="option" />
<Echarts :loading="loading" :option="option1" />
</div>
<div w-full flex items-center justify-between>
<n-card title="开奖记录" :bordered="false" content-style="padding: 0;">
<n-data-table
:max-height="500"
:loading="loading"
:columns="columns"
:data="data"
:bordered="true"
:virtual-scroll="true"
remote
/>
</n-card>
</div>
</CommonPage>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import api from '../../api.js'
import Echarts from '@/components/Echarts.vue'
import dayjs from 'dayjs'
const loading = ref(false)
const range = ref(null)
const option = ref({
title: {
text: '单期下注(豆子)/赔付(积分) 统计',
left: 'center',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: [],
},
yAxis: {
type: 'value',
},
series: [
{
name: '下注(豆子)',
data: [],
type: 'bar',
},
{
name: '赔付(积分)',
data: [],
type: 'bar',
},
],
dataZoom: [
{
type: 'inside',
},
{
type: 'slider',
},
],
})
const option1 = ref({
title: {
text: '总下注(豆子)/总赔付(积分)',
left: 'center',
},
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'right',
},
series: [
{
type: 'pie',
radius: '50%',
data: [],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
},
],
})
const data = ref([])
const columns = ref([
{
title: '期数',
key: 'Periods',
align: 'center',
},
{
title: '开奖秒数',
key: 'Name',
align: 'center',
},
{
title: '下注',
key: 'NumberSum',
align: 'center',
},
{
title: '赔付',
key: 'TotalCount',
align: 'center',
},
{
title: '时间',
key: 'Date',
align: 'center',
},
])
onMounted(() => {
getList()
})
const clear = () => {
range.value = null
getList()
}
const getList = async () => {
loading.value = true
const dataObj = {
StartTime: dayjs().format('YYYY-MM-DD'),
EndTime: dayjs().format('YYYY-MM-DD'),
}
if (range.value) {
dataObj.StartTime = range.value[0]
dataObj.EndTime = range.value[1]
}
const res = await api.getUserList(dataObj)
const newData = res.data.data || []
data.value = newData
option.value.xAxis.data = []
option.value.series[0].data = []
option.value.series[1].data = []
option1.value.series[0].data = []
if (newData.length > 0) {
res.data.data.forEach((item) => {
const a = (
((res.data.total * 10) / (res.data.total * 10 + res.data.totalDices)) *
100
).toFixed(2)
const b = ((res.data.totalDices / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(
2
)
option.value.xAxis.data.push(`${item.Periods}期-${item.Name}`)
option.value.series[0].name = `下注(豆子): ${a}%`
option.value.series[0].data.push(item.NumberSum * 10)
option.value.series[1].name = `赔付(积分): ${b}%`
option.value.series[1].data.push(item.TotalCount)
})
const a = (((res.data.total * 10) / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(
2
)
const b = ((res.data.totalDices / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(2)
option1.value.series[0].data.push({ value: res.data.total * 10, name: `总下注: ${a}%` })
option1.value.series[0].data.push({
value: res.data.totalDices,
name: `总赔付: ${b}%`,
})
}
loading.value = false
}
</script>
<style lang="scss" scoped>
.chart {
width: 50%;
height: 400px;
}
</style>

View File

@@ -136,7 +136,7 @@
<script setup> <script setup>
import { h } from 'vue' import { h } from 'vue'
import api from '../api' import api from '../../api'
import { getToken } from '@/utils' import { getToken } from '@/utils'
const ws = new WebSocket(`wss://${import.meta.env.VITE_WS_URL}`) const ws = new WebSocket(`wss://${import.meta.env.VITE_WS_URL}`)
@@ -334,8 +334,8 @@ const handleSorterChange = (sorter) => {
case 'TotalCount': case 'TotalCount':
jlColumns.value[3].sortOrder = !sorter ? false : sorter.order jlColumns.value[3].sortOrder = !sorter ? false : sorter.order
jlData.value.data = jlData.value.data.sort((a, b) => { jlData.value.data = jlData.value.data.sort((a, b) => {
if (sorter.order === 'descend') return b.NumberSum - a.NumberSum if (sorter.order === 'descend') return b.TotalCount - a.TotalCount
return a.NumberSum - b.NumberSum return a.TotalCount - b.TotalCount
}) })
break break
case 'NumberSum': case 'NumberSum':

View File

@@ -14,29 +14,6 @@
<n-button ml-10 @click="clear">重置</n-button> <n-button ml-10 @click="clear">重置</n-button>
</n-gi> </n-gi>
</n-grid> </n-grid>
<!-- <n-grid class="mb-10" x-gap="12" :cols="3">
<n-gi>
<n-card>
<n-statistic label="总下注">
<n-number-animation :from="0" :to="TYVal.total" />
</n-statistic>
</n-card>
</n-gi>
<n-gi>
<n-card>
<n-statistic label="总赔付">
<n-number-animation :from="0" :to="TYVal.totalNum" />
</n-statistic>
</n-card>
</n-gi>
<n-gi>
<n-card>
<n-statistic label="总盈利">
<n-number-animation :from="0" :to="TYVal.total / 10 - TYVal.totalNum / 100" />
</n-statistic>
</n-card>
</n-gi>
</n-grid> -->
<div w-full flex items-center> <div w-full flex items-center>
<Echarts :loading="loading" :option="option" /> <Echarts :loading="loading" :option="option" />
<Echarts :loading="loading" :option="option1" /> <Echarts :loading="loading" :option="option1" />
@@ -59,7 +36,7 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import api from '../api.js' import api from '../../api.js'
import Echarts from '@/components/Echarts.vue' import Echarts from '@/components/Echarts.vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@@ -78,11 +55,6 @@ const option = ref({
type: 'shadow', type: 'shadow',
}, },
}, },
// legend: {
// data: ['()', '()'],
// left: 'left',
// type: 'scroll',
// },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
@@ -148,11 +120,6 @@ const option1 = ref({
const data = ref([]) const data = ref([])
const TYVal = ref({
total: 0,
totalNum: 0,
})
const columns = ref([ const columns = ref([
{ {
title: '期数', title: '期数',
@@ -203,10 +170,6 @@ const getList = async () => {
const res = await api.getStatistics(dataObj) const res = await api.getStatistics(dataObj)
const newData = res.data.data || [] const newData = res.data.data || []
data.value = newData data.value = newData
TYVal.value = {
total: res.data.total,
totalNum: res.data.totalDices,
}
option.value.xAxis.data = [] option.value.xAxis.data = []
option.value.series[0].data = [] option.value.series[0].data = []
option.value.series[1].data = [] option.value.series[1].data = []

View File

@@ -33,7 +33,7 @@
</template> </template>
<script setup> <script setup>
import api from '../api.js' import api from '../../api.js'
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
import { h, ref, onMounted } from 'vue' import { h, ref, onMounted } from 'vue'

View File

@@ -1,9 +1,10 @@
<script setup> <script setup>
import { h, ref, resolveDirective, withDirectives } from 'vue' import { h, ref, resolveDirective, withDirectives } from 'vue'
import api from '../api' import api from '../api'
import { NTag, NImage, NButton } from 'naive-ui' import { NTag, NImage, NButton, NDropdown } from 'naive-ui'
import Upload from '@/components/Upload.vue' import Upload from '@/components/Upload.vue'
import Editor from '@/components/Editor.vue' import Editor from '@/components/Editor.vue'
import { useRouter } from 'vue-router'
const vPerms = resolveDirective('perms') const vPerms = resolveDirective('perms')
@@ -65,6 +66,56 @@ const columns = ref([
), ),
[[vPerms, ['/admin/game/edit']]] [[vPerms, ['/admin/game/edit']]]
), ),
withDirectives(
h(
'span',
{
class: 'ml-10',
},
{
default: () => [
h(
NDropdown,
{
trigger: 'click',
size: 'small',
options: [
{
key: 0,
label: '实时数据',
},
{
key: 1,
label: '数据统计',
},
{
key: 2,
label: '宙斯统计',
},
],
onSelect: (e) => dropdownSelect(e),
},
{
default: () =>
h(
NButton,
{
class: 'ml-5',
type: 'primary',
text: true,
size: 'small',
},
{
default: () => '操作',
}
),
}
),
],
}
),
[[vPerms, ['待定']]]
),
] ]
}, },
}, },
@@ -165,6 +216,14 @@ const clear = () => {
showModal.value = false showModal.value = false
getList() getList()
} }
const route = useRouter()
const paths = ['/game/game_data', '/game/game_statistics', '/game/game_zs']
const dropdownSelect = (e) => {
route.push(paths[e])
}
</script> </script>
<template> <template>

View File

@@ -80,7 +80,6 @@
<script setup> <script setup>
import api from './api' import api from './api'
import { NEllipsis } from 'naive-ui'
const loading = ref(false) const loading = ref(false)
@@ -154,27 +153,54 @@ const columns = ref([
{ {
title: '用户', title: '用户',
align: 'center', align: 'center',
key: 'user_name', slot: 'user',
render: (row) => {
return [
h(
'div',
{},
{
default: () => row.User.nickName,
}
),
]
},
}, },
{ {
title: '用户电话', title: '用户电话',
align: 'center', align: 'center',
key: 'phone', slot: 'phone',
render: (row) => {
return [
h(
'div',
{},
{
default: () => row.User.phone,
}
),
]
},
}, },
{ {
title: '商品名称', title: '商品名称',
align: 'center', align: 'center',
slot: 'goods_name', slot: 'goods_name',
render: (row) => { render: (row) => {
return h( const el = []
NEllipsis, row.OrderGoods.forEach((item) => {
{ el.push(
style: 'max-width: 240px', h(
}, 'div',
{ {},
default: () => row.goods_name, {
} default: () =>
) `${item.Goods.name}|${item.pay_price}元或${item.pay_integral}积分|X${item.number}`,
}
)
)
})
return el
}, },
}, },
{ {
@@ -185,12 +211,20 @@ const columns = ref([
{ {
title: '订单总价', title: '订单总价',
align: 'center', align: 'center',
key: 'number', slot: 'number',
render: (row) => h('span', row.pay_type === 1 ? `${row.price}` : `${row.exchange}积分`),
},
{
title: '支付方式',
align: 'center',
slot: 'pay_type',
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'),
}, },
{ {
title: '商家名称', title: '商家名称',
align: 'center', align: 'center',
key: 'store_name', slot: 'store_name',
render: (row) => h('span', row.Store.name),
}, },
// { // {
// title: '订单佣金(元)', // title: '订单佣金(元)',

View File

@@ -24,7 +24,7 @@
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
> >
<!-- {{ model }} --> <!-- {{ model }}-->
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left"> <n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
<n-grid :cols="24" :x-gap="24"> <n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="16" label="父级分类:" path="pid"> <n-form-item-gi :span="16" label="父级分类:" path="pid">
@@ -85,7 +85,7 @@
<n-switch v-model:value="model.status" :checked-value="1" :unchecked-value="2" /> <n-switch v-model:value="model.status" :checked-value="1" :unchecked-value="2" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi v-if="model.type !== 3" :span="10" label="是否显示:" path="isShow"> <n-form-item-gi v-if="model.type !== 3" :span="10" label="是否显示:" path="isShow">
<n-switch v-model:value="model.isShow" :checked-value="1" :unchecked-value="2" /> <n-switch v-model:value="model.is_show" :checked-value="1" :unchecked-value="2" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="16" label="菜单名称:" path="name"> <n-form-item-gi :span="16" label="菜单名称:" path="name">
<n-input v-model:value="model.name" placeholder="请填写菜单名称" /> <n-input v-model:value="model.name" placeholder="请填写菜单名称" />
@@ -307,7 +307,7 @@ const model = ref({
type: null, type: null,
icon: '', icon: '',
status: 1, status: 1,
isShow: 1, is_show: 1,
name: '', name: '',
route: '', route: '',
params: '', params: '',
@@ -359,7 +359,7 @@ const openModal = (type, row = {}) => {
type: row.type, type: row.type,
icon: row.icon, icon: row.icon,
status: row.status, status: row.status,
isShow: row.isShow, is_show: row.is_show,
name: row.name, name: row.name,
route: row.route, route: row.route,
params: row.params, params: row.params,
@@ -386,7 +386,7 @@ const handleValidateClick = () => {
Icon: model.value.icon, Icon: model.value.icon,
Type: model.value.type, Type: model.value.type,
Status: model.value.status, Status: model.value.status,
IsShow: model.value.isShow, IsShow: model.value.is_show,
Name: model.value.name, Name: model.value.name,
Route: model.value.route, Route: model.value.route,
Params: model.value.params, Params: model.value.params,

View File

@@ -143,7 +143,7 @@
</div> </div>
<n-tabs v-model:value="tabVal" type="line" animated @update-value="tabsChange"> <n-tabs v-model:value="tabVal" type="line" animated @update-value="tabsChange">
<n-tab name="1" tab="活动订单"></n-tab> <n-tab name="1" tab="活动订单"></n-tab>
<n-tab name="2" tab="积分订单"></n-tab> <!-- <n-tab name="2" tab="积分订单"></n-tab>-->
<n-tab name="3" tab="豆子记录"></n-tab> <n-tab name="3" tab="豆子记录"></n-tab>
<n-tab name="4" tab="积分记录"></n-tab> <n-tab name="4" tab="积分记录"></n-tab>
<n-tab name="5" tab="推广记录"></n-tab> <n-tab name="5" tab="推广记录"></n-tab>

View File

@@ -38,6 +38,7 @@ export default defineConfig(({ command, mode }) => {
outDir: OUTPUT_DIR || 'dist', outDir: OUTPUT_DIR || 'dist',
reportCompressedSize: false, // 启用/禁用 gzip 压缩大小报告 reportCompressedSize: false, // 启用/禁用 gzip 压缩大小报告
chunkSizeWarningLimit: 1024, // chunk 大小警告的限制单位kb chunkSizeWarningLimit: 1024, // chunk 大小警告的限制单位kb
sourcemap: true,
}, },
} }
}) })