最新
This commit is contained in:
6
internal/model/merchant.go
Normal file
6
internal/model/merchant.go
Normal file
@ -0,0 +1,6 @@
|
||||
type StoreData struct {
|
||||
g.Meta `orm:"table:stores"`
|
||||
Id int `json:"id" orm:"id"`
|
||||
MerchantId int `json:"merchantId" orm:"merchant_id"`
|
||||
StoreName string `json:"storeName" orm:"name"`
|
||||
}
|
||||
@ -8,3 +8,18 @@ export function feedbackList(params) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function feedbackDetail(id) {
|
||||
return request({
|
||||
url: `/x/feedback/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function feedbackReply(data) {
|
||||
return request({
|
||||
url: '/x/feedback/reply',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
45
src/api/game.js
Normal file
45
src/api/game.js
Normal file
@ -0,0 +1,45 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取游戏列表,带分页参数
|
||||
export function getGameList(params) {
|
||||
return request({
|
||||
url: '/x/game',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 新增游戏
|
||||
export function addGame(data) {
|
||||
return request({
|
||||
url: '/x/game',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑游戏
|
||||
export function editGame(data) {
|
||||
return request({
|
||||
url: '/x/game',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除游戏
|
||||
export function deleteGame(id) {
|
||||
return request({
|
||||
url: `/x/game/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 上传游戏图标
|
||||
export function uploadGameImg(data) {
|
||||
return request({
|
||||
url: '/x/upload/game',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
export function merchantList(params){
|
||||
return request({
|
||||
url: '/x/merchant',
|
||||
@ -16,3 +15,19 @@ export function merchantAudit(data) {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function merchantCreate(data) {
|
||||
return request({
|
||||
url: '/x/merchant/audit',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function merchantUpdate(data) {
|
||||
return request({
|
||||
url: '/x/merchant/audit',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
50
src/api/reward-type.js
Normal file
50
src/api/reward-type.js
Normal file
@ -0,0 +1,50 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取奖励类型列表
|
||||
export function getRewardTypeList(params) {
|
||||
return request({
|
||||
url: '/x/rewardType',
|
||||
method: 'get',
|
||||
params: {
|
||||
page: params.page || 1,
|
||||
size: params.size || 10,
|
||||
name: params.name,
|
||||
status: params.status,
|
||||
storeId: params.storeId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 新增奖励类型
|
||||
export function addRewardType(data) {
|
||||
return request({
|
||||
url: '/x/rewardType',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑奖励类型
|
||||
export function updateRewardType(data) {
|
||||
return request({
|
||||
url: `/x/rewardType`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除奖励类型
|
||||
export function deleteRewardType(id) {
|
||||
return request({
|
||||
url: `/x/rewardType/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取门店列表
|
||||
export function getStoreList() {
|
||||
return request({
|
||||
url: '/x/store/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
45
src/api/reward.js
Normal file
45
src/api/reward.js
Normal file
@ -0,0 +1,45 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取任务列表
|
||||
export function getRewardSystemList(params) {
|
||||
return request({
|
||||
url: '/x/reward',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 新增奖励
|
||||
export function addReward(data) {
|
||||
return request({
|
||||
url: '/x/reward',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑奖励
|
||||
export function updateReward(data) {
|
||||
return request({
|
||||
url: '/x/reward',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除奖励
|
||||
export function deleteReward(id) {
|
||||
return request({
|
||||
url: `/x/reward/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 上传图标
|
||||
export function uploadReward(data) {
|
||||
return request({
|
||||
url: `/x/upload/reward`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
10
src/api/store.js
Normal file
10
src/api/store.js
Normal file
@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 获取门店列表
|
||||
export function getStoreList(params) {
|
||||
return request({
|
||||
url: '/x/store',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
40
src/api/task.js
Normal file
40
src/api/task.js
Normal file
@ -0,0 +1,40 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
|
||||
// 获取商户和门店列表
|
||||
export function getMerchantAndStoreList() {
|
||||
return request({
|
||||
url: '/x/task/selector',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
export function getTaskList({ netbarAccount, gid, num = 10, pageidx }) {
|
||||
return request({
|
||||
url: '/x/task/getNonLoginTaskList',
|
||||
method: 'get',
|
||||
params: {
|
||||
netbarAccount,
|
||||
gid,
|
||||
num,
|
||||
...(pageidx !== undefined ? { pageidx } : {})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作任务奖励
|
||||
* @param {Object} data
|
||||
* @param {number} data.type - 操作类型(必填):1=添加,2=删除
|
||||
* @param {number} data.taskId - 任务ID(必填)
|
||||
* @param {number} data.rewardId - 奖励ID(必填)
|
||||
* @returns {Promise<{success: boolean}>}
|
||||
*/
|
||||
export function operateTaskReward(data) {
|
||||
return request({
|
||||
url: '/x/reward/taskReward',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -9,23 +9,6 @@ export function userList(params) {
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function deleteUser(id) {
|
||||
return request({
|
||||
url: `/api/users/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除用户
|
||||
export function batchDeleteUsers(ids) {
|
||||
return request({
|
||||
url: '/api/users/batch',
|
||||
method: 'delete',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 更新用户状态
|
||||
export function updateUserStatus(id, status) {
|
||||
return request({
|
||||
|
||||
@ -18,9 +18,9 @@ import '@/permission' // permission control
|
||||
|
||||
|
||||
// set ElementUI lang to EN
|
||||
Vue.use(ElementUI, { locale })
|
||||
// Vue.use(ElementUI, { locale })
|
||||
// 如果想要中文版 element-ui,按如下方式声明
|
||||
// Vue.use(ElementUI)
|
||||
Vue.use(ElementUI)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
||||
@ -70,6 +70,7 @@ export const constantRoutes = [
|
||||
redirect: '/agent/merchant-list',
|
||||
name: 'AgentCenter',
|
||||
meta: { title: '代理商管理', icon: 'el-icon-user' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'merchant-list',
|
||||
@ -79,7 +80,55 @@ export const constantRoutes = [
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/system',
|
||||
component: Layout,
|
||||
redirect: '/system/index',
|
||||
name: 'SystemConfig',
|
||||
meta: { title: '配置中心', icon: 'el-icon-setting' },
|
||||
children: [
|
||||
{
|
||||
path: 'game-config',
|
||||
name: 'GameConfig',
|
||||
component: () => import('@/views/system/game.vue'),
|
||||
meta: { title: '游戏配置' }
|
||||
},
|
||||
{
|
||||
path: 'reward-type',
|
||||
name: 'RewardType',
|
||||
component: () => import('@/views/system/reward-type.vue'),
|
||||
meta: { title: '奖励类型配置' }
|
||||
},
|
||||
{
|
||||
path: 'reward-list',
|
||||
name: 'RewardList',
|
||||
component: () => import('@/views/system/reward-list.vue'),
|
||||
meta: { title: '奖励配置' }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/activity',
|
||||
component: Layout,
|
||||
redirect: '/activity/game-task',
|
||||
name: 'ActivityCenter',
|
||||
meta: { title: '活动中心', icon: 'el-icon-star-on' },
|
||||
alwaysShow: true,
|
||||
children: [
|
||||
{
|
||||
path: 'game-task',
|
||||
name: 'GameTask',
|
||||
component: () => import('@/views/activity/game-task'),
|
||||
meta: { title: '游戏任务' }
|
||||
},
|
||||
{
|
||||
path: 'test',
|
||||
name: 'Test',
|
||||
component: () => import('@/views/activity/test'),
|
||||
meta: { title: '游戏任务' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
||||
|
||||
@ -54,9 +54,14 @@ service.interceptors.response.use(
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
if (res.code == 7) {
|
||||
if (res.code === 7) {
|
||||
window.location = "/";
|
||||
}
|
||||
if (res.code === 8) {
|
||||
store.dispatch("user/resetToken").then(() => {
|
||||
window.location = "/login";
|
||||
});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
441
src/views/activity/game-task.vue
Normal file
441
src/views/activity/game-task.vue
Normal file
@ -0,0 +1,441 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<div class="game-task-page">
|
||||
<!-- 二级菜单下拉框 -->
|
||||
<div class="filter-container">
|
||||
<el-cascader
|
||||
v-model="selectedCategories"
|
||||
:options="categoryOptions"
|
||||
:props="cascaderProps"
|
||||
placeholder="请选择商户"
|
||||
style="width: 300px;"
|
||||
@change="handleCategoryChange"
|
||||
@click.native="handleCascaderClick"
|
||||
/>
|
||||
<el-button @click="handleReset" style="margin-left: 10px;">重置</el-button>
|
||||
<el-button class="refresh-btn" @click="handleRefresh" style="margin-left: auto;">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeGame" @tab-click="handleGameChange">
|
||||
<el-tab-pane
|
||||
v-for="game in gameList"
|
||||
:key="game.id"
|
||||
:label="game.gameName"
|
||||
:name="game.gameId.toString()"
|
||||
/>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 任务列表表格 -->
|
||||
<div class="task-table-container">
|
||||
<el-table v-loading="loading" :data="taskList" style="width: 100%; max-width: 100%; table-layout: fixed; overflow-x: hidden;" border>
|
||||
<el-table-column prop="qqNetbarTaskRules" label="任务规则" :resizable="false" align="center" header-align="center" min-width="16%" />
|
||||
<el-table-column prop="qqNetbarTaskMemo" label="任务描述" :resizable="false" align="center" header-align="center" min-width="16%" />
|
||||
<el-table-column prop="qqNetbarTaskName" label="任务名称" :resizable="false" align="center" header-align="center" min-width="16%" />
|
||||
<el-table-column prop="qqNetbarReward" label="奖励名称" :resizable="false" align="center" header-align="center" min-width="16%">
|
||||
<template slot-scope="scope">
|
||||
<span>
|
||||
{{
|
||||
(scope.row.rewards && scope.row.rewards.filter(r => r.rewardTypeSource === 1).map(r => r.name).join('、')) || '-'
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="qqNetbarTargetTime" label="目标次数" :resizable="false" align="center" header-align="center" min-width="16%" />
|
||||
<el-table-column label="操作" align="center" min-width="16%">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="configureReward(scope.row)">配置奖励</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="配置奖励"
|
||||
:visible.sync="rewardTypeDialogVisible"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="reward-preview-list" style="margin-bottom: 16px;">
|
||||
<div v-if="currentTask && currentTask.rewards && currentTask.rewards.filter(r => r.rewardTypeSource === 1).length" style="margin-bottom: 8px;">
|
||||
<div v-for="reward in (currentTask.rewards || []).filter(r => r.rewardTypeSource === 1)" :key="reward.id" class="reward-preview-item" style="display: flex; align-items: center; margin-bottom: 6px;">
|
||||
<img v-if="reward.imageUrl" :src="reward.imageUrl" alt="奖励图片" style="width: 40px; height: 40px; object-fit: contain; border: 1px solid #eee; border-radius: 6px; margin-right: 10px;" />
|
||||
<span style="flex:1;">{{ reward.name }}(类型:{{ reward.rewardTypeName || '-' }},数量:{{ reward.grantQuantity || '-' }})</span>
|
||||
<el-button type="danger" size="mini" @click="handleDeleteReward(reward)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else style="color: #999; margin-bottom: 8px;">暂无已配置奖励</div>
|
||||
</div>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="奖励类型">
|
||||
<el-select v-model="selectedRewardTypeId" placeholder="请选择奖励类型" filterable :loading="rewardTypeLoading" style="width: 100%" @change="handleRewardTypeChange">
|
||||
<el-option v-for="item in rewardTypeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="奖励">
|
||||
<el-select v-model="selectedRewardId" placeholder="请选择奖励" filterable :loading="rewardListLoading" style="width: 100%">
|
||||
<el-option v-for="item in rewardList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="rewardTypeDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleRewardTypeConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getGameList } from '@/api/game'
|
||||
import { getTaskList, getMerchantAndStoreList, setTaskReward, operateTaskReward } from '@/api/task'
|
||||
import { getRewardTypeList } from '@/api/reward-type'
|
||||
import { getRewardSystemList } from '@/api/reward'
|
||||
|
||||
export default {
|
||||
name: 'GameTask',
|
||||
data() {
|
||||
return {
|
||||
activeGame: '',
|
||||
gameList: [],
|
||||
loading: false,
|
||||
// 二级菜单下拉框相关数据
|
||||
selectedCategories: [],
|
||||
categoryOptions: [],
|
||||
cascaderProps: {
|
||||
expandTrigger: 'hover'
|
||||
},
|
||||
// 任务列表相关数据
|
||||
taskList: [],
|
||||
taskLoading: false,
|
||||
rewardTypeDialogVisible: false,
|
||||
rewardTypeList: [],
|
||||
rewardTypeLoading: false,
|
||||
selectedRewardTypeId: null,
|
||||
rewardList: [],
|
||||
rewardListLoading: false,
|
||||
selectedRewardId: null,
|
||||
currentTask: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initPage()
|
||||
},
|
||||
methods: {
|
||||
async initPage() {
|
||||
// 先获取商户和门店数据并设置默认
|
||||
await this.getMerchantAndStoreData(false)
|
||||
// 再获取游戏列表并设置默认
|
||||
await this.getGameList(false)
|
||||
// 下拉框和游戏列表都加载完后,拉取任务列表
|
||||
await this.getTaskList()
|
||||
},
|
||||
// 获取商户和门店数据
|
||||
async getMerchantAndStoreData(autoGetTask = true) {
|
||||
try {
|
||||
const res = await getMerchantAndStoreList()
|
||||
if (res.code === 0) {
|
||||
this.categoryOptions = res.data.list.map(merchant => ({
|
||||
value: merchant.id,
|
||||
label: merchant.merchantName,
|
||||
children: merchant.StoreDatas.map(store => ({
|
||||
value: store.id,
|
||||
label: store.storeName,
|
||||
netbarAccount: store.netbarAccount
|
||||
}))
|
||||
}))
|
||||
// 默认选中第一个商户和第一个门店
|
||||
if (this.categoryOptions.length > 0 && this.categoryOptions[0].children && this.categoryOptions[0].children.length > 0) {
|
||||
this.selectedCategories = [this.categoryOptions[0].value, this.categoryOptions[0].children[0].value]
|
||||
if (autoGetTask) this.handleCategoryChange(this.selectedCategories)
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.message || '获取商户和门店列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('获取商户和门店列表失败')
|
||||
}
|
||||
},
|
||||
|
||||
async getGameList(autoGetTask = true) {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getGameList({
|
||||
page: 1,
|
||||
size: 1000,
|
||||
pageidx: ''
|
||||
})
|
||||
if (res.code === 0) {
|
||||
this.gameList = res.data.list
|
||||
if (this.gameList.length > 0) {
|
||||
this.activeGame = this.gameList[0].gameId.toString()
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.message || '获取游戏列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 分类选择改变
|
||||
async handleCategoryChange(value) {
|
||||
console.log('handleCategoryChange', value)
|
||||
this.loading = true
|
||||
await this.getTaskList()
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
// 下拉框点击事件
|
||||
async handleCascaderClick() {
|
||||
await this.getMerchantAndStoreData(false)
|
||||
},
|
||||
|
||||
handleReset() {
|
||||
this.selectedCategories = []
|
||||
},
|
||||
|
||||
async handleRefresh() {
|
||||
this.loading = true
|
||||
await this.getTaskList()
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
handleGameChange(tab) {
|
||||
// 更新游戏ID
|
||||
this.activeGame = tab.name
|
||||
// 切换游戏后,重新拉取任务列表
|
||||
this.loading = true
|
||||
this.getTaskList().finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleSizeChange(size) {
|
||||
},
|
||||
|
||||
handleCurrentChange(page) {
|
||||
},
|
||||
|
||||
async getTaskList() {
|
||||
this.loading = true
|
||||
// 获取netbarAccount和gid
|
||||
let netbarAccount = ''
|
||||
let gid = ''
|
||||
console.log('selectedCategories', this.selectedCategories)
|
||||
if (this.selectedCategories.length === 2) {
|
||||
const merchant = this.categoryOptions.find(m => m.value === this.selectedCategories[0])
|
||||
if (merchant && merchant.children) {
|
||||
const store = merchant.children.find(s => s.value === this.selectedCategories[1])
|
||||
if (store) netbarAccount = store.netbarAccount || ''
|
||||
}
|
||||
}
|
||||
if (this.activeGame) {
|
||||
gid = this.activeGame
|
||||
}
|
||||
console.log('getTaskList params', { netbarAccount, gid })
|
||||
if (!netbarAccount || !gid) {
|
||||
this.loading = false
|
||||
this.taskList = []
|
||||
return
|
||||
}
|
||||
const res = await getTaskList({ netbarAccount, gid, num: 10, pageidx: '' })
|
||||
console.log('getTaskList result', res)
|
||||
if (res.code === 0) {
|
||||
this.taskList = res.data.taskList || []
|
||||
} else {
|
||||
this.$message.error(res.message || '获取任务列表失败')
|
||||
this.taskList = []
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
async configureReward(row) {
|
||||
// 只保留 rewardTypeSource === 1 的奖励
|
||||
this.currentTask = {
|
||||
...row,
|
||||
rewards: (row.rewards || []).filter(r => r.rewardTypeSource === 1)
|
||||
}
|
||||
this.selectedRewardTypeId = null
|
||||
this.selectedRewardId = null
|
||||
this.rewardTypeDialogVisible = true
|
||||
this.rewardTypeLoading = true
|
||||
this.rewardList = []
|
||||
this.rewardListLoading = false
|
||||
// 获取奖励类型
|
||||
getRewardTypeList({ page: 1, size: 1000 }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.rewardTypeList = res.data.list || []
|
||||
} else {
|
||||
this.$message.error(res.message || '获取奖励类型失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$message.error('获取奖励类型失败')
|
||||
}).finally(() => {
|
||||
this.rewardTypeLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleRewardTypeChange(val) {
|
||||
this.selectedRewardId = null
|
||||
this.rewardList = []
|
||||
if (!val) return
|
||||
this.rewardListLoading = true
|
||||
getRewardSystemList({ rewardTypeId: val, page: 1, size: 100 }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.rewardList = res.data.list || []
|
||||
} else {
|
||||
this.$message.error(res.message || '获取奖励列表失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$message.error('获取奖励列表失败')
|
||||
}).finally(() => {
|
||||
this.rewardListLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleRewardTypeConfirm() {
|
||||
if (!this.selectedRewardTypeId || !this.selectedRewardId || !this.currentTask) {
|
||||
this.$message.error('请选择奖励类型和奖励')
|
||||
return
|
||||
}
|
||||
const taskId = this.currentTask.qqNetbarTaskId || this.currentTask.id
|
||||
operateTaskReward({
|
||||
type: 1,
|
||||
taskId,
|
||||
rewardId: this.selectedRewardId
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('添加奖励成功')
|
||||
// 前端追加并过滤,只保留 rewardTypeSource === 1
|
||||
if (!this.currentTask.rewards) this.currentTask.rewards = []
|
||||
const added = this.rewardList.find(r => r.id === this.selectedRewardId)
|
||||
if (added && added.rewardTypeSource === 1) this.currentTask.rewards.push(added)
|
||||
this.currentTask.rewards = this.currentTask.rewards.filter(r => r.rewardTypeSource === 1)
|
||||
this.selectedRewardTypeId = null
|
||||
this.selectedRewardId = null
|
||||
this.rewardTypeDialogVisible = false
|
||||
this.getTaskList()
|
||||
} else {
|
||||
this.$message.error(res.message || '添加奖励失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$message.error('请求失败')
|
||||
})
|
||||
},
|
||||
handleDeleteReward(reward) {
|
||||
if (!this.currentTask) return
|
||||
const taskId = this.currentTask.qqNetbarTaskId || this.currentTask.id
|
||||
operateTaskReward({
|
||||
type: 2,
|
||||
taskId,
|
||||
rewardId: reward.id
|
||||
}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('删除奖励成功')
|
||||
// 前端移除,只保留 rewardTypeSource === 1
|
||||
this.currentTask.rewards = (this.currentTask.rewards || []).filter(r => r.id !== reward.id && r.rewardTypeSource === 1)
|
||||
} else {
|
||||
this.$message.error(res.message || '删除奖励失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$message.error('请求失败')
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.game-task-page {
|
||||
background: #fff;
|
||||
padding: 20px 24px 12px 24px;
|
||||
border-radius: 10px;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.filter-container {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.task-table-container {
|
||||
margin-top: 20px;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 禁止el-table横向滚动,body和header都不允许 */
|
||||
.task-table-container .el-table__body-wrapper,
|
||||
.task-table-container .el-table__header-wrapper,
|
||||
.task-table-container .el-table__footer-wrapper {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.task-table-container .el-table__body,
|
||||
.task-table-container .el-table__header,
|
||||
.task-table-container .el-table__footer {
|
||||
overflow-x: hidden !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 16px;
|
||||
border-radius: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background: #e8f7ea;
|
||||
color: #3fc06d;
|
||||
}
|
||||
.status-block {
|
||||
background: #fbeaea;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
/* 强制彻底禁止el-table横向滚动 */
|
||||
::v-deep .task-table-container .el-table__body-wrapper,
|
||||
::v-deep .task-table-container .el-table__header-wrapper,
|
||||
::v-deep .task-table-container .el-table__footer-wrapper,
|
||||
::v-deep .task-table-container .el-table__body,
|
||||
::v-deep .task-table-container .el-table__header,
|
||||
::v-deep .task-table-container .el-table__footer {
|
||||
overflow-x: hidden !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
54
src/views/activity/test.vue
Normal file
54
src/views/activity/test.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="slider-center">
|
||||
<div id="nc"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AliyunSlider',
|
||||
mounted() {
|
||||
// 动态加载阿里云滑动验证码js
|
||||
if (!window.AWSC) {
|
||||
const script = document.createElement('script')
|
||||
script.src = 'https://g.alicdn.com/AWSC/AWSC/awsc.js'
|
||||
script.onload = this.initSlider
|
||||
document.body.appendChild(script)
|
||||
} else {
|
||||
this.initSlider()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initSlider() {
|
||||
if (!window.AWSC) return
|
||||
window.AWSC.use('nc', (state, module) => {
|
||||
window.nc = module.init({
|
||||
appkey: 'CF_APP_1',
|
||||
scene: 'register',
|
||||
renderTo: 'nc',
|
||||
success: function (data) {
|
||||
window.console && console.log(data.sessionId)
|
||||
window.console && console.log(data.sig)
|
||||
window.console && console.log(data.token)
|
||||
},
|
||||
fail: function (failCode) {
|
||||
window.console && console.log(failCode)
|
||||
},
|
||||
error: function (errorCode) {
|
||||
window.console && console.log(errorCode)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.slider-center {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@ -11,8 +11,6 @@
|
||||
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
|
||||
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
|
||||
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
|
||||
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
|
||||
<el-table-column prop="address" label="地址" header-align="center" align="center" />
|
||||
<el-table-column prop="auditAt" label="通过时间" header-align="center" align="center" />
|
||||
<el-table-column prop="expireAt" label="服务到期时间" header-align="center" align="center" />
|
||||
<el-table-column prop="status" label="状态" header-align="center" align="center">
|
||||
@ -44,11 +42,134 @@
|
||||
:merchant-data="currentMerchant"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 门店列表弹窗 -->
|
||||
<el-dialog
|
||||
:title="currentMerchant.name + ' - 门店列表'"
|
||||
:visible.sync="storeDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
class="store-dialog"
|
||||
width="100%"
|
||||
:fullscreen="true"
|
||||
:show-close="false"
|
||||
:append-to-body="true"
|
||||
>
|
||||
<div class="store-list-container">
|
||||
<div class="store-list-header">
|
||||
<div class="left">
|
||||
<div class="back-btn" @click="handleBack">
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
<span>返回</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" @click="handleAddStore">新增门店</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="storeLoading"
|
||||
:data="storeList"
|
||||
border={false}
|
||||
class="custom-table"
|
||||
header-row-class-name="custom-header"
|
||||
row-class-name="custom-row"
|
||||
>
|
||||
<el-table-column prop="name" label="门店名称" header-align="center" align="center" />
|
||||
<el-table-column prop="storeCode" label="门店编号" header-align="center" align="center" />
|
||||
<el-table-column prop="address" label="门店地址" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.address || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="contactName" label="联系人姓名" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.contactName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="contactPhone" label="联系人电话" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.contactPhone || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span :class="['status-tag', scope.row.status === 1 ? 'status-normal' : 'status-block']">
|
||||
{{ scope.row.status === 1 ? '营业中' : '已关闭' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createdAt" label="创建时间" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.createdAt || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" class="edit-btn" @click="handleEditStore(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" class="delete-btn" @click="handleDeleteStore(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="storeTotal"
|
||||
:page-size="storeQuery.size"
|
||||
:current-page="storeQuery.page"
|
||||
@current-change="handleStorePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 门店编辑弹窗 -->
|
||||
<el-dialog
|
||||
:title="storeDialogType === 'add' ? '新增门店' : '编辑门店'"
|
||||
:visible.sync="storeEditDialogVisible"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleStoreDialogClosed"
|
||||
class="store-edit-dialog"
|
||||
>
|
||||
<el-form
|
||||
ref="storeForm"
|
||||
:model="storeForm"
|
||||
:rules="storeRules"
|
||||
label-width="100px"
|
||||
size="medium"
|
||||
>
|
||||
<el-form-item label="门店名称" prop="name">
|
||||
<el-input v-model="storeForm.name" placeholder="请输入门店名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="门店编号" prop="storeCode" v-if="storeDialogType === 'edit'">
|
||||
<el-input v-model="storeForm.storeCode" placeholder="请输入门店编号" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="门店地址" prop="address">
|
||||
<el-input v-model="storeForm.address" placeholder="请输入门店地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人姓名" prop="contactName">
|
||||
<el-input v-model="storeForm.contactName" placeholder="请输入联系人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="联系人电话" prop="contactPhone">
|
||||
<el-input v-model="storeForm.contactPhone" placeholder="请输入联系人电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="storeForm.status">
|
||||
<el-radio :label="1">营业中</el-radio>
|
||||
<el-radio :label="0">已关闭</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="storeEditDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="storeSubmitLoading" @click="handleStoreSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { merchantList } from '@/api/merchant'
|
||||
import { getStoreList } from '@/api/store'
|
||||
import MerchantEdit from './merchant-edit.vue'
|
||||
|
||||
export default {
|
||||
@ -61,7 +182,52 @@ export default {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
editDialogVisible: false,
|
||||
currentMerchant: {}
|
||||
currentMerchant: {},
|
||||
// 门店列表相关数据
|
||||
storeDialogVisible: false,
|
||||
storeEditDialogVisible: false,
|
||||
storeLoading: false,
|
||||
storeList: [],
|
||||
storeTotal: 0,
|
||||
storeQuery: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
merchantId: undefined
|
||||
},
|
||||
// 门店弹窗相关数据
|
||||
storeDialogType: 'add', // add 或 edit
|
||||
storeSubmitLoading: false,
|
||||
storeForm: {
|
||||
name: '',
|
||||
storeCode: '',
|
||||
address: '',
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
status: 1
|
||||
},
|
||||
storeRules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入门店名称', trigger: 'blur' },
|
||||
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
storeCode: [
|
||||
{ required: true, message: '请输入门店编号', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
],
|
||||
address: [
|
||||
{ required: true, message: '请输入门店地址', trigger: 'blur' }
|
||||
],
|
||||
contactName: [
|
||||
{ required: true, message: '请输入联系人姓名', trigger: 'blur' }
|
||||
],
|
||||
contactPhone: [
|
||||
{ required: true, message: '请输入联系人电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择状态', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -90,8 +256,36 @@ export default {
|
||||
this.editDialogVisible = true
|
||||
},
|
||||
handleViewStores(row) {
|
||||
// TODO: 实现查看门店功能
|
||||
this.$message.info('查看门店功能开发中')
|
||||
this.currentMerchant = row
|
||||
this.storeQuery.merchantId = row.id
|
||||
this.storeDialogVisible = true
|
||||
this.getStoreList()
|
||||
},
|
||||
handleBack() {
|
||||
this.storeDialogVisible = false
|
||||
this.storeList = []
|
||||
this.storeTotal = 0
|
||||
this.currentMerchant = {}
|
||||
},
|
||||
async getStoreList() {
|
||||
this.storeLoading = true
|
||||
try {
|
||||
const res = await getStoreList(this.storeQuery)
|
||||
if (res.code === 0) {
|
||||
this.storeList = res.data.list
|
||||
this.storeTotal = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.message || '获取门店列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('获取门店列表失败')
|
||||
} finally {
|
||||
this.storeLoading = false
|
||||
}
|
||||
},
|
||||
handleStorePageChange(page) {
|
||||
this.storeQuery.page = page
|
||||
this.getStoreList()
|
||||
},
|
||||
async handleToggleStatus(row) {
|
||||
try {
|
||||
@ -110,6 +304,61 @@ export default {
|
||||
},
|
||||
handleEditSuccess() {
|
||||
this.getList()
|
||||
},
|
||||
handleAddStore() {
|
||||
this.storeDialogType = 'add'
|
||||
this.storeEditDialogVisible = true
|
||||
},
|
||||
handleEditStore(row) {
|
||||
this.storeDialogType = 'edit'
|
||||
this.storeForm = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
storeCode: row.storeCode,
|
||||
address: row.address,
|
||||
contactName: row.contactName,
|
||||
contactPhone: row.contactPhone,
|
||||
status: row.status
|
||||
}
|
||||
this.storeEditDialogVisible = true
|
||||
},
|
||||
handleDeleteStore(row) {
|
||||
this.$confirm('确认删除该门店吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$message.info('删除门店功能待实现,ID:' + row.id)
|
||||
}).catch(() => {})
|
||||
},
|
||||
handleStoreDialogClosed() {
|
||||
this.$refs.storeForm && this.$refs.storeForm.resetFields()
|
||||
this.storeForm = {
|
||||
name: '',
|
||||
storeCode: '',
|
||||
address: '',
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
status: 1
|
||||
}
|
||||
},
|
||||
handleStoreSubmit() {
|
||||
this.$refs.storeForm.validate(async valid => {
|
||||
if (!valid) return
|
||||
|
||||
this.storeSubmitLoading = true
|
||||
try {
|
||||
// TODO: 调用新增/编辑门店接口
|
||||
const action = this.storeDialogType === 'add' ? '新增' : '编辑'
|
||||
this.$message.success(`${action}成功`)
|
||||
this.storeEditDialogVisible = false
|
||||
this.getStoreList()
|
||||
} catch (error) {
|
||||
this.$message.error('操作失败')
|
||||
} finally {
|
||||
this.storeSubmitLoading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,4 +432,91 @@ export default {
|
||||
font-weight: 500;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.store-list-container {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.store-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
padding: 8px 0;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
margin-left: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.store-dialog {
|
||||
::v-deep .el-dialog__header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.store-edit-dialog {
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
::v-deep .el-form-item__label {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
::v-deep .el-radio-group {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -11,8 +11,7 @@
|
||||
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
|
||||
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
|
||||
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
|
||||
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
|
||||
<el-table-column prop="address" label="地址" header-align="center" align="center" />
|
||||
<el-table-column prop="applicationReason" label="申请原因" header-align="center" align="center" />
|
||||
<el-table-column prop="createdAt" label="申请时间" header-align="center" align="center" />
|
||||
<el-table-column prop="status" label="状态" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
@ -66,12 +65,27 @@ export default {
|
||||
async handleApprove(row) {
|
||||
try {
|
||||
await this.$confirm('确认通过该商户的审核吗?', '提示', {
|
||||
type: 'warning'
|
||||
type: 'warning',
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消'
|
||||
})
|
||||
// 选择服务到期时间
|
||||
const { value: serviceExpire } = await this.$prompt(
|
||||
'请选择服务到期时间',
|
||||
'服务到期时间',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'date',
|
||||
inputPattern: /.*/, // 允许为空
|
||||
inputValue: ''
|
||||
}
|
||||
)
|
||||
const res = await merchantAudit({
|
||||
id: row.id,
|
||||
auditStatus: 2,
|
||||
auditRemark: '审核通过'
|
||||
auditRemark: '审核通过',
|
||||
serviceExpire
|
||||
})
|
||||
if (res.code === 0) {
|
||||
this.$message.success('审核通过成功')
|
||||
|
||||
@ -11,8 +11,6 @@
|
||||
<el-table-column prop="name" label="商户名称" header-align="center" align="center" />
|
||||
<el-table-column prop="contactName" label="联系人" header-align="center" align="center" />
|
||||
<el-table-column prop="contactPhone" label="联系电话" header-align="center" align="center" />
|
||||
<el-table-column prop="contactEmail" label="联系邮箱" header-align="center" align="center" />
|
||||
<el-table-column prop="address" label="地址" header-align="center" align="center" />
|
||||
<el-table-column prop="auditAt" label="拒绝时间" header-align="center" align="center" />
|
||||
<el-table-column prop="rejectReason" label="拒绝原因" header-align="center" align="center" />
|
||||
<el-table-column prop="status" label="状态" header-align="center" align="center">
|
||||
|
||||
@ -91,6 +91,8 @@ export default {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.search-area {
|
||||
margin-bottom: 20px;
|
||||
@ -120,4 +122,29 @@ export default {
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog {
|
||||
margin: 0 !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
.el-dialog__header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
.el-dialog__body {
|
||||
height: calc(100% - 120px);
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
0
src/views/store/store-list.vue
Normal file
0
src/views/store/store-list.vue
Normal file
0
src/views/system/components/game-edit.vue
Normal file
0
src/views/system/components/game-edit.vue
Normal file
508
src/views/system/game.vue
Normal file
508
src/views/system/game.vue
Normal file
@ -0,0 +1,508 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<div class="game-config-page">
|
||||
<div class="header-row">
|
||||
<div class="search-form">
|
||||
<el-form :inline="true" :model="query" size="small">
|
||||
<el-form-item label="游戏名称">
|
||||
<el-input v-model="query.gameName" placeholder="请输入游戏名称" clearable @keyup.enter.native="handleSearch" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button class="refresh-btn" @click="getList">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
<el-button type="primary" @click="addDialogVisible = true">新增游戏</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="list" style="width: 100%" v-loading="loading" size="small" :row-class-name="'game-table-row'"
|
||||
:cell-style="{ padding: '12px 8px' }">
|
||||
<el-table-column label="游戏图标" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar v-if="scope.row.avatar" :src="scope.row.avatar" size="small" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="gameName" label="游戏名称" min-width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip v-if="scope.row.gameName && scope.row.gameName.length > 20" :content="scope.row.gameName" placement="top">
|
||||
<span>{{ scope.row.gameName.slice(0, 20) + '...' }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ scope.row.gameName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="gameCode" label="游戏编码" min-width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip v-if="scope.row.gameCode && scope.row.gameCode.length > 20" :content="scope.row.gameCode" placement="top">
|
||||
<span>{{ scope.row.gameCode.slice(0, 20) + '...' }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ scope.row.gameCode }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="gameId" label="游戏ID" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip v-if="String(scope.row.gameId).length > 20" :content="scope.row.gameId" placement="top">
|
||||
<span>{{ String(scope.row.gameId).slice(0, 20) + '...' }}</span>
|
||||
</el-tooltip>
|
||||
<span v-else>{{ scope.row.gameId }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="mini" class="action-btn edit-btn" @click="onEdit(scope.row)">
|
||||
<i class="el-icon-edit"></i>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" class="action-btn delete-btn" @click="onDelete(scope.row)">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table-footer">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="query.size"
|
||||
:current-page="query.page"
|
||||
@current-change="handlePageChange"
|
||||
class="custom-pagination"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog title="新增游戏" :visible.sync="addDialogVisible" width="400px" :close-on-click-modal="false">
|
||||
<el-form :model="addForm" label-width="100px" label-position="left" class="add-form">
|
||||
<el-form-item label="游戏ID" required>
|
||||
<el-input v-model="addForm.gameId" placeholder="请输入游戏ID" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏名称" required>
|
||||
<el-input v-model="addForm.gameName" placeholder="请输入游戏名称" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏编码" required>
|
||||
<el-input v-model="addForm.gameCode" placeholder="请输入游戏编码" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定类型" required>
|
||||
<el-select v-model="addForm.boundType" placeholder="请选择绑定类型" style="width: 100%">
|
||||
<el-option label="QQ" :value="1"></el-option>
|
||||
<el-option label="微信" :value="2"></el-option>
|
||||
<el-option label="全部" :value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏图标" required>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="/x/upload/game"
|
||||
:show-file-list="false"
|
||||
:http-request="handleGameImgRequest"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img v-if="addForm.avatar" :src="addForm.avatar" class="avatar" />
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleAdd">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog title="编辑游戏" :visible.sync="editDialogVisible" width="400px" :close-on-click-modal="false">
|
||||
<el-form :model="editForm" label-width="100px" label-position="left" class="add-form">
|
||||
<el-form-item label="游戏ID" required>
|
||||
<el-input v-model="editForm.gameId" placeholder="请输入游戏ID" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏名称" required>
|
||||
<el-input v-model="editForm.gameName" placeholder="请输入游戏名称" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏编码" required>
|
||||
<el-input v-model="editForm.gameCode" placeholder="请输入游戏编码" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定类型" required>
|
||||
<el-select v-model="editForm.boundType" placeholder="请选择绑定类型" style="width: 100%">
|
||||
<el-option label="QQ" :value="1"></el-option>
|
||||
<el-option label="微信" :value="2"></el-option>
|
||||
<el-option label="全部" :value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏图标" required>
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
action="/x/upload/game"
|
||||
:show-file-list="false"
|
||||
:http-request="handleEditGameImgRequest"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar" />
|
||||
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleEdit">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getGameList, addGame, editGame, deleteGame, uploadGameImg } from '@/api/game'
|
||||
|
||||
export default {
|
||||
name: 'GameConfig',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
query: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
gameName: ''
|
||||
},
|
||||
addDialogVisible: false,
|
||||
addForm: {
|
||||
gameId: '',
|
||||
gameName: '',
|
||||
gameCode: '',
|
||||
boundType: '',
|
||||
avatar: ''
|
||||
},
|
||||
editDialogVisible: false,
|
||||
editForm: {
|
||||
id: '',
|
||||
gameId: '',
|
||||
gameName: '',
|
||||
gameCode: '',
|
||||
boundType: '',
|
||||
avatar: ''
|
||||
},
|
||||
editOrigin: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getGameList(this.query)
|
||||
if (res.code === 0) {
|
||||
this.list = res.data.list
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.message || '获取游戏列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.query.page = page
|
||||
this.getList()
|
||||
},
|
||||
handleAvatarSuccess(res) {
|
||||
this.addForm.avatar = res.url
|
||||
},
|
||||
handleGameImgRequest(option) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', option.file)
|
||||
uploadGameImg(formData).then(res => {
|
||||
if (res && res.code === 0 && res.data && res.data.url) {
|
||||
this.addForm.avatar = res.data.url
|
||||
this.$message.success('上传成功')
|
||||
option.onSuccess(res)
|
||||
} else {
|
||||
this.$message.error(res.message || '上传失败')
|
||||
option.onError(new Error(res.message || '上传失败'))
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$message.error('上传失败')
|
||||
option.onError(err)
|
||||
})
|
||||
},
|
||||
beforeAvatarUpload(file) {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
if (!isImage) {
|
||||
this.$message.error('只能上传图片文件!')
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('图片大小不能超过2MB!')
|
||||
}
|
||||
return isImage && isLt2M
|
||||
},
|
||||
async handleAdd() {
|
||||
if (!this.addForm.gameId || !this.addForm.gameName || !this.addForm.gameCode || !this.addForm.boundType) {
|
||||
this.$message.warning('请填写完整信息')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await addGame(this.addForm)
|
||||
if (res.code === 0 && res.data.success) {
|
||||
this.$message.success('新增成功')
|
||||
this.addDialogVisible = false
|
||||
this.addForm = { gameId: '', gameName: '', gameCode: '', boundType: '', avatar: '' }
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '新增失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
},
|
||||
onDetail(row) {
|
||||
this.$message.info('详情功能待实现,ID:' + row.gameId)
|
||||
},
|
||||
onEdit(row) {
|
||||
this.editOrigin = { ...row }
|
||||
this.editForm = { ...row }
|
||||
this.editDialogVisible = true
|
||||
},
|
||||
handleEditAvatarSuccess(res) {
|
||||
this.editForm.avatar = res.url
|
||||
},
|
||||
handleEditGameImgRequest(option) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', option.file)
|
||||
uploadGameImg(formData).then(res => {
|
||||
if (res && res.code === 0 && res.data && res.data.url) {
|
||||
this.editForm.avatar = res.data.url
|
||||
this.$message.success('上传成功')
|
||||
option.onSuccess(res)
|
||||
} else {
|
||||
this.$message.error(res.message || '上传失败')
|
||||
option.onError(new Error(res.message || '上传失败'))
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$message.error('上传失败')
|
||||
option.onError(err)
|
||||
})
|
||||
},
|
||||
async handleEdit() {
|
||||
if (!this.editForm.gameId || !this.editForm.gameName || !this.editForm.gameCode || !this.editForm.boundType) {
|
||||
this.$message.warning('请填写完整信息')
|
||||
return
|
||||
}
|
||||
const compareFields = ['gameId', 'gameName', 'gameCode']
|
||||
const submitData = { id: this.editForm.id }
|
||||
compareFields.forEach(key => {
|
||||
const oldVal = this.editOrigin[key]
|
||||
const newVal = this.editForm[key]
|
||||
if (newVal !== oldVal) {
|
||||
submitData[key] = newVal
|
||||
}
|
||||
})
|
||||
submitData.boundType = this.editForm.boundType
|
||||
submitData.avatar = this.editForm.avatar
|
||||
try {
|
||||
const res = await editGame(submitData)
|
||||
if (res.code === 0 && res.data.success) {
|
||||
this.$message.success('编辑成功')
|
||||
this.editDialogVisible = false
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '编辑失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm('确认删除该游戏吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
const res = await deleteGame(row.id)
|
||||
if (res.code === 0 && res.data.success) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '删除失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
handleSearch() {
|
||||
this.query.page = 1
|
||||
this.getList()
|
||||
},
|
||||
handleReset() {
|
||||
this.query = {
|
||||
page: 1,
|
||||
size: 10,
|
||||
gameName: ''
|
||||
}
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
editGame
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.game-config-page {
|
||||
background: #fff;
|
||||
padding: 20px 24px 12px 24px;
|
||||
border-radius: 10px;
|
||||
min-height: 500px;
|
||||
}
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.search-form {
|
||||
flex: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.refresh-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
line-height: 40px;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.game-table-row td {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
font-size: 14px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.el-table .cell {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.table-footer {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.custom-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
.add-form .el-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.add-form .el-input {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
.add-form .el-form-item__content {
|
||||
width: calc(100% - 100px);
|
||||
}
|
||||
.add-form .el-form-item__label {
|
||||
text-align: left;
|
||||
padding-right: 12px;
|
||||
font-weight: normal;
|
||||
width: 100px !important;
|
||||
}
|
||||
.add-form .el-form-item__label::before {
|
||||
content: '*';
|
||||
color: transparent;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
line-height: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: block;
|
||||
}
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.edit-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
}
|
||||
}
|
||||
|
||||
&.delete-btn {
|
||||
background: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
&:hover {
|
||||
background: #e64242;
|
||||
border-color: #e64242;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
597
src/views/system/reward-list.vue
Normal file
597
src/views/system/reward-list.vue
Normal file
@ -0,0 +1,597 @@
|
||||
<template>
|
||||
<div class="reward-list-container">
|
||||
<div class="game-task-page">
|
||||
<div class="header-row">
|
||||
<div class="search-form">
|
||||
<el-form :inline="true" :model="query" size="small">
|
||||
<el-form-item label="奖励名称" label-width="120px">
|
||||
<el-input v-model="query.name" placeholder="请输入奖励名称" clearable @keyup.enter.native="handleSearch" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" label-width="120px">
|
||||
<el-select v-model="query.status" placeholder="请选择状态" clearable>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button class="refresh-btn" @click="getTaskList">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleAddReward">添加奖励</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table :data="taskList" style="width: 100%" v-loading="loading" size="small" :row-class-name="'task-table-row'"
|
||||
:cell-style="{ padding: '12px 8px' }">
|
||||
<el-table-column prop="name" label="奖励名称" min-width="120" align="center" />
|
||||
<el-table-column prop="rewardTypeName" label="奖励类型" min-width="120" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.rewardTypeName || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="expireType" label="过期类型" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.expireType === 1">时间范围</span>
|
||||
<span v-else-if="scope.row.expireType === 2">固定时间</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="有效期" min-width="180" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.expireType === 1">
|
||||
{{ formatDate(scope.row.validFrom) || '-' }} ~ {{ formatDate(scope.row.validTo) || '-' }}
|
||||
</span>
|
||||
<span v-else-if="scope.row.expireType === 2">
|
||||
领取后{{ scope.row.expireDays || '-' }}天内有效
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dailyTotalLimit" label="每日总限量" min-width="100" align="center" />
|
||||
<el-table-column prop="totalLimit" label="总限量" min-width="100" align="center" />
|
||||
<el-table-column prop="userDailyLimit" label="用户每日限量" min-width="120" align="center" />
|
||||
<el-table-column prop="userTotalLimit" label="用户总限量" min-width="120" align="center" />
|
||||
<el-table-column prop="receivedNum" label="已领取数量" min-width="100" align="center" />
|
||||
<el-table-column prop="grantQuantity" label="发放数量" min-width="100" align="center" />
|
||||
<el-table-column label="操作" width="180" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="mini" class="action-btn edit-btn" @click="onEdit(scope.row)">
|
||||
<i class="el-icon-edit"></i>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" class="action-btn delete-btn" @click="onDelete(scope.row)">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table-footer">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="query.size"
|
||||
:current-page="query.page"
|
||||
@current-change="handlePageChange"
|
||||
class="custom-pagination"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑奖励弹窗 -->
|
||||
<el-dialog
|
||||
:title="dialogType === 'add' ? '新增奖励' : '编辑奖励'"
|
||||
:visible.sync="addRewardDialogVisible"
|
||||
width="600px"
|
||||
@close="resetAddRewardForm"
|
||||
>
|
||||
<el-form
|
||||
ref="addRewardForm"
|
||||
:model="addRewardForm"
|
||||
:rules="addRewardRules"
|
||||
label-width="120px"
|
||||
class="add-reward-form"
|
||||
>
|
||||
<el-form-item label="奖励名称" prop="name" label-width="120px">
|
||||
<el-input v-model="addRewardForm.name" placeholder="请输入奖励名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="奖励类型" prop="rewardTypeId" label-width="120px">
|
||||
<el-select v-model="addRewardForm.rewardTypeId" placeholder="请选择奖励类型" filterable style="width: 100%">
|
||||
<el-option v-for="item in rewardTypeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="游戏ID" prop="gameId" label-width="120px">
|
||||
<el-input-number v-model="addRewardForm.gameId" :min="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="奖励图片" prop="rewardImg" label-width="120px">
|
||||
<el-upload
|
||||
class="reward-img-uploader"
|
||||
action="/x/upload/reward"
|
||||
:show-file-list="false"
|
||||
:http-request="handleRewardImgRequest"
|
||||
:before-upload="beforeRewardImgUpload"
|
||||
>
|
||||
<img v-if="addRewardForm.rewardImg" :src="addRewardForm.rewardImg" class="reward-img-preview" style="width: 80px; height: 80px; object-fit: contain; border: 1px solid #eee; border-radius: 6px;" />
|
||||
<i v-else class="el-icon-plus reward-img-upload-icon" style="font-size: 32px; color: #bbb; width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; border: 1px dashed #d9d9d9; border-radius: 6px;" />
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="QQ网吧物品ID" prop="qqGoodsId" label-width="120px">
|
||||
<el-input v-model="addRewardForm.qqGoodsId" placeholder="请输入QQ网吧物品ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="QQ网吧物品ID字符串" prop="qqGoodsIdStr" label-width="120px">
|
||||
<el-input v-model="addRewardForm.qqGoodsIdStr" placeholder="请输入QQ网吧物品ID字符串" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status" label-width="120px">
|
||||
<el-select v-model="addRewardForm.status" placeholder="请选择状态" style="width: 100%">
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="过期类型" prop="expireType" label-width="120px">
|
||||
<el-select v-model="addRewardForm.expireType" placeholder="请选择过期类型" style="width: 100%">
|
||||
<el-option label="时间范围" :value="1" />
|
||||
<el-option label="固定时间" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addRewardForm.expireType === 1" label="有效开始时间" prop="validFrom" label-width="120px">
|
||||
<el-date-picker v-model="addRewardForm.validFrom" type="datetime" placeholder="请选择开始时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addRewardForm.expireType === 1" label="有效结束时间" prop="validTo" label-width="120px">
|
||||
<el-date-picker v-model="addRewardForm.validTo" type="datetime" placeholder="请选择结束时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addRewardForm.expireType === 2" label="领取后过期天数" prop="expireDays" label-width="120px">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<el-input-number v-model="addRewardForm.expireDays" :min="1" placeholder="请输入天数" style="width: 180px;" />
|
||||
<span style="margin-left: 8px;">天</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="每日总限量" prop="dailyTotalLimit" label-width="120px">
|
||||
<el-input-number v-model="addRewardForm.dailyTotalLimit" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="总限量" prop="totalLimit" label-width="120px">
|
||||
<el-input-number v-model="addRewardForm.totalLimit" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户每日限量" prop="userDailyLimit" label-width="120px">
|
||||
<el-input-number v-model="addRewardForm.userDailyLimit" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户总限量" prop="userTotalLimit" label-width="120px">
|
||||
<el-input-number v-model="addRewardForm.userTotalLimit" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="addRewardDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitAddReward">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRewardSystemList, addReward, updateReward, deleteReward } from '@/api/reward'
|
||||
import { getRewardTypeList } from '@/api/reward-type'
|
||||
import { uploadReward } from '@/api/reward'
|
||||
|
||||
export default {
|
||||
name: 'GameTask',
|
||||
created() {
|
||||
this.getTaskList()
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
taskList: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
query: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
name: '',
|
||||
status: undefined,
|
||||
rewardTypeId: undefined
|
||||
},
|
||||
addRewardDialogVisible: false,
|
||||
rewardTypeList: [],
|
||||
dialogType: 'add',
|
||||
addRewardForm: {
|
||||
id: undefined,
|
||||
name: '',
|
||||
rewardTypeId: undefined,
|
||||
gameId: undefined,
|
||||
rewardImg: '',
|
||||
qqGoodsId: '',
|
||||
qqGoodsIdStr: '',
|
||||
status: 1,
|
||||
expireType: undefined,
|
||||
validFrom: '',
|
||||
validTo: '',
|
||||
expireDays: undefined,
|
||||
dailyTotalLimit: undefined,
|
||||
totalLimit: undefined,
|
||||
userDailyLimit: undefined,
|
||||
userTotalLimit: undefined,
|
||||
source: 1
|
||||
},
|
||||
addRewardRules: {
|
||||
rewardTypeId: [{ required: true, message: '请选择奖励类型', trigger: 'change' }],
|
||||
name: [{ required: true, message: '请输入奖励名称', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '请输入奖励描述', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
value: [{ required: true, message: '请输入奖励值', trigger: 'blur' }],
|
||||
expireType: [{ required: true, message: '请选择过期类型', trigger: 'change' }],
|
||||
validFrom: [
|
||||
{ required: true, message: '请选择有效开始时间', trigger: 'change',
|
||||
validator: (rule, value, callback, source) => {
|
||||
if (source.expireType === 1 && !value) callback(new Error('请选择有效开始时间'));
|
||||
else callback();
|
||||
}
|
||||
}
|
||||
],
|
||||
validTo: [
|
||||
{ required: true, message: '请选择有效结束时间', trigger: 'change',
|
||||
validator: (rule, value, callback, source) => {
|
||||
if (source.expireType === 1 && !value) callback(new Error('请选择有效结束时间'));
|
||||
else callback();
|
||||
}
|
||||
}
|
||||
],
|
||||
expireDays: [
|
||||
{ required: true, message: '请输入天数', trigger: 'blur',
|
||||
validator: (rule, value, callback, source) => {
|
||||
if (source.expireType === 2 && (!value || value < 1)) callback(new Error('请输入天数'));
|
||||
else callback();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.query.page = 1
|
||||
this.getTaskList()
|
||||
},
|
||||
handleReset() {
|
||||
this.query = {
|
||||
page: 1,
|
||||
size: 10,
|
||||
name: '',
|
||||
status: undefined,
|
||||
rewardTypeId: undefined
|
||||
}
|
||||
this.getTaskList()
|
||||
},
|
||||
async getTaskList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getRewardSystemList(this.query)
|
||||
if (res.code === 0) {
|
||||
this.taskList = res.data.list
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.message || '获取任务列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.query.page = page
|
||||
this.getTaskList()
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm('确认删除该奖励吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
const res = await deleteReward(row.id)
|
||||
if (res.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getTaskList()
|
||||
} else {
|
||||
this.$message.error(res.message || '删除失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('删除失败')
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
formatDate(date) {
|
||||
if (!date) return '';
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const hours = String(d.getHours()).padStart(2, '0');
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(d.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
},
|
||||
handleAddReward() {
|
||||
this.dialogType = 'add'
|
||||
this.addRewardDialogVisible = true
|
||||
this.getRewardTypeList()
|
||||
},
|
||||
async getRewardTypeList() {
|
||||
try {
|
||||
const res = await getRewardTypeList({
|
||||
page: 1,
|
||||
size: 1000
|
||||
})
|
||||
if (res.code === 0) {
|
||||
this.rewardTypeList = res.data.list || []
|
||||
} else {
|
||||
this.$message.error(res.message || '获取奖励类型列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('获取奖励类型列表失败')
|
||||
}
|
||||
},
|
||||
onEdit(row) {
|
||||
this.dialogType = 'edit'
|
||||
this.addRewardForm = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
rewardTypeId: row.rewardTypeId,
|
||||
gameId: row.gameId,
|
||||
rewardImg: row.rewardImg || row.imageUrl || '',
|
||||
qqGoodsId: row.qqGoodsId,
|
||||
qqGoodsIdStr: row.qqGoodsIdStr,
|
||||
status: row.status,
|
||||
expireType: row.expireType || 1,
|
||||
validFrom: row.validFrom || row.startTime || '',
|
||||
validTo: row.validTo || row.endTime || '',
|
||||
expireDays: row.expireDays || '',
|
||||
dailyTotalLimit: row.dailyTotalLimit,
|
||||
totalLimit: row.totalLimit,
|
||||
userDailyLimit: row.userDailyLimit,
|
||||
userTotalLimit: row.userTotalLimit,
|
||||
source: 1
|
||||
}
|
||||
if (this.addRewardForm.expireType === 1) {
|
||||
this.addRewardForm.expireDays = ''
|
||||
} else if (this.addRewardForm.expireType === 2) {
|
||||
this.addRewardForm.validFrom = ''
|
||||
this.addRewardForm.validTo = ''
|
||||
}
|
||||
this.addRewardDialogVisible = true
|
||||
this.getRewardTypeList()
|
||||
},
|
||||
async submitAddReward() {
|
||||
try {
|
||||
await this.$refs.addRewardForm.validate()
|
||||
const api = this.dialogType === 'add' ? addReward : updateReward
|
||||
const formData = { ...this.addRewardForm, source: 1 }
|
||||
const res = await api(formData)
|
||||
if (res.code === 0) {
|
||||
this.$message.success(this.dialogType === 'add' ? '添加奖励成功' : '编辑奖励成功')
|
||||
this.addRewardDialogVisible = false
|
||||
this.getTaskList()
|
||||
} else {
|
||||
this.$message.error(res.message || (this.dialogType === 'add' ? '添加奖励失败' : '编辑奖励失败'))
|
||||
}
|
||||
} catch (e) {
|
||||
if (e === false) {
|
||||
this.$message.error('请完善表单信息')
|
||||
} else {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
}
|
||||
},
|
||||
resetAddRewardForm() {
|
||||
this.addRewardForm = {
|
||||
id: undefined,
|
||||
name: '',
|
||||
rewardTypeId: undefined,
|
||||
gameId: undefined,
|
||||
rewardImg: '',
|
||||
qqGoodsId: '',
|
||||
qqGoodsIdStr: '',
|
||||
status: 1,
|
||||
expireType: undefined,
|
||||
validFrom: '',
|
||||
validTo: '',
|
||||
expireDays: undefined,
|
||||
dailyTotalLimit: undefined,
|
||||
totalLimit: undefined,
|
||||
userDailyLimit: undefined,
|
||||
userTotalLimit: undefined,
|
||||
source: 1
|
||||
}
|
||||
},
|
||||
handleRewardImgRequest(option) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', option.file)
|
||||
uploadReward(formData).then(res => {
|
||||
if (res && res.code === 0 && res.data && res.data.url) {
|
||||
this.addRewardForm.rewardImg = res.data.url
|
||||
this.$message.success('上传成功')
|
||||
option.onSuccess(res)
|
||||
} else {
|
||||
this.$message.error(res.message || '上传失败')
|
||||
option.onError(new Error(res.message || '上传失败'))
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$message.error('上传失败')
|
||||
option.onError(err)
|
||||
})
|
||||
},
|
||||
beforeRewardImgUpload(file) {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
if (!isImage) {
|
||||
this.$message.error('只能上传图片文件!')
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('图片大小不能超过 2MB!')
|
||||
}
|
||||
return isImage && isLt2M
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.game-task-page {
|
||||
background: #fff;
|
||||
padding: 20px 24px 12px 24px;
|
||||
border-radius: 10px;
|
||||
min-height: 500px;
|
||||
}
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.search-form {
|
||||
flex: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.search-form .el-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.refresh-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 14px;
|
||||
line-height: 40px;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.task-table-row td {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
font-size: 14px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.el-table .cell {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.table-footer {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.custom-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.edit-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(85, 111, 246, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&.delete-btn {
|
||||
background: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
&:hover {
|
||||
background: #e64242;
|
||||
border-color: #e64242;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(245, 108, 108, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-reward-form {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.add-reward-form .el-form-item {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.add-reward-form .el-input,
|
||||
.add-reward-form .el-select,
|
||||
.add-reward-form .el-date-picker,
|
||||
.add-reward-form .el-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add-reward-form .el-form-item__label {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
379
src/views/system/reward-type.vue
Normal file
379
src/views/system/reward-type.vue
Normal file
@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<el-container>
|
||||
<el-main>
|
||||
<div class="reward-type-page">
|
||||
<div class="header-row">
|
||||
<div class="search-form">
|
||||
<el-form :inline="true" :model="query" size="small">
|
||||
<el-form-item label="名称">
|
||||
<el-input v-model="query.name" placeholder="请输入名称" clearable @keyup.enter.native="handleSearch" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<el-button class="refresh-btn" @click="getList">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
<el-button class="add-btn" @click="dialogVisible = true">
|
||||
<i class="el-icon-plus"></i>
|
||||
<span>新增奖励类型</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="list" style="width: 100%" v-loading="loading" size="small" :row-class-name="'reward-table-row'"
|
||||
:cell-style="{ padding: '12px 8px' }">
|
||||
<el-table-column prop="name" label="名称" min-width="120" align="center" />
|
||||
<el-table-column prop="tencentTypeId" label="腾讯奖励类型ID" min-width="120" align="center" />
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="!scope.row.storeName">
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="mini" class="action-btn edit-btn" @click="onEdit(scope.row)">
|
||||
<i class="el-icon-edit"></i>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" class="action-btn delete-btn" @click="onDelete(scope.row)">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table-footer">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="query.size"
|
||||
:current-page="query.page"
|
||||
@current-change="handlePageChange"
|
||||
class="custom-pagination"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
:title="dialogType === 'add' ? '新增奖励类型' : '编辑奖励类型'"
|
||||
:visible.sync="dialogVisible"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleDialogClosed"
|
||||
>
|
||||
<el-form :model="form" :rules="rules" ref="form" label-width="80px" label-position="left" class="add-form">
|
||||
<el-form-item label="名称" prop="name" required>
|
||||
<el-input v-model="form.name" placeholder="请输入名称" maxlength="50" style="width: 360px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="腾讯奖励类型ID" prop="tencentTypeId" required>
|
||||
<el-input-number v-model="form.tencentTypeId" :min="1" style="width: 360px;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer" style="text-align: left;">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRewardTypeList, addRewardType, updateRewardType, deleteRewardType } from '@/api/reward-type'
|
||||
|
||||
export default {
|
||||
name: 'RewardType',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
query: {
|
||||
page: 1,
|
||||
size: 10,
|
||||
name: '',
|
||||
status: undefined,
|
||||
storeId: undefined
|
||||
},
|
||||
dialogVisible: false,
|
||||
dialogType: 'add', // add 或 edit
|
||||
form: {
|
||||
name: '',
|
||||
tencentTypeId: undefined,
|
||||
source: 1
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
|
||||
],
|
||||
tencentTypeId: [
|
||||
{ required: true, message: '请输入腾讯奖励类型ID', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getRewardTypeList(this.query)
|
||||
if (res.code === 0) {
|
||||
this.list = res.data.list
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.message || '获取奖励类型列表失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.query.page = page
|
||||
this.getList()
|
||||
},
|
||||
onEdit(row) {
|
||||
this.dialogType = 'edit'
|
||||
this.form = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
tencentTypeId: row.tencentTypeId,
|
||||
source: row.source
|
||||
}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm('确认删除该奖励类型吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
const res = await deleteRewardType(row.id)
|
||||
if (res.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '删除失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('删除失败')
|
||||
}
|
||||
}).catch(() => {})
|
||||
},
|
||||
handleDialogClosed() {
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
this.form = {
|
||||
name: '',
|
||||
tencentTypeId: undefined,
|
||||
source: 1
|
||||
}
|
||||
this.dialogType = 'add' // 重置为新增状态
|
||||
},
|
||||
async handleSubmit() {
|
||||
this.$refs.form.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
const api = this.dialogType === 'add' ? addRewardType : updateRewardType
|
||||
const submitData = { ...this.form }
|
||||
const res = await api(submitData)
|
||||
if (res.code === 0) {
|
||||
this.$message.success(this.dialogType === 'add' ? '新增成功' : '编辑成功')
|
||||
this.dialogVisible = false
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || (this.dialogType === 'add' ? '新增失败' : '编辑失败'))
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSearch() {
|
||||
this.query.page = 1
|
||||
this.getList()
|
||||
},
|
||||
handleReset() {
|
||||
this.query = {
|
||||
page: 1,
|
||||
size: 10,
|
||||
name: '',
|
||||
status: undefined,
|
||||
storeId: undefined
|
||||
}
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.reward-type-page {
|
||||
background: #fff;
|
||||
padding: 20px 24px 12px 24px;
|
||||
border-radius: 10px;
|
||||
min-height: 500px;
|
||||
}
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.search-form {
|
||||
flex: 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.search-form .el-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.refresh-btn,
|
||||
.add-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.add-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
color: #fff;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
}
|
||||
}
|
||||
.el-table {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.reward-table-row td {
|
||||
padding-top: 8px !important;
|
||||
padding-bottom: 8px !important;
|
||||
font-size: 14px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.el-table .cell {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
text-align: center !important;
|
||||
}
|
||||
.table-footer {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.custom-pagination {
|
||||
margin: 0;
|
||||
}
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.edit-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(85, 111, 246, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&.delete-btn {
|
||||
background: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
&:hover {
|
||||
background: #e64242;
|
||||
border-color: #e64242;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(245, 108, 108, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
.add-form .el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.add-form .el-form-item__label {
|
||||
text-align: left;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.add-form .el-input {
|
||||
font-size: 14px;
|
||||
}
|
||||
.add-form .el-form-item__content {
|
||||
text-align: left;
|
||||
}
|
||||
.add-form .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
.add-form .el-textarea__inner {
|
||||
text-align: left;
|
||||
}
|
||||
.add-form .el-radio-group {
|
||||
text-align: left;
|
||||
}
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 2px 16px;
|
||||
border-radius: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background: #e8f7ea;
|
||||
color: #3fc06d;
|
||||
}
|
||||
.status-block {
|
||||
background: #fbeaea;
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,475 @@
|
||||
<template>
|
||||
<div class="feedback-list-page">
|
||||
<div class="search-row">
|
||||
<el-form :inline="true" @submit.native.prevent>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="query.status" placeholder="请选择状态">
|
||||
<el-option label="全部" :value="0" />
|
||||
<el-option label="未处理" :value="1" />
|
||||
<el-option label="已处理" :value="2" />
|
||||
<el-option label="已驳回" :value="3" />
|
||||
<el-option label="已关闭" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="反馈类型" required>
|
||||
<el-select v-model="query.feedbackType" placeholder="请选择反馈类型" clearable>
|
||||
<el-option label="全部" :value="0" />
|
||||
<el-option label="BUG" :value="1" />
|
||||
<el-option label="建议" :value="2" />
|
||||
<el-option label="投诉" :value="3" />
|
||||
<el-option label="其他" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题">
|
||||
<el-input v-model="query.title" placeholder="标题" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getList">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="feedback-action">
|
||||
<el-button class="refresh-btn" @click="handleRefresh">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card">
|
||||
<el-table
|
||||
:data="list"
|
||||
border={false}
|
||||
class="custom-table"
|
||||
header-row-class-name="custom-header"
|
||||
row-class-name="custom-row"
|
||||
>
|
||||
<el-table-column prop="userId" label="用户" width="80" />
|
||||
<el-table-column prop="title" label="反馈标题" />
|
||||
<el-table-column prop="content" label="反馈内容" />
|
||||
<el-table-column prop="feedbackType" label="反馈类型" :formatter="feedbackTypeFormatter" />
|
||||
<el-table-column prop="createdAt" label="反馈时间" />
|
||||
<el-table-column prop="status" label="状态" :formatter="statusFormatter">
|
||||
<template slot-scope="scope">
|
||||
<span :class="['status-tag', getStatusClass(scope.row.status)]">
|
||||
{{ statusFormatter(scope.row) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reply" label="回复" />
|
||||
<el-table-column label="操作" width="240" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="mini" class="action-btn detail-btn" @click="onDetail(scope.row)">
|
||||
<i class="el-icon-view"></i>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button type="success" size="mini" class="action-btn reply-btn" @click="onReply(scope.row)">
|
||||
<i class="el-icon-chat-dot-round"></i>
|
||||
回复
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" class="action-btn delete-btn" @click="onDelete(scope.row)">
|
||||
<i class="el-icon-delete"></i>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="table-footer">
|
||||
<div class="table-summary">
|
||||
显示第 {{ (query.page - 1) * query.size + 1 }} 到
|
||||
{{ Math.min(query.page * query.size, total) }} 条,共 {{ total }} 条记录
|
||||
</div>
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:current-page="query.page"
|
||||
:page-size="query.size"
|
||||
:total="total"
|
||||
@current-change="handlePageChange"
|
||||
class="custom-pagination"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="反馈详情"
|
||||
:visible.sync="detailDialogVisible"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="标题">{{ detailData.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="内容">{{ detailData.content }}</el-descriptions-item>
|
||||
<el-descriptions-item label="反馈类型">{{ feedbackTypeFormatter(detailData) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ statusFormatter(detailData) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="回复">{{ detailData.reply }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户ID">{{ detailData.userId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="商户ID">{{ detailData.merchantId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="门店ID">{{ detailData.storeId }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
title="回复反馈"
|
||||
:visible.sync="replyDialogVisible"
|
||||
width="400px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form :model="replyForm" label-width="60px">
|
||||
<el-form-item label="回复">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="replyForm.reply"
|
||||
placeholder="请输入回复内容"
|
||||
:rows="4"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="replyForm.status" placeholder="请选择状态">
|
||||
<el-option label="未处理" :value="1" />
|
||||
<el-option label="已处理" :value="2" />
|
||||
<el-option label="已驳回" :value="3" />
|
||||
<el-option label="已关闭" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="replyDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleReply">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { feedbackList, feedbackDetail, feedbackReply } from '@/api/feedback'
|
||||
|
||||
export default {
|
||||
name: 'FeedbackList',
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
total: 0,
|
||||
query: {
|
||||
status: 0,
|
||||
page: 1,
|
||||
size: 10,
|
||||
feedbackType: 0,
|
||||
title: ''
|
||||
},
|
||||
detailDialogVisible: false,
|
||||
detailData: {},
|
||||
replyDialogVisible: false,
|
||||
replyForm: {
|
||||
id: null,
|
||||
reply: '',
|
||||
status: 2 // 默认已处理
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
try {
|
||||
const res = await feedbackList(this.query)
|
||||
if (res.code === 0) {
|
||||
this.list = res.data.list
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.message || '获取数据失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.query.page = page
|
||||
this.getList()
|
||||
},
|
||||
handleRefresh() {
|
||||
this.query = {
|
||||
status: 0,
|
||||
page: 1,
|
||||
size: 10,
|
||||
feedbackType: 0,
|
||||
title: ''
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
handleReset() {
|
||||
this.query = {
|
||||
status: 0,
|
||||
page: 1,
|
||||
size: 10,
|
||||
feedbackType: 0,
|
||||
title: ''
|
||||
}
|
||||
this.getList()
|
||||
},
|
||||
feedbackTypeFormatter(row) {
|
||||
const map = {
|
||||
1: 'BUG',
|
||||
2: '建议',
|
||||
3: '投诉',
|
||||
4: '其他',
|
||||
0: '全部'
|
||||
}
|
||||
return map[row.feedbackType] || row.feedbackType
|
||||
},
|
||||
statusFormatter(row) {
|
||||
const map = {
|
||||
0: '全部',
|
||||
1: '未处理',
|
||||
2: '已处理',
|
||||
3: '已驳回',
|
||||
4: '已关闭'
|
||||
}
|
||||
return map[row.status] || row.status
|
||||
},
|
||||
getStatusClass(status) {
|
||||
const map = {
|
||||
0: 'status-all',
|
||||
1: 'status-pending',
|
||||
2: 'status-processed',
|
||||
3: 'status-rejected',
|
||||
4: 'status-closed'
|
||||
}
|
||||
return map[status] || ''
|
||||
},
|
||||
async onDetail(row) {
|
||||
try {
|
||||
const res = await feedbackDetail(row.id)
|
||||
if (res.code === 0) {
|
||||
this.detailData = res.data
|
||||
this.detailDialogVisible = true
|
||||
} else {
|
||||
this.$message.error(res.message || '获取详情失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
},
|
||||
onReply(row) {
|
||||
this.replyForm.id = row.id
|
||||
this.replyForm.reply = row.reply || ''
|
||||
this.replyForm.status = 2 // 默认"已处理"
|
||||
this.replyDialogVisible = true
|
||||
},
|
||||
async handleReply() {
|
||||
try {
|
||||
const res = await feedbackReply(this.replyForm)
|
||||
if (res.code === 0 && res.data.success) {
|
||||
this.$message.success('回复成功')
|
||||
this.replyDialogVisible = false
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '回复失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error('请求失败')
|
||||
}
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm('确认删除该反馈吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$message.success('删除功能待实现')
|
||||
// 这里可以调用删除接口
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.feedback-list-page {
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
padding: 0 32px 32px 32px;
|
||||
}
|
||||
.search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 18px;
|
||||
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 0;
|
||||
margin-right: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.feedback-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.feedback-action .refresh-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.table-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
padding: 0 0 12px 0;
|
||||
}
|
||||
.custom-table {
|
||||
background: #fff;
|
||||
border: none;
|
||||
font-size: 15px;
|
||||
::v-deep .el-table__body-wrapper td,
|
||||
::v-deep .el-table__header-wrapper th {
|
||||
border-right: none !important;
|
||||
border-left: none !important;
|
||||
}
|
||||
}
|
||||
.custom-header th {
|
||||
background: #f7f8fa !important;
|
||||
color: #888;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #f0f0f0 !important;
|
||||
border-top: 1px solid #f0f0f0 !important;
|
||||
padding-top: 16px !important;
|
||||
padding-bottom: 16px !important;
|
||||
height: 56px !important;
|
||||
line-height: 24px !important;
|
||||
}
|
||||
.custom-row td {
|
||||
border-bottom: 1px solid #f0f0f0 !important;
|
||||
border-top: none !important;
|
||||
padding-top: 14px !important;
|
||||
padding-bottom: 14px !important;
|
||||
height: 52px !important;
|
||||
line-height: 22px !important;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
.table-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.table-summary {
|
||||
color: #888;
|
||||
font-size: 13px;
|
||||
}
|
||||
.custom-pagination {
|
||||
.el-pager li {
|
||||
border-radius: 6px;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 16px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
|
||||
&.status-all {
|
||||
background: #f0f2f5;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
&.status-processed {
|
||||
background: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
&.status-rejected {
|
||||
background: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
&.status-closed {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.detail-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
}
|
||||
}
|
||||
|
||||
&.reply-btn {
|
||||
background: #67c23a;
|
||||
border-color: #67c23a;
|
||||
&:hover {
|
||||
background: #529b2e;
|
||||
border-color: #529b2e;
|
||||
}
|
||||
}
|
||||
|
||||
&.delete-btn {
|
||||
background: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
&:hover {
|
||||
background: #e64242;
|
||||
border-color: #e64242;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -10,7 +10,10 @@
|
||||
clearable
|
||||
/>
|
||||
<div class="user-action">
|
||||
<el-button type="primary" class="add-btn" @click="onAdd">添加用户</el-button>
|
||||
<el-button class="refresh-btn" @click="handleRefresh">
|
||||
<i class="el-icon-refresh"></i>
|
||||
<span>刷新</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-card">
|
||||
@ -49,14 +52,18 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="birthday" label="注册时间" width="200" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.birthday ? scope.row.birthday : '-' }}
|
||||
{{ scope.row.firstVisitAt ? scope.row.firstVisitAt : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="lastLoginStoreName" label="最近登录门店" width="200" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<el-table-column label="操作" width="160" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" class="edit-btn" @click="onEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" class="delete-btn" @click="onDeleteSingle(scope.row)">删除</el-button>
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="mini" class="action-btn edit-btn" @click="onEdit(scope.row)">
|
||||
<i class="el-icon-edit"></i>
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -80,7 +87,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userList, deleteUser, batchDeleteUsers } from '@/api/user'
|
||||
import { userList } from '@/api/user'
|
||||
|
||||
export default {
|
||||
name: 'UserList',
|
||||
@ -125,34 +132,12 @@ export default {
|
||||
this.searchForm.page = val
|
||||
this.getList()
|
||||
},
|
||||
onAdd() {
|
||||
// TODO: 实现添加用户弹窗
|
||||
this.$message.info('添加用户功能待实现')
|
||||
},
|
||||
onEdit(row) {
|
||||
// TODO: 实现编辑功能
|
||||
this.$message.info('编辑功能待实现')
|
||||
},
|
||||
async onDeleteSingle(row) {
|
||||
try {
|
||||
await this.$confirm('确认删除该用户吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
const res = await deleteUser(row.id)
|
||||
if (res.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
handleRefresh() {
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.message || '删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
this.$message.error('删除失败')
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,12 +161,38 @@ export default {
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.user-action .add-btn {
|
||||
background: #556ff6;
|
||||
border: none;
|
||||
.user-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.user-action .refresh-btn {
|
||||
background: #f7f8fa;
|
||||
border: 1px solid #e3e6f0;
|
||||
border-radius: 6px;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
background: #556ff6;
|
||||
color: #fff;
|
||||
border-color: #556ff6;
|
||||
i {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 14px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.table-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
@ -271,14 +282,31 @@ export default {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
.edit-btn {
|
||||
color: #556ff6;
|
||||
font-weight: 500;
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.edit-btn {
|
||||
background: #556ff6;
|
||||
border-color: #556ff6;
|
||||
&:hover {
|
||||
background: #3d4fd8;
|
||||
border-color: #3d4fd8;
|
||||
}
|
||||
}
|
||||
.delete-btn {
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
margin-left: 8px;
|
||||
}
|
||||
::v-deep .el-breadcrumb__inner,
|
||||
::v-deep .el-breadcrumb__inner a {
|
||||
|
||||
Reference in New Issue
Block a user