Files
jk8_admin/web/src/views/backend/user/moneyLog/popupForm.vue

358 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!-- 对话框表单 -->
<el-dialog
class="ba-operate-dialog"
:close-on-click-modal="false"
:model-value="['Add', 'Edit'].includes(baTable.form.operate!)"
@close="baTable.toggleForm"
>
<template #header>
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
</div>
</template>
<el-scrollbar class="ba-table-form-scrollbar">
<div
class="ba-operate-form"
:class="'ba-' + baTable.form.operate + '-form'"
:style="config.layout.shrink ? '' : 'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
>
<el-form
ref="formRef"
@keyup.enter="submitForm"
:model="baTable.form.items"
:label-position="config.layout.shrink ? 'top' : 'right'"
:label-width="baTable.form.labelWidth + 'px'"
:rules="rules"
v-if="!baTable.form.loading"
>
<template v-if="baTable.form.operate == 'Edit'">
<el-form-item label="Date & Time">
<el-date-picker v-model="state.editTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" />
<el-radio-group v-model="state.timeMode" class="inline-mode">
<el-radio value="Auto">Auto</el-radio>
<el-radio value="Manual">Manual</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Category">
<el-select v-model="baTable.form.items!.category">
<el-option label="Customer" :value="1" />
<el-option label="Other Adjust" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="Type" prop="type">
<el-radio-group v-model="baTable.form.items!.type">
<el-radio-button :value="1">Deposit</el-radio-button>
<el-radio-button :value="2">Withdraw</el-radio-button>
<el-radio-button :value="3">IN</el-radio-button>
<el-radio-button :value="4">OUT</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="Username">
<el-input v-model="baTable.form.items!.user_name" disabled placeholder="e.g. PLAYER001" />
</el-form-item>
<el-form-item label="Remark" prop="memo">
<el-input v-model="baTable.form.items!.memo" placeholder="(optional)" />
</el-form-item>
<el-form-item label="Amount (AUD)" prop="money">
<el-input-number v-model="baTable.form.items!.money" :min="0" :precision="2" />
</el-form-item>
</template>
<template v-else>
<FormItem
type="remoteSelect"
prop="user_id"
:label="t('user.moneyLog.User ID')"
v-model="baTable.form.items!.user_id"
:placeholder="t('Click select')"
:input-attr="{
pk: 'user.id',
field: 'nickname_text',
remoteUrl: '/admin/user.User/index',
onChange: getAdd,
}"
/>
</template>
<el-form-item :label="baTable.form.operate == 'Edit' ? 'Bank' : t('user.moneyLog.bank_id')" prop="bank_id">
<el-select
v-model="baTable.form.items!.bank_id"
filterable
remote
:placeholder="t('Please select bank account')"
:remote-method="getBankList"
:loading="state.bankLoading"
clearable
style="width: 100%"
>
<el-option v-for="item in state.bankOptions" :key="item.id" :label="item.bank_name" :value="item.id">
<div style="display: flex; justify-content: space-between; align-items: center">
<span>{{ item.bank_name }}</span>
<span style="color: #a8abb2; font-size: 12px">{{ item.bank_account }}</span>
</div>
</el-option>
</el-select>
</el-form-item>
<template v-if="baTable.form.operate == 'Edit'">
<el-form-item label="Label">
<el-select v-model="baTable.form.items!.label" placeholder="- (optional) -" clearable>
<el-option label="First Deposit" :value="1" />
<el-option label="Unclaim" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="Game Ticket Auto">
<el-checkbox v-model="state.gameTicketAuto">Auto generate ticket</el-checkbox>
</el-form-item>
</template>
<template v-else>
<FormItem
:label="t('user.moneyLog.type')"
type="select"
v-model="baTable.form.items!.type"
prop="type"
:input-attr="{
content: {
'1': t('user.moneyLog.type_list.1'),
'2': t('user.moneyLog.type_list.2'),
},
}"
:placeholder="t('Please select field', { field: t('user.moneyLog.type') })"
@change="onTypeChange"
/>
<el-form-item prop="money" :label="t('user.moneyLog.Change amount')">
<el-input
v-model="baTable.form.items!.money"
type="number"
:placeholder="t('user.moneyLog.Please enter the balance change amount')"
></el-input>
</el-form-item>
<el-form-item prop="memo" :label="t('user.moneyLog.remarks')">
<el-input
@keyup.enter.stop=""
@keyup.ctrl.enter="submitForm"
v-model="baTable.form.items!.memo"
type="textarea"
:placeholder="t('user.moneyLog.Please enter change remarks / description')"
></el-input>
</el-form-item>
</template>
</el-form>
</div>
</el-scrollbar>
<template #footer>
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
<el-button @click="baTable.toggleForm('')">{{ t('Cancel') }}</el-button>
<el-button v-blur :loading="baTable.form.submitLoading" @click="submitForm" type="primary">
{{ baTable.form.operateIds!.length > 1 ? t('Save and edit next item') : t('Save') }}
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { reactive, inject, watch, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import type baTableClass from '/@/utils/baTable'
import { add } from '/@/api/backend/user/moneyLog'
import FormItem from '/@/components/formItem/index.vue'
import type { FormItemRule } from 'element-plus'
import { buildValidatorData } from '/@/utils/validate'
import { useConfig } from '/@/stores/config'
import createAxios from '/@/utils/axios'
const getBankList = (query: string = '') => {
state.bankLoading = true
createAxios({
url: '/admin/Bank/index',
method: 'GET',
params: {
// BuildAdmin 后端标准的搜索与分页格式
quickSearch: query,
limit: 20,
},
})
.then((res) => {
// BuildAdmin 列表的标准返回结构是 res.data.list
state.bankOptions = res.data.list || []
})
.finally(() => {
state.bankLoading = false
})
}
const config = useConfig()
const { t } = useI18n()
const baTable = inject('baTable') as baTableClass
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
bank_id: [buildValidatorData({ name: 'required', message: t('Please select field', { field: t('user.moneyLog.bank_id') }) })],
type: [buildValidatorData({ name: 'required', message: t('Please select field', { field: t('user.moneyLog.type') }) })],
money: [
buildValidatorData({ name: 'required', title: t('user.moneyLog.Change amount') }),
{
validator: (rule: any, val: string, callback: Function) => {
if (!val || (parseFloat(val) <= 0 && baTable.form.operate == 'Add')) {
return callback(new Error(t('Please enter the correct field', { field: t('user.moneyLog.Change amount') })))
}
return callback()
},
trigger: 'blur',
},
],
memo: [buildValidatorData({ name: 'required', title: t('user.moneyLog.remarks') })],
})
const formRef = useTemplateRef('formRef')
const state: {
userInfo: anyObj
after: number
bankOptions: any[]
bankLoading: boolean
editTime: string
timeMode: 'Auto' | 'Manual'
gameTicketAuto: boolean
} = reactive({
userInfo: {},
after: 0,
bankOptions: [],
bankLoading: false,
editTime: '',
timeMode: 'Manual',
gameTicketAuto: true,
})
const getAdd = () => {
if (!baTable.form.items!.user_id || parseInt(baTable.form.items!.user_id) <= 0) {
return
}
add(baTable.form.items!.user_id).then((res) => {
state.userInfo = res.data.user
state.after = res.data.user.money
})
}
watch(
() => [baTable.form.items!.money, baTable.form.items!.type],
([newMoney, newType]) => {
if (!state.userInfo || Object.keys(state.userInfo).length === 0) {
state.after = 0
return
}
const inputAmount = newMoney === '' ? 0 : parseFloat(newMoney as string)
const currentMoney = baTable.form.operate == 'Edit' ? parseFloat(baTable.form.items!.before) : parseFloat(state.userInfo.money)
// 核心逻辑判断
let result = 0
if (newType == 2) {
// 类型 2减法
result = currentMoney - inputAmount
} else {
// 其他(类型 1加法
result = currentMoney + inputAmount
}
state.after = parseFloat(result.toFixed(2))
},
{ deep: true } // 深度监听
)
const pad = (number: number) => number.toString().padStart(2, '0')
const formatDateTime = (value: unknown) => {
const timestamp = Number(value)
if (!Number.isFinite(timestamp) || !timestamp) return ''
const date = new Date(timestamp * 1000)
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
const timestampFromEditTime = () => {
if (state.timeMode === 'Auto' || !state.editTime) {
return Math.floor(Date.now() / 1000)
}
const timestamp = Math.floor(new Date(state.editTime.replace(' ', 'T')).getTime() / 1000)
return Number.isFinite(timestamp) ? timestamp : Math.floor(Date.now() / 1000)
}
const onTypeChange = (val: number | string) => {
baTable.form.items!.type = val
}
const findTableRow = (rows: TableRow[] = [], id: number | string): TableRow | undefined => {
for (const row of rows) {
if (row.id == id) return row
if (row.children) {
const child = findTableRow(row.children, id)
if (child) return child
}
}
}
const getUsername = (items: anyObj) => {
const tableRow = findTableRow(baTable.table.data, items.id)
return items.user?.jk_username || tableRow?.user?.jk_username || ''
}
const initEditForm = () => {
const items = baTable.form.items
if (!items || baTable.form.operate !== 'Edit') return
items.category = items.category || 1
if (!items.user_name) {
items.user_name = getUsername(items)
}
items.bank_id = items.bank_id || items.bank?.id || ''
items.label = items.label || ''
state.editTime = formatDateTime(items.create_time)
state.timeMode = 'Manual'
state.gameTicketAuto =
items.game_ticket !== undefined ? Number(items.game_ticket) === 1 : Array.isArray(items.scoreLog) && items.scoreLog.length > 0
if (items.bank && !state.bankOptions.some((bank) => bank.id === items.bank_id)) {
state.bankOptions.unshift(items.bank)
}
}
const submitForm = () => {
if (baTable.form.operate === 'Edit') {
baTable.form.items!.create_time = timestampFromEditTime()
baTable.form.items!.game_ticket = state.gameTicketAuto ? 1 : 0
}
baTable.onSubmit(formRef.value)
}
// 打开表单时刷新用户数据
watch(
() => baTable.form.operate,
(newValue) => {
if (newValue) {
getAdd()
getBankList()
initEditForm()
}
}
)
watch(
() => baTable.form.items,
() => {
initEditForm()
}
)
</script>
<style scoped lang="scss">
.preview-img {
width: 60px;
height: 60px;
}
.inline-mode {
margin-left: 12px;
}
:deep(.el-form-item .el-select),
:deep(.el-form-item .el-input) {
width: 100%;
}
</style>