| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- <template>
- <div class="cf-value admin-page">
- <div class="header">
- <h2>CF值(平台积分)管理</h2>
- <div class="filters">
- <el-input v-model="searchUserId" placeholder="用户ID/用户名搜索" style="width: 200px;" clearable @keyup.enter.native="searchBalance" />
- <el-button type="primary" @click="searchBalance">查询余额</el-button>
- </div>
- </div>
- <el-tabs v-model="activeTab">
- <el-tab-pane label="余额列表" name="balances">
- <div class="adm-table-wrap">
- <el-table :data="balanceList" v-loading="loadingBalance" border stripe height="100%">
- <el-table-column prop="id" label="ID" width="70">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('id', $event)">ID<span v-if="sortIndex('id') !== null" class="sort-index" :class="sortClass('id') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('id') }}</span>{{ sortIcon('id') }}</span>
- </template>
- </el-table-column>
- <el-table-column label="用户" width="140">
- <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
- </el-table-column>
- <el-table-column prop="familyId" label="家庭ID" width="100">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('familyId', $event)">家庭ID<span v-if="sortIndex('familyId') !== null" class="sort-index" :class="sortClass('familyId') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('familyId') }}</span>{{ sortIcon('familyId') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="totalEarned" label="累计获得" width="100">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('totalEarned', $event)">累计获得<span v-if="sortIndex('totalEarned') !== null" class="sort-index" :class="sortClass('totalEarned') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('totalEarned') }}</span>{{ sortIcon('totalEarned') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="available" label="可用" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('available', $event)">可用<span v-if="sortIndex('available') !== null" class="sort-index" :class="sortClass('available') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('available') }}</span>{{ sortIcon('available') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="frozen" label="冻结" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('frozen', $event)">冻结<span v-if="sortIndex('frozen') !== null" class="sort-index" :class="sortClass('frozen') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('frozen') }}</span>{{ sortIcon('frozen') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="withdrawn" label="已提现" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('withdrawn', $event)">已提现<span v-if="sortIndex('withdrawn') !== null" class="sort-index" :class="sortClass('withdrawn') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('withdrawn') }}</span>{{ sortIcon('withdrawn') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="exchanged" label="已兑换" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('exchanged', $event)">已兑换<span v-if="sortIndex('exchanged') !== null" class="sort-index" :class="sortClass('exchanged') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('exchanged') }}</span>{{ sortIcon('exchanged') }}</span>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="100">
- <template slot-scope="{ row }">
- <el-button type="text" size="small" @click="viewLogs(row.userId)">流水</el-button>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <el-pagination v-if="balanceTotal > 0" :current-page="balancePage" :page-size="balanceSize" :total="balanceTotal" layout="prev, pager, next" @current-change="pageBalances" />
- </el-tab-pane>
- <el-tab-pane label="流水查询" name="logs">
- <div class="filters">
- <el-input v-model="logUserId" placeholder="用户ID" style="width: 200px;" clearable @keyup.enter.native="searchLogs" />
- <el-button type="primary" @click="searchLogs">查询流水</el-button>
- </div>
- <div class="adm-table-wrap">
- <el-table :data="logList" v-loading="loadingLog" border stripe height="100%">
- <el-table-column prop="id" label="ID" width="70">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('id', $event)">ID<span v-if="sortIndex('id') !== null" class="sort-index" :class="sortClass('id') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('id') }}</span>{{ sortIcon('id') }}</span>
- </template>
- </el-table-column>
- <el-table-column label="用户" width="120">
- <template slot-scope="{ row }">{{ row.userName || '用户' + row.userId }}</template>
- </el-table-column>
- <el-table-column prop="type" label="类型" width="100">
- <template slot-scope="{ row }">
- <el-tag :type="typeTag(row.type)" size="small">{{ row.type }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="amount" label="数量" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('amount', $event)">数量<span v-if="sortIndex('amount') !== null" class="sort-index" :class="sortClass('amount') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('amount') }}</span>{{ sortIcon('amount') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="balanceAfter" label="变动后余额" width="100">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('balanceAfter', $event)">变动后余额<span v-if="sortIndex('balanceAfter') !== null" class="sort-index" :class="sortClass('balanceAfter') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('balanceAfter') }}</span>{{ sortIcon('balanceAfter') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="refType" label="来源" width="130">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('refType', $event)">来源<span v-if="sortIndex('refType') !== null" class="sort-index" :class="sortClass('refType') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('refType') }}</span>{{ sortIcon('refType') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="refId" label="关联ID" width="80">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('refId', $event)">关联ID<span v-if="sortIndex('refId') !== null" class="sort-index" :class="sortClass('refId') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('refId') }}</span>{{ sortIcon('refId') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip>
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('remark', $event)">备注<span v-if="sortIndex('remark') !== null" class="sort-index" :class="sortClass('remark') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('remark') }}</span>{{ sortIcon('remark') }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="createdAt" label="时间" width="160">
- <template slot="header">
- <span class="sort-header" @click.stop="onSortClick('createdAt', $event)">时间<span v-if="sortIndex('createdAt') !== null" class="sort-index" :class="sortClass('createdAt') === 'sort-asc' ? 'sort-asc-badge' : 'sort-desc-badge'">{{ sortIndex('createdAt') }}</span>{{ sortIcon('createdAt') }}</span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <el-pagination v-if="logTotal > 0" :current-page="logPage" :page-size="logSize" :total="logTotal" layout="prev, pager, next" @current-change="pageLogs" />
- </el-tab-pane>
- </el-tabs>
- </div>
- </template>
- <script>
- import SortMixin from '@/mixins/SortMixin'
- import ListStateMixin from '@/mixins/ListStateMixin'
- import { getCfValueBalance, getCfValueLogs, getCfValueAllBalances, searchUsers } from '@/api/admin'
- export default {
- name: 'CfValueManage',
- mixins: [SortMixin, ListStateMixin],
- data() {
- return {
- activeTab: 'balances',
- searchUserId: '',
- logUserId: '',
- sortableColumns: { id: 'ID', createdAt: '时间', familyId: '家庭ID', totalEarned: '累计获得', available: '可用', frozen: '冻结', withdrawn: '已提现', exchanged: '已兑换', amount: '数量', balanceAfter: '变动后余额', refType: '来源', refId: '关联ID', remark: '备注' },
- // Balances
- balanceList: [],
- loadingBalance: false,
- balancePage: 1,
- balanceSize: 20,
- balanceTotal: 0,
- // Logs
- logList: [],
- loadingLog: false,
- logPage: 1,
- logSize: 20,
- logTotal: 0
- }
- },
- mounted() {
- this.loadBalances()
- },
- methods: {
- resolveUserNames(items, idField) {
- if (!items || items.length === 0) return Promise.resolve()
- var ids = []
- items.forEach(function(item) {
- if (item[idField] && ids.indexOf(item[idField]) === -1) ids.push(item[idField])
- })
- var nameMap = {}
- var self = this
- return Promise.all(ids.map(function(id) {
- return searchUsers({ keyword: String(id) })
- .then(function(res) {
- if (res.data && res.data.length > 0) {
- var u = res.data[0]
- nameMap[id] = u.nickname || u.realName || '用户' + id
- } else {
- nameMap[id] = '用户' + id
- }
- })
- .catch(function() {
- nameMap[id] = '用户' + id
- })
- })).then(function() {
- items.forEach(function(item) {
- item.userName = nameMap[item[idField]] || '用户' + item[idField]
- })
- })
- },
- typeTag(type) {
- const map = { earn: 'success', spend: 'warning', freeze: 'info', unfreeze: 'info', withdraw: 'primary' }
- return map[type] || 'info'
- },
- loadList() { this.loadBalances() },
- loadData() { this.searchLogs() },
- loadBalances() {
- this.loadingBalance = true
- const params = { page: this.balancePage, size: this.balanceSize }
- if (this.sortState.length > 0) params.sort = this.sortState
- getCfValueAllBalances(params).then(async res => {
- if (res.code === 200) {
- this.balanceList = res.data.records || []
- this.balanceTotal = res.data.total || 0
- await this.resolveUserNames(this.balanceList, 'userId')
- }
- }).finally(() => { this.loadingBalance = false })
- },
- pageBalances(page) {
- this.balancePage = page
- this.loadBalances()
- },
- searchBalance() {
- if (!this.searchUserId) { this.loadBalances(); return }
- this.loadingBalance = true
- getCfValueBalance({ userId: this.searchUserId }).then(async res => {
- if (res.code === 200) {
- this.balanceList = [res.data]
- this.balanceTotal = 1
- await this.resolveUserNames(this.balanceList, 'userId')
- }
- }).finally(() => { this.loadingBalance = false })
- },
- viewLogs(userId) {
- this.logUserId = String(userId)
- this.activeTab = 'logs'
- this.searchLogs()
- },
- searchLogs() {
- if (!this.logUserId) return
- this.loadingLog = true
- this.logPage = 1
- const params = { userId: this.logUserId, page: 1, size: this.logSize }
- if (this.sortState.length > 0) params.sort = this.sortState
- getCfValueLogs(params).then(async res => {
- if (res.code === 200) {
- this.logList = res.data.records || []
- this.logTotal = res.data.total || 0
- await this.resolveUserNames(this.logList, 'userId')
- }
- }).finally(() => { this.loadingLog = false })
- },
- pageLogs(page) {
- this.logPage = page
- this.searchLogs()
- }
- }
- }
- </script>
- <style scoped>
- .cf-value { padding: 20px; }
- .sort-header {
- cursor: pointer;
- user-select: none;
- font-weight: 500;
- font-size: 12px;
- }
- .sort-header:hover {
- color: #409eff;
- }
- </style>
|