mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-07-05 01:58:15 +08:00
feat: ✨ 财务管理
This commit is contained in:
@ -10,8 +10,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.1",
|
||||
"ace-builds": "1.42.0",
|
||||
"aieditor": "1.3.9",
|
||||
"ace-builds": "1.43.0",
|
||||
"aieditor": "1.4.0",
|
||||
"axios": "1.10.0",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "1.11.13",
|
||||
@ -23,8 +23,8 @@
|
||||
"nprogress": "0.2.0",
|
||||
"sortablejs": "1.15.6",
|
||||
"vkbeautify": "0.99.3",
|
||||
"vue": "3.5.16",
|
||||
"vue-i18n": "11.1.6",
|
||||
"vue": "3.5.17",
|
||||
"vue-i18n": "11.1.7",
|
||||
"vue-router": "4.5.1",
|
||||
"vue3-ace-editor": "2.2.4",
|
||||
"vue3-json-viewer": "2.4.0",
|
||||
@ -32,12 +32,12 @@
|
||||
"vuex": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"prettier": "3.5.3",
|
||||
"@vitejs/plugin-vue": "6.0.0",
|
||||
"prettier": "3.6.1",
|
||||
"prettier-plugin-organize-attributes": "1.0.0",
|
||||
"sass": "1.89.2",
|
||||
"terser": "5.43.0",
|
||||
"vite": "6.3.5"
|
||||
"terser": "5.43.1",
|
||||
"vite": "7.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
95
src/frontend/admin/src/api/sys/userwallet.js
Normal file
95
src/frontend/admin/src/api/sys/userwallet.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 用户钱包服务
|
||||
* @module @/api/sys/user.wallet
|
||||
*/
|
||||
import config from '@/config'
|
||||
import http from '@/utils/request'
|
||||
export default {
|
||||
/**
|
||||
* 批量删除用户钱包
|
||||
*/
|
||||
bulkDelete: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/bulk.delete`,
|
||||
name: `批量删除用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户钱包计数
|
||||
*/
|
||||
count: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/count`,
|
||||
name: `用户钱包计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户钱包分组计数
|
||||
*/
|
||||
countBy: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/count.by`,
|
||||
name: `用户钱包分组计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建用户钱包
|
||||
*/
|
||||
create: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/create`,
|
||||
name: `创建用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除用户钱包
|
||||
*/
|
||||
delete: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/delete`,
|
||||
name: `删除用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑用户钱包
|
||||
*/
|
||||
edit: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/edit`,
|
||||
name: `编辑用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个用户钱包
|
||||
*/
|
||||
get: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/get`,
|
||||
name: `获取单个用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询用户钱包
|
||||
*/
|
||||
pagedQuery: {
|
||||
url: `${config.API_URL}/api/sys/user.wallet/paged.query`,
|
||||
name: `分页查询用户钱包`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
}
|
95
src/frontend/admin/src/api/sys/wallettrade.js
Normal file
95
src/frontend/admin/src/api/sys/wallettrade.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 钱包交易服务
|
||||
* @module @/api/sys/wallet.trade
|
||||
*/
|
||||
import config from '@/config'
|
||||
import http from '@/utils/request'
|
||||
export default {
|
||||
/**
|
||||
* 批量删除钱包交易
|
||||
*/
|
||||
bulkDelete: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/bulk.delete`,
|
||||
name: `批量删除钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 钱包交易计数
|
||||
*/
|
||||
count: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/count`,
|
||||
name: `钱包交易计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 钱包交易分组计数
|
||||
*/
|
||||
countBy: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/count.by`,
|
||||
name: `钱包交易分组计数`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建钱包交易
|
||||
*/
|
||||
create: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/create`,
|
||||
name: `创建钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除钱包交易
|
||||
*/
|
||||
delete: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/delete`,
|
||||
name: `删除钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 编辑钱包交易
|
||||
*/
|
||||
edit: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/edit`,
|
||||
name: `编辑钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个钱包交易
|
||||
*/
|
||||
get: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/get`,
|
||||
name: `获取单个钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询钱包交易
|
||||
*/
|
||||
pagedQuery: {
|
||||
url: `${config.API_URL}/api/sys/wallet.trade/paged.query`,
|
||||
name: `分页查询钱包交易`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
}
|
@ -60,6 +60,9 @@ export default {
|
||||
hasPermission: function (p) {
|
||||
return this.permissions.includes('*/*/*') || this.permissions.some((a) => a === p)
|
||||
},
|
||||
hasApiPermission: function (p) {
|
||||
return this.apiPermissions.includes('*/*/*') || this.apiPermissions.some((a) => a === p)
|
||||
},
|
||||
}
|
||||
|
||||
app.use(JsonViewer)
|
||||
|
@ -6,7 +6,7 @@
|
||||
<el-button
|
||||
@click="
|
||||
() => {
|
||||
this.$router.push({ path: '/sys/job', query: { view: 'fail' } })
|
||||
this.$router.push({ path: '/system/job', query: { view: 'fail' } })
|
||||
this.$emit('closed')
|
||||
}
|
||||
"
|
||||
@ -22,7 +22,7 @@
|
||||
<el-button
|
||||
@click="
|
||||
() => {
|
||||
this.$router.push({ path: '/sys/job' })
|
||||
this.$router.push({ path: '/system/job' })
|
||||
this.$emit('closed')
|
||||
}
|
||||
"
|
||||
|
@ -624,6 +624,6 @@ export default {
|
||||
链接: 'Link',
|
||||
框架: 'IFrame',
|
||||
按钮: 'Button',
|
||||
倒序排序:'Sort-Descending',
|
||||
顺序排序:'Sort-Ascending',
|
||||
倒序排序: 'Sort-Descending',
|
||||
顺序排序: 'Sort-Ascending',
|
||||
}
|
@ -622,6 +622,6 @@ export default {
|
||||
链接: '链接',
|
||||
框架: '框架',
|
||||
按钮: '按钮',
|
||||
倒序排序:'倒序排序',
|
||||
顺序排序:'顺序排序',
|
||||
倒序排序: '倒序排序',
|
||||
顺序排序: '顺序排序',
|
||||
}
|
@ -51,5 +51,12 @@ export default {
|
||||
global.user?.roles.findIndex((x) => x.ignorePermissionControl) >= 0
|
||||
? ['*/*/*']
|
||||
: tool.recursiveFindProperty(preloads[0]?.data, 'type', 'button').map((x) => x.tag)
|
||||
global.apiPermissions =
|
||||
global.user?.roles.findIndex((x) => x.ignorePermissionControl) >= 0
|
||||
? ['*/*/*']
|
||||
: preloads[1]?.data?.roles
|
||||
?.map((x) => x.apiIds.join(','))
|
||||
?.join(',')
|
||||
.split(',')
|
||||
},
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-card :header="$t('登录日志')" shadow="never">
|
||||
<login-log :keywords="$GLOBAL.user.id" :show-filter="false"></login-log>
|
||||
<login-log :ownerId="$GLOBAL.user.id.toString()" :show-filter="false"></login-log>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
|
@ -74,10 +74,19 @@
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'input',
|
||||
field: ['root', 'keywords'],
|
||||
placeholder: $t('日志编号 / 登录名 / 客户端IP'),
|
||||
type: 'select-input',
|
||||
field: [
|
||||
'dy',
|
||||
[
|
||||
{ label: $t('日志编号'), key: 'id' },
|
||||
{ label: $t('用户编号'), key: 'owner.id' },
|
||||
{ label: $t('登录名'), key: 'loginUserName' },
|
||||
{ label: $t('客户端IP'), key: 'createdClientIp' },
|
||||
],
|
||||
],
|
||||
placeholder: $t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@ -150,7 +159,15 @@ export default {
|
||||
naInfo,
|
||||
},
|
||||
computed: {},
|
||||
created() {},
|
||||
created() {
|
||||
if (this.ownerId) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.id',
|
||||
operator: 'eq',
|
||||
value: this.ownerId,
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
@ -227,8 +244,11 @@ export default {
|
||||
this.$refs.search.search()
|
||||
},
|
||||
onReset() {
|
||||
if (!this.showFilter) return
|
||||
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
|
||||
if (this.showFilter) {
|
||||
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
|
||||
}
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
if (this.ownerId) this.$refs.search.selectInputKey = 'owner.id'
|
||||
},
|
||||
//搜索
|
||||
async onSearch(form) {
|
||||
@ -255,6 +275,13 @@ export default {
|
||||
)
|
||||
}
|
||||
|
||||
if (typeof form.dy.id === 'string' && form.dy.id.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'id',
|
||||
operator: 'eq',
|
||||
value: form.dy.id,
|
||||
})
|
||||
}
|
||||
if (typeof form.dy.errorCode === 'string' && form.dy.errorCode.trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'errorCode',
|
||||
@ -279,6 +306,15 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy['owner.id'] === 'string' && form.dy['owner.id'].trim() !== '') {
|
||||
this.$refs.search.selectInputKey = 'owner.id'
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.id',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.id'],
|
||||
})
|
||||
}
|
||||
|
||||
await this.$refs.table.upData()
|
||||
},
|
||||
|
||||
@ -295,6 +331,15 @@ export default {
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (this.ownerId) {
|
||||
this.$refs.search.selectInputKey = 'owner.id'
|
||||
this.$refs.search.form.dy['owner.id'] = this.ownerId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'owner.id',
|
||||
value: this.ownerId,
|
||||
type: 'dy',
|
||||
})
|
||||
}
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keeps.push({
|
||||
@ -303,8 +348,9 @@ export default {
|
||||
type: 'root',
|
||||
})
|
||||
}
|
||||
this.onReset()
|
||||
},
|
||||
props: { keywords: { type: String }, showFilter: { type: Boolean, default: true } },
|
||||
props: { keywords: { type: String }, showFilter: { type: Boolean, default: true }, ownerId: { type: String } },
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
@ -67,7 +67,7 @@
|
||||
config: { props: { label: 'userName', value: 'id' } },
|
||||
placeholder: '用户',
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasPermission('sys/log/operation/user'),
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/user/query'),
|
||||
},
|
||||
{
|
||||
multiple: true,
|
||||
|
323
src/frontend/admin/src/views/sys/trade/index.vue
Normal file
323
src/frontend/admin/src/views/sys/trade/index.vue
Normal file
@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header v-loading="statistics.total === '...'" class="el-header-statistics">
|
||||
<el-row :gutter="15">
|
||||
<el-col :lg="24">
|
||||
<el-card shadow="never">
|
||||
<scStatistic :title="$t('总数')" :value="statistics.total" group-separator></scStatistic>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<el-header class="el-header-select-filter">
|
||||
<scSelectFilter
|
||||
:data="[
|
||||
{
|
||||
title: $t('交易方向'),
|
||||
key: 'tradeDirection',
|
||||
options: [
|
||||
{ label: '全部', value: '' },
|
||||
...Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
|
||||
return {
|
||||
value: x[0],
|
||||
label: x[1][1],
|
||||
badge: this.statistics.tradeDirection?.find((y) => y.key.tradeDirection.toLowerCase() === x[0].toLowerCase())
|
||||
?.value,
|
||||
}
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
title: $t('交易类型'),
|
||||
key: 'tradeType',
|
||||
options: [
|
||||
{ label: '全部', value: '' },
|
||||
...Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
|
||||
return {
|
||||
value: x[0],
|
||||
label: x[1][1],
|
||||
badge: this.statistics.tradeType?.find((y) => y.key.tradeType.toLowerCase() === x[0].toLowerCase())?.value,
|
||||
}
|
||||
}),
|
||||
],
|
||||
},
|
||||
]"
|
||||
:label-width="15"
|
||||
@on-change="filterChange"
|
||||
ref="selectFilter"></scSelectFilter>
|
||||
</el-header>
|
||||
<el-header>
|
||||
<div class="left-panel">
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'select-input',
|
||||
field: [
|
||||
'dy',
|
||||
[
|
||||
{ label: $t('交易编号'), key: 'id' },
|
||||
{ label: $t('用户名'), key: 'owner.userName' },
|
||||
{ label: $t('用户编号'), key: 'ownerId' },
|
||||
],
|
||||
],
|
||||
placeholder: $t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@reset="onReset"
|
||||
@search="onSearch"
|
||||
dateFormat="YYYY-MM-DD HH:mm:ss"
|
||||
dateType="datetimerange"
|
||||
dateValueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
ref="search" />
|
||||
</div>
|
||||
<div class="right-panel"></div>
|
||||
</el-header>
|
||||
<el-main class="nopadding">
|
||||
<scTable
|
||||
:context-menus="[
|
||||
'id',
|
||||
'ownerId',
|
||||
'createdTime',
|
||||
'tradeType',
|
||||
'amount',
|
||||
'balanceBefore',
|
||||
'summary',
|
||||
'owner.userName',
|
||||
'tradeDirection',
|
||||
]"
|
||||
:context-multi="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
|
||||
:context-opers="['view']"
|
||||
:default-sort="{ prop: 'id', order: 'descending' }"
|
||||
:export-api="$API.sys_wallettrade.export"
|
||||
:params="query"
|
||||
:query-api="$API.sys_wallettrade.pagedQuery"
|
||||
:vue="this"
|
||||
@data-change="getStatistics"
|
||||
@selection-change="
|
||||
(items) => {
|
||||
selection = items
|
||||
}
|
||||
"
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<naColId :label="$t('交易编号')" prop="id" sortable="custom" width="170" />
|
||||
<naColUser
|
||||
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
|
||||
:label="$t('所属用户')"
|
||||
header-align="center"
|
||||
nestProp="owner.userName"
|
||||
nestProp2="ownerId"
|
||||
prop="ownerId"
|
||||
sortable="custom"
|
||||
width="170"></naColUser>
|
||||
<naColIndicator
|
||||
:label="$t('交易方向')"
|
||||
:options="
|
||||
Object.entries(this.$GLOBAL.enums.tradeDirections).map((x) => {
|
||||
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
|
||||
})
|
||||
"
|
||||
align="center"
|
||||
prop="tradeDirection"
|
||||
sortable="custom" />
|
||||
<naColIndicator
|
||||
:label="$t('交易类型')"
|
||||
:options="
|
||||
Object.entries(this.$GLOBAL.enums.tradeTypes).map((x) => {
|
||||
return { value: x[0], text: `${x[1][1]}`, type: x[1][2], pulse: x[1][3] === 'true' }
|
||||
})
|
||||
"
|
||||
align="center"
|
||||
prop="tradeType"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.balanceBefore)"
|
||||
:label="$t('交易前余额')"
|
||||
align="right"
|
||||
prop="balanceBefore"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.amount)"
|
||||
:label="$t('发生金额')"
|
||||
align="right"
|
||||
prop="amount"
|
||||
sortable="custom" />
|
||||
<el-table-column :label="$t('交易后余额')" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ $TOOL.groupSeparator(row.balanceBefore + row.amount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('备注')" min-width="100" prop="summary" show-overflow-tooltip sortable="custom" />
|
||||
<naColOperation :buttons="[naColOperation.buttons[0]]" :vue="this" width="50" />
|
||||
</scTable>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<save-dialog
|
||||
v-if="dialog.save"
|
||||
@closed="dialog.save = null"
|
||||
@mounted="$refs.saveDialog.open(dialog.save)"
|
||||
@success="(data, mode) => $refs.table.refresh()"
|
||||
ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
const naColUser = defineAsyncComponent(() => import('@/components/naColUser'))
|
||||
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
|
||||
export default {
|
||||
components: {
|
||||
naColUser,
|
||||
saveDialog,
|
||||
},
|
||||
computed: {
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
table() {
|
||||
return table
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.ownerId) {
|
||||
this.query.dynamicFilter.filters.push({ field: 'ownerId', operator: 'eq', value: this.ownerId })
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
total: '...',
|
||||
},
|
||||
dialog: {},
|
||||
loading: false,
|
||||
query: {
|
||||
dynamicFilter: {
|
||||
filters: [],
|
||||
},
|
||||
filter: {},
|
||||
keywords: this.keywords,
|
||||
},
|
||||
selection: [],
|
||||
}
|
||||
},
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
filterChange(data) {
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
|
||||
})
|
||||
this.$refs.search.search()
|
||||
},
|
||||
|
||||
async getStatistics() {
|
||||
this.statistics.total = this.$refs.table?.total
|
||||
const res = await Promise.all([
|
||||
this.$API.sys_wallettrade.countBy.post({
|
||||
dynamicFilter: {
|
||||
filters: this.query.dynamicFilter.filters,
|
||||
},
|
||||
requiredFields: ['TradeDirection'],
|
||||
}),
|
||||
this.$API.sys_wallettrade.countBy.post({
|
||||
dynamicFilter: {
|
||||
filters: this.query.dynamicFilter.filters,
|
||||
},
|
||||
requiredFields: ['TradeType'],
|
||||
}),
|
||||
])
|
||||
this.statistics.tradeDirection = res[0].data
|
||||
this.statistics.tradeType = res[1].data
|
||||
},
|
||||
//重置
|
||||
onReset() {
|
||||
Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))
|
||||
if (this.ownerId) {
|
||||
this.$refs.search.selectInputKey = 'ownerId'
|
||||
}
|
||||
},
|
||||
//搜索
|
||||
async onSearch(form) {
|
||||
if (Array.isArray(form.dy.createdTime)) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdTime',
|
||||
operator: 'dateRange',
|
||||
value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['owner.userName'] === 'string' && form.dy['owner.userName'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.userName',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.userName'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['ownerId'] === 'string' && form.dy['ownerId'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'ownerId',
|
||||
operator: 'eq',
|
||||
value: form.dy['ownerId'],
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy['id'] === 'string' && form.dy['id'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'id',
|
||||
operator: 'eq',
|
||||
value: form.dy['id'],
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy['tradeType'] === 'string' && form.dy['tradeType'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'tradeType',
|
||||
operator: 'eq',
|
||||
value: form.dy['tradeType'],
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof form.dy['tradeDirection'] === 'string' && form.dy['tradeDirection'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'tradeDirection',
|
||||
operator: 'eq',
|
||||
value: form.dy['tradeDirection'],
|
||||
})
|
||||
}
|
||||
await this.$refs.table.upData()
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (this.ownerId) {
|
||||
this.$refs.search.selectInputKey = 'ownerId'
|
||||
this.$refs.search.form.dy.ownerId = this.ownerId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'ownerId',
|
||||
value: this.ownerId,
|
||||
type: 'dy',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'keywords',
|
||||
value: this.keywords,
|
||||
type: 'root',
|
||||
})
|
||||
}
|
||||
|
||||
this.onReset()
|
||||
},
|
||||
props: ['keywords', 'ownerId'],
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
119
src/frontend/admin/src/views/sys/trade/save.vue
Normal file
119
src/frontend/admin/src/views/sys/trade/save.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="`${titleMap[mode]}:${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<div v-loading="loading">
|
||||
<el-tabs v-model="tabId" @tab-change="tabChange" tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')" name="basic">
|
||||
<el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="15rem" ref="dialogForm">
|
||||
<el-form-item :label="$t('唯一编码')" prop="id">
|
||||
<el-input v-model="form.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易方向')" prop="tradeDirection">
|
||||
<el-input v-model="form.tradeDirection" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易类型')" prop="tradeType">
|
||||
<el-input v-model="form.tradeType" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易前余额')" prop="balanceBefore">
|
||||
<el-input v-model="form.balanceBefore" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易金额')" prop="amount">
|
||||
<el-input v-model="form.amount" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易后余额')">
|
||||
<el-input :value="form.balanceBefore + form.amount" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者部门编号')" prop="ownerDeptId">
|
||||
<el-input v-model="form.ownerDeptId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户编号')" prop="ownerId">
|
||||
<el-input v-model="form.ownerId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建者用户编号')" prop="createdUserId">
|
||||
<el-input v-model="form.createdUserId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户名')" prop="createdUserName">
|
||||
<el-input v-model="form.createdUserName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建时间')" prop="createdTime">
|
||||
<el-input v-model="form.createdTime" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<JsonViewer
|
||||
:expand-depth="5"
|
||||
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
expanded
|
||||
sort></JsonViewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
loading: true,
|
||||
mode: 'add',
|
||||
//验证规则
|
||||
rules: {},
|
||||
tabId: 'basic',
|
||||
titleMap: {
|
||||
add: this.$t('新增交易'),
|
||||
edit: this.$t('编辑交易'),
|
||||
view: this.$t('查看交易'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.visible = true
|
||||
if (data.mode === 'add') {
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
this.loading = true
|
||||
this.mode = data.mode
|
||||
if (data.row?.id) {
|
||||
const res = await this.$API.sys_wallettrade.get.post({ id: data.row.id })
|
||||
if (res.data) {
|
||||
Object.assign(this.form, res.data)
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
}
|
||||
this.$message.error(`未找到该数据`)
|
||||
return this
|
||||
},
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
//
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -116,8 +116,8 @@
|
||||
<naColAvatar :label="$t('用户名')" prop="userName" width="170" />
|
||||
<el-table-column :label="$t('手机号 / 邮箱')" align="right" prop="mobile" sortable="custom" width="250">
|
||||
<template #default="{ row }">
|
||||
<p>{{ row.mobile }}</p>
|
||||
<p>{{ row.email }}</p>
|
||||
<p>{{ row.mobile ?? '-' }}</p>
|
||||
<p>{{ row.email ?? '-' }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<naColTags
|
||||
|
@ -7,7 +7,7 @@
|
||||
destroy-on-close
|
||||
full-screen>
|
||||
<el-form
|
||||
:disabled="mode === 'view' && tabId !== 'log'"
|
||||
:disabled="mode === 'view' && tabId !== 'log' && tabId !== 'wallet' && tabId !== 'trade'"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="right"
|
||||
@ -234,6 +234,12 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('钱包信息')" name="wallet">
|
||||
<wallet v-if="tabId === 'wallet'" :id="form.id.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('交易流水')" name="trade">
|
||||
<trade v-if="tabId === 'trade'" :ownerId="form.id.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('操作日志')" name="log">
|
||||
<log v-if="tabId === 'log'" :owner-id="form.id"></log>
|
||||
</el-tab-pane>
|
||||
@ -259,12 +265,14 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const log = defineAsyncComponent(() => import('@/views/sys/log/operation'))
|
||||
const trade = defineAsyncComponent(() => import('@/views/sys/trade'))
|
||||
const wallet = defineAsyncComponent(() => import('@/views/sys/wallet'))
|
||||
const naArea = defineAsyncComponent(() => import('@/components/naArea'))
|
||||
const naDept = defineAsyncComponent(() => import('@/components/naDept'))
|
||||
const scUpload = defineAsyncComponent(() => import('@/components/scUpload'))
|
||||
const scSelect = defineAsyncComponent(() => import('@/components/scSelect'))
|
||||
export default {
|
||||
components: { log, naArea, naDept, scUpload, scSelect },
|
||||
components: { log, naArea, naDept, scUpload, scSelect, trade, wallet },
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
|
315
src/frontend/admin/src/views/sys/wallet/index.vue
Normal file
315
src/frontend/admin/src/views/sys/wallet/index.vue
Normal file
@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-header v-loading="statistics.total === '...'" class="el-header-statistics">
|
||||
<el-row :gutter="15">
|
||||
<el-col :lg="24">
|
||||
<el-card shadow="never">
|
||||
<scStatistic :title="$t('总数')" :value="statistics.total" group-separator></scStatistic>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<el-header>
|
||||
<div class="left-panel">
|
||||
<na-search
|
||||
:controls="[
|
||||
{
|
||||
type: 'select-input',
|
||||
field: [
|
||||
'dy',
|
||||
[
|
||||
{ label: $t('用户编号'), key: 'id' },
|
||||
{ label: $t('用户名'), key: 'owner.userName' },
|
||||
{ label: $t('电子邮箱'), key: 'owner.email' },
|
||||
{ label: $t('手机号'), key: 'owner.mobile' },
|
||||
],
|
||||
],
|
||||
placeholder: $t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
},
|
||||
{
|
||||
type: 'remote-select',
|
||||
field: ['filter', 'deptId'],
|
||||
api: $API.sys_dept.query,
|
||||
config: { props: { label: 'name', value: 'id' } },
|
||||
placeholder: $t('所属部门'),
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/dept/query'),
|
||||
},
|
||||
{
|
||||
type: 'remote-select',
|
||||
field: ['filter', 'roleId'],
|
||||
api: $API.sys_role.query,
|
||||
config: { props: { label: 'name', value: 'id' } },
|
||||
placeholder: $t('所属角色'),
|
||||
style: 'width:15rem',
|
||||
condition: () => $GLOBAL.hasApiPermission('api/sys/dept/query'),
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
@reset="onReset"
|
||||
@search="onSearch"
|
||||
dateFormat="YYYY-MM-DD HH:mm:ss"
|
||||
dateType="datetimerange"
|
||||
dateValueFormat="YYYY-MM-DD HH:mm:ss"
|
||||
ref="search" />
|
||||
</div>
|
||||
<div class="right-panel"></div>
|
||||
</el-header>
|
||||
<el-main class="nopadding">
|
||||
<scTable
|
||||
:context-menus="[
|
||||
'id',
|
||||
'ownerId',
|
||||
'owner.userName',
|
||||
'createdTime',
|
||||
'totalBalance',
|
||||
'availableBalance',
|
||||
'frozenBalance',
|
||||
'totalIncome',
|
||||
'totalExpenditure',
|
||||
'modifiedTime',
|
||||
]"
|
||||
:context-multi="{ id: ['createdTime'], ownerId: ['owner.userName'] }"
|
||||
:context-opers="['view']"
|
||||
:default-sort="{ prop: 'id', order: 'descending' }"
|
||||
:export-api="$API.sys_userwallet.export"
|
||||
:params="query"
|
||||
:query-api="$API.sys_userwallet.pagedQuery"
|
||||
:vue="this"
|
||||
@data-change="getStatistics"
|
||||
@selection-change="
|
||||
(items) => {
|
||||
selection = items
|
||||
}
|
||||
"
|
||||
ref="table"
|
||||
remote-filter
|
||||
remote-sort
|
||||
row-key="id"
|
||||
stripe>
|
||||
<naColId :label="$t('钱包编号')" prop="id" sortable="custom" width="170" />
|
||||
<naColUser
|
||||
:clickOpenDialog="$GLOBAL.hasApiPermission('api/sys/user/get')"
|
||||
:label="$t('所属用户')"
|
||||
nestProp="owner.userName"
|
||||
nestProp2="ownerId"
|
||||
prop="ownerId"
|
||||
sortable="custom"
|
||||
width="170"></naColUser>
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.totalBalance)"
|
||||
:label="$t('总余额')"
|
||||
align="right"
|
||||
prop="totalBalance"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.availableBalance)"
|
||||
:label="$t('可用余额')"
|
||||
align="right"
|
||||
prop="availableBalance"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.frozenBalance)"
|
||||
:label="$t('冻结余额')"
|
||||
align="right"
|
||||
prop="frozenBalance"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.totalIncome)"
|
||||
:label="$t('总收入')"
|
||||
align="right"
|
||||
prop="totalIncome"
|
||||
sortable="custom" />
|
||||
<el-table-column
|
||||
:formatter="(row) => $TOOL.groupSeparator(row.totalExpenditure)"
|
||||
:label="$t('总支出')"
|
||||
align="right"
|
||||
prop="totalExpenditure"
|
||||
sortable="custom" />
|
||||
<el-table-column v-tim :label="$t('最后交易时间')" align="right" prop="modifiedTime" sortable="custom" width="150">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.modifiedTime" v-time.tip="row.modifiedTime" :title="row.modifiedTime"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<naColOperation
|
||||
:buttons="[
|
||||
naColOperation.buttons[0],
|
||||
{
|
||||
icon: 'el-icon-plus',
|
||||
title: $t('新建交易'),
|
||||
click: async (row, vue) => {
|
||||
vue.dialog.trade = { row }
|
||||
},
|
||||
condition: () => {
|
||||
return $GLOBAL.hasApiPermission('api/sys/wallet.trade/create')
|
||||
},
|
||||
},
|
||||
]"
|
||||
:vue="this"
|
||||
width="120" />
|
||||
</scTable>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
||||
<trade-dialog
|
||||
v-if="dialog.trade"
|
||||
@closed="dialog.trade = null"
|
||||
@mounted="$refs.tradeDialog.open(dialog.trade)"
|
||||
@success="(data, mode) => $refs.table.refresh()"
|
||||
ref="tradeDialog"></trade-dialog>
|
||||
|
||||
<save-dialog
|
||||
v-if="dialog.save"
|
||||
@closed="dialog.save = null"
|
||||
@mounted="$refs.saveDialog.open(dialog.save)"
|
||||
@success="(data, mode) => $refs.table.refresh()"
|
||||
ref="saveDialog"></save-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
const tradeDialog = defineAsyncComponent(() => import('./trade.vue'))
|
||||
const saveDialog = defineAsyncComponent(() => import('./save.vue'))
|
||||
const naColUser = defineAsyncComponent(() => import('@/components/naColUser'))
|
||||
export default {
|
||||
components: {
|
||||
tradeDialog,
|
||||
saveDialog,
|
||||
naColUser,
|
||||
},
|
||||
computed: {
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
table() {
|
||||
return table
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.roleId) {
|
||||
this.query.filter.roleId = this.roleId
|
||||
}
|
||||
if (this.deptId) {
|
||||
this.query.filter.deptId = this.deptId
|
||||
}
|
||||
if (this.id) {
|
||||
this.query.dynamicFilter.filters.push({ field: 'id', operator: 'eq', value: this.id })
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
total: '...',
|
||||
},
|
||||
dialog: {},
|
||||
loading: false,
|
||||
query: {
|
||||
dynamicFilter: {
|
||||
filters: [],
|
||||
},
|
||||
filter: {},
|
||||
keywords: this.keywords,
|
||||
},
|
||||
selection: [],
|
||||
}
|
||||
},
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
async getStatistics() {
|
||||
this.statistics.total = this.$refs.table?.total
|
||||
},
|
||||
//重置
|
||||
onReset() {
|
||||
if (this.id) {
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
}
|
||||
},
|
||||
//搜索
|
||||
async onSearch(form) {
|
||||
if (Array.isArray(form.dy.createdTime)) {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'createdTime',
|
||||
operator: 'dateRange',
|
||||
value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['owner.userName'] === 'string' && form.dy['owner.userName'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.userName',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.userName'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['owner.email'] === 'string' && form.dy['owner.email'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.email',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.email'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['owner.mobile'] === 'string' && form.dy['owner.mobile'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'owner.mobile',
|
||||
operator: 'eq',
|
||||
value: form.dy['owner.mobile'],
|
||||
})
|
||||
}
|
||||
if (typeof form.dy['id'] === 'string' && form.dy['id'].trim() !== '') {
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: 'id',
|
||||
operator: 'eq',
|
||||
value: form.dy['id'],
|
||||
})
|
||||
}
|
||||
await this.$refs.table.upData()
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
if (this.keywords) {
|
||||
this.$refs.search.form.root.keywords = this.keywords
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'keywords',
|
||||
value: this.keywords,
|
||||
type: 'root',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.id) {
|
||||
this.$refs.search.selectInputKey = 'id'
|
||||
this.$refs.search.form.dy.id = this.id
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'id',
|
||||
value: this.id,
|
||||
type: 'dy',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.roleId) {
|
||||
this.$refs.search.form.filter.roleId = this.roleId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'roleId',
|
||||
value: this.roleId,
|
||||
type: 'filter',
|
||||
})
|
||||
}
|
||||
if (this.deptId) {
|
||||
this.$refs.search.form.filter.deptId = this.deptId
|
||||
this.$refs.search.keeps.push({
|
||||
field: 'deptId',
|
||||
value: this.deptId,
|
||||
type: 'filter',
|
||||
})
|
||||
}
|
||||
|
||||
this.onReset()
|
||||
},
|
||||
props: ['keywords', 'roleId', 'deptId', 'id'],
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
124
src/frontend/admin/src/views/sys/wallet/save.vue
Normal file
124
src/frontend/admin/src/views/sys/wallet/save.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="`${titleMap[mode]}:${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close full-screen>
|
||||
<div v-loading="loading">
|
||||
<el-tabs v-model="tabId" @tab-change="tabChange" tab-position="top">
|
||||
<el-tab-pane :label="$t('基本信息')" name="basic">
|
||||
<el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="15rem" ref="dialogForm">
|
||||
<el-form-item :label="$t('唯一编码')" prop="id">
|
||||
<el-input v-model="form.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('总余额')" prop="totalBalance">
|
||||
<el-input v-model="form.totalBalance" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('可用余额')" prop="availableBalance">
|
||||
<el-input v-model="form.availableBalance" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('冻结余额')" prop="frozenBalance">
|
||||
<el-input v-model="form.frozenBalance" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('总收入')" prop="totalIncome">
|
||||
<el-input v-model="form.totalIncome" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('总支出')" prop="totalExpenditure">
|
||||
<el-input v-model="form.totalExpenditure" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者部门编号')" prop="ownerDeptId">
|
||||
<el-input v-model="form.ownerDeptId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('所有者用户编号')" prop="ownerId">
|
||||
<el-input v-model="form.ownerId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('创建时间')" prop="createdTime">
|
||||
<el-input v-model="form.createdTime" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('修改时间')" prop="modifiedTime">
|
||||
<el-input v-model="form.modifiedTime" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('数据版本')" prop="version">
|
||||
<el-input v-model="form.version" :disabled="mode === 'edit'" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('交易流水')" name="trade">
|
||||
<trade v-if="tabId === 'trade'" :ownerId="form.ownerId.toString()" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
|
||||
<JsonViewer
|
||||
:expand-depth="5"
|
||||
:theme="this.$TOOL.data.get('APP_SET_DARK') || this.$CONFIG.APP_SET_DARK ? 'dark' : 'light'"
|
||||
:value="form"
|
||||
copyable
|
||||
expanded
|
||||
sort></JsonViewer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const trade = defineAsyncComponent(() => import('@/views/sys/trade'))
|
||||
export default {
|
||||
components: { trade },
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
loading: true,
|
||||
mode: 'add',
|
||||
//验证规则
|
||||
rules: {},
|
||||
tabId: 'basic',
|
||||
titleMap: {
|
||||
add: this.$t('新增钱包'),
|
||||
edit: this.$t('编辑钱包'),
|
||||
view: this.$t('查看钱包'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.visible = true
|
||||
if (data.mode === 'add') {
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
this.loading = true
|
||||
this.mode = data.mode
|
||||
if (data.row?.id) {
|
||||
const res = await this.$API.sys_userwallet.get.post({ id: data.row.id })
|
||||
if (res.data) {
|
||||
Object.assign(this.form, res.data)
|
||||
this.loading = false
|
||||
return this
|
||||
}
|
||||
}
|
||||
this.$message.error(`未找到该数据`)
|
||||
return this
|
||||
},
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
//
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
91
src/frontend/admin/src/views/sys/wallet/trade.vue
Normal file
91
src/frontend/admin/src/views/sys/wallet/trade.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<scDialog v-model="visible" :title="$t('新建交易')" @closed="$emit('closed')" append-to-body destroy-on-close>
|
||||
<el-form :model="form" :rules="rules" label-position="right" label-width="12rem" ref="dialogForm" style="height: 100%">
|
||||
<el-form-item>
|
||||
<el-descriptions border column="1">
|
||||
<el-descriptions-item :label="$t('用户名')">
|
||||
<b>{{ row.owner.userName }}</b>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('用户编号')">{{ row.owner.id }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('可用余额')">{{ $TOOL.groupSeparator(row.availableBalance) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易类型')" prop="tradeType">
|
||||
<el-select v-model="form.tradeType" clearable filterable>
|
||||
<el-option v-for="(item, i) in $GLOBAL.enums.tradeTypes" :key="i" :label="item[1]" :value="i" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('交易金额')" prop="amount">
|
||||
<el-input-number v-model="form.amount" :max="999999999" :min="-999999999" precision="0" style="width: 15rem"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('备注')" prop="summary">
|
||||
<el-input v-model="form.summary" rows="3" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">{{ $t('取消') }}</el-button>
|
||||
<el-button v-if="mode !== 'view'" :disabled="loading" :loading="loading" @click="submit" type="primary">{{ $t('保存') }}</el-button>
|
||||
</template>
|
||||
</scDialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
//表单数据
|
||||
form: {},
|
||||
row: {},
|
||||
loading: true,
|
||||
|
||||
//验证规则
|
||||
rules: {
|
||||
tradeType: [{ required: true, message: this.$t('请选择交易类型') }],
|
||||
amount: [{ required: true, message: this.$t('请输入交易金额') }],
|
||||
},
|
||||
tabId: '0',
|
||||
titleMap: {
|
||||
add: this.$t('新增用户'),
|
||||
edit: this.$t('编辑用户'),
|
||||
view: this.$t('查看用户'),
|
||||
},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
emits: ['success', 'closed', 'mounted'],
|
||||
methods: {
|
||||
//显示
|
||||
async open(data) {
|
||||
this.row = data.row
|
||||
this.form.ownerId = data.row.id
|
||||
this.visible = true
|
||||
this.loading = false
|
||||
return this
|
||||
},
|
||||
|
||||
//表单提交方法
|
||||
async submit() {
|
||||
const valid = await this.$refs.dialogForm.validate().catch(() => {})
|
||||
if (!valid) {
|
||||
return false
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await this.$API.sys_wallettrade.create.post(this.form)
|
||||
this.$emit('success', res.data, this.mode)
|
||||
this.visible = false
|
||||
this.$message.success(this.$t('操作成功'))
|
||||
} catch {}
|
||||
this.loading = false
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$emit('mounted')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
Reference in New Issue
Block a user