1
This commit is contained in:
169
demo/src/views/ai/ai_messages.vue
Normal file
169
demo/src/views/ai/ai_messages.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="消息类型">
|
||||
<Select v-model="gridOption.param.seachOption.message_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="question">提问</Option>
|
||||
<Option value="feedback">反馈</Option>
|
||||
<Option value="suggestion">建议</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="处理状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="pending">待处理</Option>
|
||||
<Option value="processing">处理中</Option>
|
||||
<Option value="completed">已完成</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import ai_messagesServer from '@/api/ai/ai_messages_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["message_type"] = [{ required: true, message: '请填写消息类型' }];
|
||||
rules["content"] = [{ required: true, message: '请填写消息内容' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'nickname', value: '用户昵称' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
message_type: null,
|
||||
status: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 120 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 120 },
|
||||
{ title: '消息类型', key: 'message_type', minWidth: 120 },
|
||||
{ title: '消息内容', key: 'content', minWidth: 300 },
|
||||
{ title: 'AI回复', key: 'ai_response', minWidth: 300 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '用户ID', key: 'user_id', type: 'number', required: true },
|
||||
{ title: '消息类型', key: 'message_type', type: 'text', required: true },
|
||||
{ title: '消息内容', key: 'content', type: 'textarea', required: true },
|
||||
{ title: 'AI回复', key: 'ai_response', type: 'textarea' },
|
||||
{ title: '处理状态', key: 'status', type: 'select', options: [
|
||||
{ value: 'pending', label: '待处理' },
|
||||
{ value: 'processing', label: '处理中' },
|
||||
{ value: 'completed', label: '已完成' },
|
||||
{ value: 'failed', label: '处理失败' }
|
||||
]}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
ai_messagesServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await ai_messagesServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
ai_messagesServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, 'AI消息管理.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
message_type: null,
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
161
demo/src/views/ball/game_comments.vue
Normal file
161
demo/src/views/ball/game_comments.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="active">正常</Option>
|
||||
<Option value="hidden">隐藏</Option>
|
||||
<Option value="deleted">已删除</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import game_commentsServer from '@/api/ball/game_comments_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["game_id"] = [{ required: true, type: "number", message: '请填写球局ID', trigger: 'change' }];
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["content"] = [{ required: true, message: '请填写评论内容' }];
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: 'game_id', value: '球局ID' },
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'content', value: '评论内容' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'content',
|
||||
value: '',
|
||||
status: null // 状态筛选
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '球局ID', key: 'game_id', minWidth: 120 },
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 120 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 120 },
|
||||
{ title: '评论内容', key: 'content', minWidth: 300 },
|
||||
{ title: '点赞数', key: 'like_count', minWidth: 120 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '球局ID', key: 'game_id', type: 'number', required: true },
|
||||
{ title: '用户ID', key: 'user_id', type: 'number', required: true },
|
||||
{ title: '评论内容', key: 'content', type: 'textarea', required: true },
|
||||
{ title: '点赞数', key: 'like_count', type: 'number' },
|
||||
{ title: '状态', key: 'status', type: 'select', options: [
|
||||
{ value: 'active', label: '正常' },
|
||||
{ value: 'hidden', label: '隐藏' },
|
||||
{ value: 'deleted', label: '已删除' }
|
||||
]}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
game_commentsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await game_commentsServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'content',
|
||||
value: '',
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
exportCsv() {
|
||||
game_commentsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '球局评论.csv');
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
314
demo/src/views/ball/game_participants.vue
Normal file
314
demo/src/views/ball/game_participants.vue
Normal file
@@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px" :placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="参与状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="joined">已加入</Option>
|
||||
<Option value="cancelled">已取消</Option>
|
||||
<Option value="substitute">替补</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="付款状态">
|
||||
<Select v-model="gridOption.param.seachOption.payment_status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="pending">待支付</Option>
|
||||
<Option value="paid">已支付</Option>
|
||||
<Option value="refunded">已退款</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import gameParticipantsServer from '@/api/ball/game_participants_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["id"] = [{ required: true, message: '请填写ID' }];
|
||||
rules["game_id"] = [{ required: true, type: "number", message: '请输入球局ID', trigger: 'change' }];
|
||||
rules["user_id"] = [{ required: true, message: '请填写参与用户ID' }];
|
||||
rules["status"] = [{ required: true, message: '请选择参与状态' }];
|
||||
rules["payment_status"] = [{ required: true, message: '请选择付款状态' }];
|
||||
rules["price"] = [{ required: false, type: "number", message: '请输入价格(元)', trigger: 'change' }];
|
||||
rules["payment_order_id"] = [{ required: false, message: '请填写支付订单ID' }];
|
||||
rules["join_message"] = [{ required: false, message: '请填写加入留言' }];
|
||||
rules["skill_level"] = [{ required: false, type: "number", message: '请输入技能水平(NTRP)', trigger: 'change' }];
|
||||
rules["contact_info"] = [{ required: false, message: '请填写联系方式' }];
|
||||
rules["joined_at"] = [{ required: true, message: '请选择加入时间' }];
|
||||
rules["cancelled_at"] = [{ required: false, message: '请选择取消时间' }];
|
||||
rules["cancel_reason"] = [{ required: false, message: '请填写取消原因' }];
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: "game_id", value: "球局ID" },
|
||||
{ key: "user_id", value: "用户ID" },
|
||||
{ key: "join_message", value: "加入留言" }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: "game_id",
|
||||
value: "",
|
||||
status: null, // 参与状态筛选
|
||||
payment_status: null // 付款状态筛选
|
||||
},
|
||||
pageOption: {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
rules,
|
||||
columns: [
|
||||
{ key: 'id', title: 'ID', minWidth: 80, is_show_edit: 0 },
|
||||
|
||||
{ key: "id", title: "ID", disabled: true, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{
|
||||
key: "game_id",
|
||||
title: "球局",
|
||||
disabled: true,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
data_type: "number",
|
||||
com: "InputNumber",
|
||||
render: (h, params) => {
|
||||
const game = params.row.gme_game;
|
||||
if (game) {
|
||||
return h('span', { attrs: { title: game.title } }, `#${game.id} ${game.title.substring(0, 10)}...`);
|
||||
}
|
||||
return h('span', `#${params.row.game_id}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "user_id",
|
||||
title: "用户",
|
||||
disabled: true,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
render: (h, params) => {
|
||||
const user = params.row.wch_user;
|
||||
if (user) {
|
||||
return h('span', { attrs: { title: `${user.nickname} (${user.phone})` } }, user.nickname);
|
||||
}
|
||||
return h('span', `用户#${params.row.user_id}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
title: "参与状态",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "joined", label: "已加入" },
|
||||
{ value: "cancelled", label: "已取消" },
|
||||
{ value: "substitute", label: "替补" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'joined': { text: '已加入', color: 'green' },
|
||||
'cancelled': { text: '已取消', color: 'red' },
|
||||
'substitute': { text: '替补', color: 'orange' }
|
||||
};
|
||||
const status = statusMap[params.row.status] || { text: params.row.status || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: status.color } }, status.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "payment_status", title: "付款状态", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Select", options: [
|
||||
{ value: "pending", label: "待支付" },
|
||||
{ value: "paid", label: "已支付" },
|
||||
{ value: "refunded", label: "已退款" }
|
||||
]
|
||||
},
|
||||
{ key: "price", title: "价格(元)", disabled: false, is_show_edit: 1, is_show_list: 0, data_type: "number", com: "InputNumber" },
|
||||
{ key: "payment_order_id", title: "支付订单ID", disabled: true, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "join_message", title: "加入留言", disabled: false, is_show_edit: 1, is_show_list: 0, com: "TextArea" },
|
||||
{
|
||||
key: "skill_level",
|
||||
title: "技能水平",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
data_type: "number",
|
||||
com: "InputNumber",
|
||||
min: 0,
|
||||
max: 7,
|
||||
step: 0.1,
|
||||
render: (h, params) => {
|
||||
const level = params.row.skill_level;
|
||||
if (level === null || level === undefined || level === 0) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '未设置');
|
||||
}
|
||||
return h('Tag', { props: { color: 'blue' } }, `NTRP ${level}`);
|
||||
}
|
||||
},
|
||||
{ key: "contact_info", title: "联系方式", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "joined_at", title: "加入时间", disabled: true, is_show_edit: 1, is_show_list: 1, com: "DatePicker" },
|
||||
{ key: "cancelled_at", title: "取消时间", disabled: false, is_show_edit: 1, is_show_list: 0, com: "DatePicker" },
|
||||
{ key: "cancel_reason", title: "取消原因", disabled: false, is_show_edit: 1, is_show_list: 0, com: "TextArea" },
|
||||
{ key: 'create_time', title: '创建时间', minWidth: 150, is_show_edit: 0 },
|
||||
{ key: 'last_modify_time', title: '更新时间', minWidth: 150, is_show_edit: 0 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
is_show_list: 1,
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '查看',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.showViewWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '修改',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
},],
|
||||
data: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
let editTempColumns = this.gridOption.columns.filter(p => p.is_show_edit === 1)
|
||||
return editTempColumns
|
||||
},
|
||||
listColumns() {
|
||||
let listTempColumns = this.gridOption.columns.filter(p => p.is_show_list === 1)
|
||||
return listTempColumns
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.initCol()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.query(1)
|
||||
},
|
||||
async initCol() {
|
||||
|
||||
let columnRows = []
|
||||
let columnKeys = columnRows.map(p => p.key)
|
||||
let newColumns = this.gridOption.columns.filter(p => columnKeys.indexOf(p.key) > -1)
|
||||
for (let i = 0; i < newColumns.length; i++) {
|
||||
let curColumn = newColumns[i]
|
||||
let modelMap = columnRows[i].map_option
|
||||
let res = await menuServer.modelInterface({ model_key: columnRows[i].modelKey, map_option: modelMap })
|
||||
curColumn.source = res.data
|
||||
}
|
||||
|
||||
},
|
||||
async inquiry() {
|
||||
let res = await gameParticipantsServer.all(this.gridOption.param)
|
||||
this.gridOption.data = res.data
|
||||
},
|
||||
async query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
let res = await gameParticipantsServer.page(this.gridOption.param)
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
},
|
||||
async showAddWarp() {
|
||||
this.$refs.editModal.addShow({
|
||||
'status': 'joined',
|
||||
'payment_status': 'pending',
|
||||
'price': 0,
|
||||
'skill_level': 0,
|
||||
'join_message': '',
|
||||
'contact_info': '',
|
||||
'cancel_reason': '',
|
||||
'joined_at': 'CURRENT_TIMESTAMP'
|
||||
}, async (newRow) => {
|
||||
let res = await gameParticipantsServer.add(newRow)
|
||||
rootVue.$Message.success('新增成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
async showViewWarp(row) {
|
||||
this.$refs.editModal.viewShow(row)
|
||||
},
|
||||
async showEditWarp(row) {
|
||||
this.$refs.editModal.editShow(row, async (newRow) => {
|
||||
let valid = await this.$refs['editModal'].$refs['From'].validate()
|
||||
if (valid) {
|
||||
let res = await gameParticipantsServer.edit(newRow)
|
||||
rootVue.$Message.success('修改成功!')
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
async delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await gameParticipantsServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'game_id',
|
||||
value: '',
|
||||
status: null,
|
||||
payment_status: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
async exportCsv(row) {
|
||||
await gameParticipantsServer.exportCsv(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
|
||||
</style>
|
||||
553
demo/src/views/ball/games.vue
Normal file
553
demo/src/views/ball/games.vue
Normal file
@@ -0,0 +1,553 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="球局类型">
|
||||
<Select v-model="gridOption.param.seachOption.game_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="个人球局">个人球局</Option>
|
||||
<Option value="团队球局">团队球局</Option>
|
||||
<Option value="比赛球局">比赛球局</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="玩法类型">
|
||||
<Select v-model="gridOption.param.seachOption.play_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="单打">单打</Option>
|
||||
<Option value="双打">双打</Option>
|
||||
<Option value="混合双打">混合双打</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model="gridOption.param.seachOption.match_status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="0">未开始</Option>
|
||||
<Option :value="1">进行中</Option>
|
||||
<Option :value="2">已结束</Option>
|
||||
<Option :value="3">已取消</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import gamesServer from '@/api/ball/games_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["id"] = [{ required: true, message: '请填写' }];
|
||||
rules["title"] = [{ required: true, message: '请填写球局标题' }];
|
||||
rules["description"] = [{ required: true, message: '请填写球局描述' }];
|
||||
rules["game_type"] = [{ required: true, message: '请填写球局类型' }];
|
||||
rules["publisher_id"] = [{ required: true, message: '请填写发布者ID' }];
|
||||
rules["venue_id"] = [{ required: true, message: '请填写场地ID' }];
|
||||
rules["max_players"] = [{ required: true, type: "number", message: '请填写最大参与人数', trigger: 'change' }];
|
||||
rules["current_players"] = [{ required: false, type: "number", message: '请填写当前参与人数', trigger: 'change' }];
|
||||
rules["start_time"] = [{ required: true, message: '请选择开始时间', trigger: 'change' }];
|
||||
rules["end_time"] = [{ required: true, message: '请选择结束时间', trigger: 'change' }];
|
||||
rules["price"] = [{ required: true, type: "number", message: '请填写价格', trigger: 'change' }];
|
||||
rules["price_mode"] = [{ required: true, message: '请选择费用模式', trigger: 'change' }];
|
||||
rules["court_type"] = [{ required: true, message: '请选择场地类型', trigger: 'change' }];
|
||||
rules["court_surface"] = [{ required: true, message: '请选择场地材质', trigger: 'change' }];
|
||||
rules["gender_limit"] = [{ required: true, message: '请选择性别限制', trigger: 'change' }];
|
||||
rules["skill_level_min"] = [{ required: false, type: "number", message: '请填写最低水平(NTRP)', trigger: 'change' }];
|
||||
rules["skill_level_max"] = [{ required: false, type: "number", message: '请填写最高水平(NTRP)', trigger: 'change' }];
|
||||
rules["is_urgent"] = [{ required: true, message: '请选择是否急招', trigger: 'change' }];
|
||||
rules["is_substitute_supported"] = [{ required: true, message: '请选择是否支持替补', trigger: 'change' }];
|
||||
rules["privacy_level"] = [{ required: true, message: '请选择隐私级别', trigger: 'change' }];
|
||||
rules["member_visibility"] = [{ required: true, message: '请选择成员可见性', trigger: 'change' }];
|
||||
rules["match_status"] = [{ required: true, message: '请选择状态', trigger: 'change' }];
|
||||
rules["location"] = [{ required: false, message: '请填写位置信息' }];
|
||||
rules["latitude"] = [{ required: false, type: "number", message: '请填写纬度', trigger: 'change' }];
|
||||
rules["longitude"] = [{ required: false, type: "number", message: '请填写经度', trigger: 'change' }];
|
||||
rules["play_type"] = [{ required: true, message: '请选择玩法类型', trigger: 'change' }];
|
||||
rules["is_wechat_contact"] = [{ required: false, type: "number", message: '请选择是否允许微信联系', trigger: 'change' }];
|
||||
rules["wechat_contact"] = [{ required: false, message: '请填写微信号' }];
|
||||
rules["venue_description"] = [{ required: false, message: '请填写场地描述' }];
|
||||
rules["venue_image_list"] = [{ required: false, message: '请上传场地预定截图' }];
|
||||
rules["location_name"] = [{ required: false, message: '请填写位置名称' }];
|
||||
rules["remark"] = [{ required: false, message: '请填写备注' }];
|
||||
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: "title", value: "球局标题" },
|
||||
{ key: "description", value: "球局描述" },
|
||||
{ key: "publisher_id", value: "发布者ID" },
|
||||
{ key: "venue_id", value: "场地ID" },
|
||||
{ key: "location_name", value: "位置名称" }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: "title",
|
||||
value: "",
|
||||
game_type: null, // 球局类型筛选
|
||||
play_type: null, // 玩法类型筛选
|
||||
match_status: null // 状态筛选
|
||||
},
|
||||
pageOption: {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
rules,
|
||||
columns: [
|
||||
{ key: 'id', title: 'id', minWidth: 80, is_show_edit: 0 },
|
||||
|
||||
{ key: "id", title: "ID", disabled: true, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{
|
||||
key: "title",
|
||||
title: "标题",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
|
||||
render: (h, params) => {
|
||||
const title = params.row.title || '';
|
||||
const displayText = title.length > 15 ? title.substring(0, 15) + '...' : title;
|
||||
return h('span', { attrs: { title: title } }, displayText);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
key: "game_type",
|
||||
title: "类型",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
|
||||
options: [
|
||||
{ value: "个人球局", label: "个人球局" },
|
||||
{ value: "团队球局", label: "团队球局" },
|
||||
{ value: "比赛球局", label: "比赛球局" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const typeMap = {
|
||||
'个人球局': { text: '个人', color: 'blue' },
|
||||
'团队球局': { text: '团队', color: 'green' },
|
||||
'比赛球局': { text: '比赛', color: 'purple' }
|
||||
};
|
||||
const type = typeMap[params.row.game_type] || { text: params.row.game_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: type.color } }, type.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "play_type",
|
||||
title: "玩法",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
|
||||
options: [
|
||||
{ value: "单打", label: "单打" },
|
||||
{ value: "双打", label: "双打" },
|
||||
{ value: "混合双打", label: "混合双打" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const playMap = {
|
||||
'单打': { text: '单打', color: 'orange' },
|
||||
'双打': { text: '双打', color: 'cyan' },
|
||||
'混合双打': { text: '混双', color: 'pink' }
|
||||
};
|
||||
const play = playMap[params.row.play_type] || { text: params.row.play_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: play.color } }, play.text);
|
||||
}
|
||||
},
|
||||
{ key: "publisher_id", title: "发布者ID", disabled: true, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "venue_id", title: "场地ID", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{
|
||||
key: "max_players",
|
||||
title: "最大人数",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
data_type: "number",
|
||||
com: "InputNumber",
|
||||
render: (h, params) => {
|
||||
const max = params.row.max_players || 0;
|
||||
return h('span', { style: { color: '#2d8cf0' } }, `${max}人`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "current_players",
|
||||
title: "当前人数",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
data_type: "number",
|
||||
com: "InputNumber",
|
||||
render: (h, params) => {
|
||||
const current = params.row.current_players || 0;
|
||||
const max = params.row.max_players || 0;
|
||||
const color = current >= max ? '#f56c6c' : '#67c23a';
|
||||
return h('span', { style: { color: color } }, `${current}人`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "start_time",
|
||||
title: "开始时间",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "DatePicker",
|
||||
|
||||
render: (h, params) => {
|
||||
const time = params.row.start_time;
|
||||
if (!time) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '未设置');
|
||||
}
|
||||
// 只显示月-日 时:分
|
||||
const date = new Date(time);
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0');
|
||||
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||
const displayTime = `${month}-${day} ${hour}:${minute}`;
|
||||
return h('span', { attrs: { title: time } }, displayTime);
|
||||
}
|
||||
},
|
||||
{ key: "end_time", title: "结束时间", disabled: false, is_show_edit: 1, is_show_list: 0, com: "DatePicker" },
|
||||
{
|
||||
key: "price",
|
||||
title: "价格",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "InputNumber",
|
||||
data_type: "number",
|
||||
|
||||
render: (h, params) => {
|
||||
const price = params.row.price;
|
||||
if (price === null || price === undefined || price === 0) {
|
||||
return h('Tag', { props: { color: 'green' } }, '免费');
|
||||
}
|
||||
return h('span', { style: { color: '#f56c6c', fontWeight: 'bold' } }, `¥${price}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "price_mode", title: "费用模式", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Select", options: [
|
||||
{ value: "AA", label: "AA制" },
|
||||
{ value: "免费", label: "免费" },
|
||||
{ value: "付费", label: "付费" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "court_type",
|
||||
title: "场地类型",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "室内", label: "室内" },
|
||||
{ value: "室外", label: "室外" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const typeMap = {
|
||||
'室内': { text: '室内', color: 'blue' },
|
||||
'室外': { text: '室外', color: 'green' }
|
||||
};
|
||||
const type = typeMap[params.row.court_type] || { text: params.row.court_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: type.color } }, type.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "court_surface",
|
||||
title: "场地材质",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "硬地", label: "硬地" },
|
||||
{ value: "红土", label: "红土" },
|
||||
{ value: "草地", label: "草地" },
|
||||
{ value: "地毯", label: "地毯" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const surfaceMap = {
|
||||
'硬地': { text: '硬地', color: 'cyan' },
|
||||
'红土': { text: '红土', color: 'orange' },
|
||||
'草地': { text: '草地', color: 'green' },
|
||||
'地毯': { text: '地毯', color: 'purple' }
|
||||
};
|
||||
const surface = surfaceMap[params.row.court_surface] || { text: params.row.court_surface || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: surface.color } }, surface.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "gender_limit",
|
||||
title: "性别限制",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "0", label: "不限" },
|
||||
{ value: "1", label: "仅限男生" },
|
||||
{ value: "2", label: "仅限女生" },
|
||||
{ value: "3", label: "仅限同性" },
|
||||
{ value: "4", label: "仅限异性" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const genderMap = {
|
||||
'0': { text: '不限', color: 'default' },
|
||||
'1': { text: '仅限男生', color: 'blue' },
|
||||
'2': { text: '仅限女生', color: 'pink' },
|
||||
'3': { text: '仅限同性', color: 'purple' },
|
||||
'4': { text: '仅限异性', color: 'orange' }
|
||||
};
|
||||
const gender = genderMap[params.row.gender_limit] || { text: '不限', color: 'default' };
|
||||
return h('Tag', { props: { color: gender.color } }, gender.text);
|
||||
}
|
||||
},
|
||||
{ key: "skill_level_min", title: "最低水平(NTRP)", disabled: false, is_show_edit: 1, is_show_list: 0, com: "InputNumber", data_type: "number" },
|
||||
{ key: "skill_level_max", title: "最高水平(NTRP)", disabled: false, is_show_edit: 1, is_show_list: 0, com: "InputNumber", data_type: "number" },
|
||||
{
|
||||
key: "is_urgent", title: "是否急招", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Radio", options: [
|
||||
{ value: "0", label: "否" },
|
||||
{ value: "1", label: "是" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "is_substitute_supported", title: "支持替补", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Radio", options: [
|
||||
{ value: "0", label: "否" },
|
||||
{ value: "1", label: "是" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "is_wechat_contact", title: "微信联系", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Radio", options: [
|
||||
{ value: 0, label: "否" },
|
||||
{ value: 1, label: "是" }
|
||||
]
|
||||
},
|
||||
{ key: "wechat_contact", title: "微信号", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "substitute_limit", title: "替补人数", disabled: false, is_show_edit: 1, is_show_list: 0, data_type: "number", com: "InputNumber" },
|
||||
{
|
||||
key: "privacy_level", title: "隐私级别", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Select", options: [
|
||||
{ value: "public", label: "公开" },
|
||||
{ value: "private", label: "私密" },
|
||||
{ value: "friends", label: "仅好友" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "member_visibility", title: "成员可见性", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Select", options: [
|
||||
{ value: "all", label: "所有人" },
|
||||
{ value: "participants", label: "仅参与者" },
|
||||
{ value: "friends", label: "仅好友" }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "match_status",
|
||||
title: "状态",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: 0, label: "未开始" },
|
||||
{ value: 1, label: "进行中" },
|
||||
{ value: 2, label: "已结束" },
|
||||
{ value: 3, label: "已取消" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
0: { text: '未开始', color: 'blue' },
|
||||
1: { text: '进行中', color: 'green' },
|
||||
2: { text: '已结束', color: 'gray' },
|
||||
3: { text: '已取消', color: 'red' }
|
||||
};
|
||||
const status = statusMap[params.row.match_status] || { text: '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: status.color } }, status.text);
|
||||
}
|
||||
},
|
||||
{ key: "venue_description", title: "场地描述", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "venue_image_list", title: "预定截图", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Upload", action: "/api/upload" },
|
||||
{ key: "location_name", title: "位置名称", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "location", title: "位置信息", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "latitude", title: "纬度", disabled: false, is_show_edit: 1, is_show_list: 0, com: "InputNumber", data_type: "number" },
|
||||
{ key: "longitude", title: "经度", disabled: false, is_show_edit: 1, is_show_list: 0, com: "InputNumber", data_type: "number" },
|
||||
{ key: "deadline_hours", title: "截止时间(小时)", disabled: false, is_show_edit: 1, is_show_list: 0, data_type: "number", com: "InputNumber" },
|
||||
{ key: "remark", title: "备注", disabled: false, is_show_edit: 1, is_show_list: 0, com: "TextArea" },
|
||||
{ key: "description", title: "球局描述", disabled: false, is_show_edit: 1, is_show_list: 0, com: "TextArea" },
|
||||
{ key: "last_modify_time", title: "更新时间", disabled: true, is_show_edit: 1, is_show_list: 0, com: "DatePicker" },
|
||||
{ key: 'create_time', title: '创建时间', minWidth: 100, is_show_edit: 0 },
|
||||
{ key: 'last_modify_time', title: '更新时间', minWidth: 100, is_show_edit: 0 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
is_show_list: 1,
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '修改',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
},],
|
||||
data: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
let editTempColumns = this.gridOption.columns.filter(p => p.is_show_edit === 1)
|
||||
return editTempColumns
|
||||
},
|
||||
listColumns() {
|
||||
let listTempColumns = this.gridOption.columns.filter(p => p.is_show_list === 1)
|
||||
return listTempColumns
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.initCol()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.query(1)
|
||||
},
|
||||
async initCol() {
|
||||
|
||||
let columnRows = []
|
||||
let columnKeys = columnRows.map(p => p.key)
|
||||
let newColumns = this.gridOption.columns.filter(p => columnKeys.indexOf(p.key) > -1)
|
||||
for (let i = 0; i < newColumns.length; i++) {
|
||||
let curColumn = newColumns[i]
|
||||
let modelMap = columnRows[i].map_option
|
||||
let res = await menuServer.modelInterface({ model_key: columnRows[i].modelKey, map_option: modelMap })
|
||||
curColumn.source = res.data
|
||||
}
|
||||
|
||||
},
|
||||
async inquiry() {
|
||||
let res = await gamesServer.all(this.gridOption.param)
|
||||
this.gridOption.data = res.data
|
||||
},
|
||||
async query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
let res = await gamesServer.page(this.gridOption.param)
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
},
|
||||
async showAddWarp() {
|
||||
this.$refs.editModal.addShow({
|
||||
'title': '',
|
||||
'description': '',
|
||||
'game_type': '个人球局',
|
||||
'play_type': '双打',
|
||||
'publisher_id': '',
|
||||
'venue_id': '',
|
||||
'max_players': 4,
|
||||
'current_players': 0,
|
||||
'start_time': '',
|
||||
'end_time': '',
|
||||
'price': 0,
|
||||
'price_mode': 'AA',
|
||||
'court_type': '室外',
|
||||
'court_surface': '硬地',
|
||||
'gender_limit': 0,
|
||||
'skill_level_min': 1.0,
|
||||
'skill_level_max': 7.0,
|
||||
'is_urgent': 0,
|
||||
'is_substitute_supported': 0,
|
||||
'is_wechat_contact': 0,
|
||||
'wechat_contact': '',
|
||||
'substitute_limit': 0,
|
||||
'privacy_level': 'public',
|
||||
'member_visibility': 'all',
|
||||
'match_status': 0,
|
||||
'location': '',
|
||||
'location_name': '',
|
||||
'latitude': 0,
|
||||
'longitude': 0,
|
||||
'deadline_hours': 1,
|
||||
'venue_description': '',
|
||||
'remark': ''
|
||||
}, async (newRow) => {
|
||||
let res = await gamesServer.add(newRow)
|
||||
rootVue.$Message.success('新增成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
async showEditWarp(row) {
|
||||
this.$refs.editModal.editShow(row, async (newRow) => {
|
||||
let valid = await this.$refs['editModal'].$refs['From'].validate()
|
||||
if (valid) {
|
||||
let res = await gamesServer.edit(newRow)
|
||||
rootVue.$Message.success('修改成功!')
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
async delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await gamesServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'title',
|
||||
value: '',
|
||||
game_type: null,
|
||||
play_type: null,
|
||||
match_status: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
async exportCsv(row) {
|
||||
await gamesServer.exportCsv(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
297
demo/src/views/ball/venues.vue
Normal file
297
demo/src/views/ball/venues.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="场地类型">
|
||||
<Select v-model="gridOption.param.seachOption.venue_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="indoor">室内</Option>
|
||||
<Option value="outdoor">室外</Option>
|
||||
<Option value="semi-outdoor">半室外</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="地面类型">
|
||||
<Select v-model="gridOption.param.seachOption.surface_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="hard">硬地</Option>
|
||||
<Option value="clay">红土</Option>
|
||||
<Option value="grass">草地</Option>
|
||||
<Option value="carpet">地毯</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="active">营业中</Option>
|
||||
<Option value="inactive">暂停营业</Option>
|
||||
<Option value="closed">已关闭</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import venuesServer from '@/api/ball/venues_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["id"] = [{ required: true, message: '请填写' }];
|
||||
rules["name"] = [{ required: true, message: '请填写场地名称' }];
|
||||
rules["address"] = [{ required: true, message: '请填写详细地址' }];
|
||||
rules["latitude"] = [{ required: true, message: '请填写纬度' }];
|
||||
rules["longitude"] = [{ required: true, message: '请填写经度' }];
|
||||
rules["venue_type"] = [{ required: true, message: '请填写场地类型' }];
|
||||
rules["court_count"] = [{ required: true, type: "number", message: '请选择场地数量', trigger: 'change' }];
|
||||
rules["price_per_hour"] = [{ required: true, message: '请填写每小时价格' }];
|
||||
rules["facilities"] = [{ required: true, message: '请填写设施描述' }];
|
||||
rules["surface_type"] = [{ required: true, message: '请选择地面类型' }];
|
||||
rules["timeSlot"] = [{ required: false, message: '请填写营业时间' }];
|
||||
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: "name", value: "场地名称" },
|
||||
{ key: "address", value: "详细地址" },
|
||||
{ key: "facilities", value: "设施描述" }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: "name",
|
||||
value: "",
|
||||
venue_type: null, // 场地类型筛选
|
||||
surface_type: null, // 地面类型筛选
|
||||
status: null // 状态筛选
|
||||
},
|
||||
pageOption: {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
rules,
|
||||
columns: [
|
||||
{ key: 'id', title: 'id', is_show_edit: 0 },
|
||||
{ key: "name", title: "场地名称", disabled: false, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{ key: "address", title: "详细地址", disabled: false, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{ key: "latitude", title: "纬度", disabled: false, is_show_edit: 1, is_show_list: 1, com: "InputNumber", data_type: "number" },
|
||||
{ key: "longitude", title: "经度", disabled: false, is_show_edit: 1, is_show_list: 1, com: "InputNumber", data_type: "number" },
|
||||
{
|
||||
key: "venue_type",
|
||||
title: "场地类型",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "indoor", label: "室内" },
|
||||
{ value: "outdoor", label: "室外" },
|
||||
{ value: "semi-outdoor", label: "半室外" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const typeMap = {
|
||||
'indoor': { text: '室内', color: 'blue' },
|
||||
'outdoor': { text: '室外', color: 'green' },
|
||||
'semi-outdoor': { text: '半室外', color: 'cyan' }
|
||||
};
|
||||
const type = typeMap[params.row.venue_type] || { text: params.row.venue_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: type.color } }, type.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "surface_type",
|
||||
title: "地面类型",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "hard", label: "硬地" },
|
||||
{ value: "clay", label: "红土" },
|
||||
{ value: "grass", label: "草地" },
|
||||
{ value: "carpet", label: "地毯" },
|
||||
{ value: "indoor", label: "室内地面" },
|
||||
{ value: "other", label: "其他" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const surfaceMap = {
|
||||
'hard': { text: '硬地', color: 'cyan' },
|
||||
'clay': { text: '红土', color: 'orange' },
|
||||
'grass': { text: '草地', color: 'green' },
|
||||
'carpet': { text: '地毯', color: 'purple' },
|
||||
'indoor': { text: '室内地面', color: 'blue' },
|
||||
'other': { text: '其他', color: 'default' }
|
||||
};
|
||||
const surface = surfaceMap[params.row.surface_type] || { text: params.row.surface_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: surface.color } }, surface.text);
|
||||
}
|
||||
},
|
||||
{ key: "court_count", title: "场地数量", disabled: false, is_show_edit: 1, is_show_list: 1, data_type: "number", com: "InputNumber" },
|
||||
{ key: "price_per_hour", title: "每小时价格", disabled: false, is_show_edit: 1, is_show_list: 1, com: "InputNumber", data_type: "number" },
|
||||
{ key: "facilities", title: "设施描述", disabled: false, is_show_edit: 1, is_show_list: 0, com: "TextArea" },
|
||||
{ key: "timeSlot", title: "营业时间", disabled: false, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{
|
||||
key: "status",
|
||||
title: "状态",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
options: [
|
||||
{ value: "active", label: "营业中" },
|
||||
{ value: "inactive", label: "暂停营业" },
|
||||
{ value: "closed", label: "已关闭" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'active': { text: '营业中', color: 'green' },
|
||||
'inactive': { text: '暂停营业', color: 'orange' },
|
||||
'closed': { text: '已关闭', color: 'red' }
|
||||
};
|
||||
const status = statusMap[params.row.status] || { text: params.row.status || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: status.color } }, status.text);
|
||||
}
|
||||
},
|
||||
{ key: 'create_time', title: '创建时间', is_show_edit: 0 },
|
||||
{ key: 'last_modify_time', title: '更新时间', is_show_edit: 0 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '修改',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
|
||||
],
|
||||
data: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
let editTempColumns = this.gridOption.columns.filter(p => p.is_show_edit === 1)
|
||||
return editTempColumns
|
||||
},
|
||||
listColumns() {
|
||||
let listTempColumns = this.gridOption.columns.filter(p => p.is_show_list !== 0)
|
||||
return listTempColumns
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.initCol()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.query(1)
|
||||
},
|
||||
async initCol() {
|
||||
|
||||
let columnRows = []
|
||||
let columnKeys = columnRows.map(p => p.key)
|
||||
let newColumns = this.gridOption.columns.filter(p => columnKeys.indexOf(p.key) > -1)
|
||||
for (let i = 0; i < newColumns.length; i++) {
|
||||
let curColumn = newColumns[i]
|
||||
let modelMap = columnRows[i].map_option
|
||||
let res = await menuServer.modelInterface({ model_key: columnRows[i].modelKey, map_option: modelMap })
|
||||
curColumn.source = res.data
|
||||
}
|
||||
|
||||
},
|
||||
async inquiry() {
|
||||
let res = await venuesServer.all(this.gridOption.param)
|
||||
this.gridOption.data = res.data
|
||||
},
|
||||
async query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
let res = await venuesServer.page(this.gridOption.param)
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
},
|
||||
async showAddWarp() {
|
||||
this.$refs.editModal.addShow({ 'venue_type': 'indoor', 'surface_type': 'hard', 'court_count': '1', 'status': 'active', 'create_time': 'CURRENT_TIMESTAMP', 'updated_at': 'CURRENT_TIMESTAMP', }, async (newRow) => {
|
||||
let res = await venuesServer.add(newRow)
|
||||
rootVue.$Message.success('新增成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
async showEditWarp(row) {
|
||||
this.$refs.editModal.editShow(row, async (newRow) => {
|
||||
let valid = await this.$refs['editModal'].$refs['From'].validate()
|
||||
if (valid) {
|
||||
let res = await venuesServer.edit(newRow)
|
||||
rootVue.$Message.success('修改成功!')
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
async delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await venuesServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'name',
|
||||
value: '',
|
||||
venue_type: null,
|
||||
surface_type: null,
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
async exportCsv(row) {
|
||||
await venuesServer.exportCsv(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
357
demo/src/views/ball/wch_users.vue
Normal file
357
demo/src/views/ball/wch_users.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="性别">
|
||||
<Select v-model="gridOption.param.seachOption.gender" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="0">未知</Option>
|
||||
<Option :value="1">男</Option>
|
||||
<Option :value="2">女</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="NTRP等级">
|
||||
<Select v-model="gridOption.param.seachOption.ntrp_level" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="1">1.0</Option>
|
||||
<Option :value="2">2.0</Option>
|
||||
<Option :value="3">3.0</Option>
|
||||
<Option :value="4">4.0</Option>
|
||||
<Option :value="5">5.0</Option>
|
||||
<Option :value="6">6.0</Option>
|
||||
<Option :value="7">7.0</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import wch_usersServer from '@/api/ball/wch_users_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["nickname"] = [{ required: true, message: '请填写微信昵称' }];
|
||||
rules["phone"] = [{ required: false, message: '请填写手机号' }];
|
||||
rules["ntrp_level"] = [{ required: false, type: "number", message: '请填写网球等级', trigger: 'change' }];
|
||||
rules["occupation"] = [{ required: false, message: '请填写职业' }];
|
||||
rules["openid"] = [{ required: false, message: '请填写微信openid' }];
|
||||
rules["unionid"] = [{ required: false, message: '请填写微信unionid' }];
|
||||
rules["session_key"] = [{ required: false, message: '请填写会话密钥' }];
|
||||
rules["avatar_url"] = [{ required: false, message: '请填写微信头像URL' }];
|
||||
rules["gender"] = [{ required: false, type: "number", message: '请选择性别', trigger: 'change' }];
|
||||
rules["country"] = [{ required: false, message: '请填写国家' }];
|
||||
rules["province"] = [{ required: false, message: '请填写省份' }];
|
||||
rules["city"] = [{ required: false, message: '请填写城市' }];
|
||||
rules["language"] = [{ required: false, message: '请填写语言' }];
|
||||
rules["is_subscribed"] = [{ required: false, type: "number", message: '请选择是否关注公众号', trigger: 'change' }];
|
||||
rules["subscribe_time"] = [{ required: false, message: '请填写关注时间', trigger: 'change' }];
|
||||
rules["last_login_time"] = [{ required: false, message: '请填写最后登录时间', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: "id", value: "用户ID" },
|
||||
{ key: "nickname", value: "昵称" },
|
||||
{ key: "phone", value: "手机号" }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: "nickname",
|
||||
value: "",
|
||||
gender: null, // 性别筛选
|
||||
ntrp_level: null // NTRP等级筛选
|
||||
},
|
||||
pageOption: {
|
||||
total: 0,
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
rules,
|
||||
columns: [
|
||||
{ key: "id", title: "ID", disabled: true, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{ key: "openid", title: "微信openid", disabled: true, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "unionid", title: "微信unionid", disabled: true, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "session_key", title: "会话密钥", disabled: true, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{
|
||||
key: "nickname",
|
||||
title: "昵称",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
|
||||
render: (h, params) => {
|
||||
const nickname = params.row.nickname || '';
|
||||
const displayText = nickname.length > 10 ? nickname.substring(0, 10) + '...' : nickname;
|
||||
return h('span', { attrs: { title: nickname } }, displayText);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "avatar_url",
|
||||
title: "头像",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "UploadSingle",
|
||||
|
||||
render: (h, params) => {
|
||||
if (!params.row.avatar_url) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '无头像');
|
||||
}
|
||||
return h('div', {
|
||||
style: {
|
||||
'display': 'flex',
|
||||
'justify-content': 'center',
|
||||
'align-items': 'center',
|
||||
'padding': '4px 0'
|
||||
}
|
||||
}, [
|
||||
h('img', {
|
||||
style: {
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
borderRadius: '50%',
|
||||
objectFit: 'cover',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
attrs: {
|
||||
src: params.row.avatar_url,
|
||||
alt: params.row.nickname || '头像',
|
||||
title: '点击查看大图'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
// 点击查看大图
|
||||
this.$Modal.info({
|
||||
title: params.row.nickname + ' 的头像',
|
||||
render: (h) => {
|
||||
return h('div', { style: { textAlign: 'center' } }, [
|
||||
h('img', {
|
||||
style: {
|
||||
maxWidth: '100%',
|
||||
maxHeight: '400px',
|
||||
borderRadius: '8px'
|
||||
},
|
||||
attrs: { src: params.row.avatar_url }
|
||||
})
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
]);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "gender",
|
||||
title: "性别",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Radio",
|
||||
|
||||
options: [
|
||||
{ value: 0, label: "未知" },
|
||||
{ value: 1, label: "男" },
|
||||
{ value: 2, label: "女" }
|
||||
],
|
||||
render: (h, params) => {
|
||||
const genderMap = {
|
||||
0: { text: '未知', color: 'default' },
|
||||
1: { text: '男', color: 'blue' },
|
||||
2: { text: '女', color: 'pink' }
|
||||
};
|
||||
const gender = genderMap[params.row.gender] || { text: '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: gender.color } }, gender.text);
|
||||
}
|
||||
},
|
||||
{ key: "country", title: "国家", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "province", title: "省份", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{ key: "city", title: "城市", disabled: false, is_show_edit: 1, is_show_list: 1, com: "Input" },
|
||||
{ key: "language", title: "语言", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Input" },
|
||||
{
|
||||
key: "phone",
|
||||
title: "手机号",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
|
||||
render: (h, params) => {
|
||||
const phone = params.row.phone || '';
|
||||
if (!phone) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '未填写');
|
||||
}
|
||||
// 手机号脱敏显示
|
||||
const maskedPhone = phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
return h('span', { attrs: { title: phone } }, maskedPhone);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ntrp_level",
|
||||
title: "等级",
|
||||
disabled: false,
|
||||
is_show_edit: 1,
|
||||
is_show_list: 1,
|
||||
com: "InputNumber",
|
||||
|
||||
render: (h, params) => {
|
||||
const level = params.row.ntrp_level;
|
||||
if (level === null || level === undefined || level === 0) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '未设置');
|
||||
}
|
||||
return h('Tag', { props: { color: 'blue' } }, `${level}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "is_subscribed", title: "是否关注公众号", disabled: false, is_show_edit: 1, is_show_list: 0, com: "Radio", options: [
|
||||
{ value: 0, label: "否" },
|
||||
{ value: 1, label: "是" }
|
||||
]
|
||||
},
|
||||
{ key: "subscribe_time", title: "关注时间", disabled: true, is_show_edit: 1, is_show_list: 0, com: "DatePicker" },
|
||||
{ key: "last_login_time", title: "最后登录时间", disabled: true, is_show_edit: 1, is_show_list: 0, com: "DatePicker" },
|
||||
{ key: 'create_time', title: '创建时间', minWidth: 100, is_show_edit: 0, com: "DatePicker" },
|
||||
{ key: 'last_modify_time', title: '更新时间', minWidth: 100, is_show_edit: 0, com: "DatePicker" },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
is_show_list: 1,
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '修改',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
},],
|
||||
data: []
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
let editTempColumns = this.gridOption.columns.filter(p => p.is_show_edit === 1)
|
||||
return editTempColumns
|
||||
},
|
||||
listColumns() {
|
||||
let listTempColumns = this.gridOption.columns.filter(p => p.is_show_list === 1)
|
||||
return listTempColumns
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
this.initCol()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.query(1)
|
||||
},
|
||||
async initCol() {
|
||||
|
||||
let columnRows = []
|
||||
let columnKeys = columnRows.map(p => p.key)
|
||||
let newColumns = this.gridOption.columns.filter(p => columnKeys.indexOf(p.key) > -1)
|
||||
for (let i = 0; i < newColumns.length; i++) {
|
||||
let curColumn = newColumns[i]
|
||||
let modelMap = columnRows[i].map_option
|
||||
let res = await menuServer.modelInterface({ model_key: columnRows[i].modelKey, map_option: modelMap })
|
||||
curColumn.source = res.data
|
||||
}
|
||||
|
||||
},
|
||||
async inquiry() {
|
||||
let res = await wch_usersServer.all(this.gridOption.param)
|
||||
this.gridOption.data = res.data
|
||||
},
|
||||
async query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
let res = await wch_usersServer.page(this.gridOption.param)
|
||||
this.gridOption.data = res.data.rows
|
||||
this.gridOption.param.pageOption.total = res.data.count
|
||||
},
|
||||
async showAddWarp() {
|
||||
this.$refs.editModal.addShow({ 'is_subscribed': '0', 'last_login_time': 'CURRENT_TIMESTAMP', 'create_time': 'CURRENT_TIMESTAMP', 'updated_at': 'CURRENT_TIMESTAMP', }, async (newRow) => {
|
||||
let res = await wch_usersServer.add(newRow)
|
||||
rootVue.$Message.success('新增成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
async showEditWarp(row) {
|
||||
this.$refs.editModal.editShow(row, async (newRow) => {
|
||||
let valid = await this.$refs['editModal'].$refs['From'].validate()
|
||||
if (valid) {
|
||||
let res = await wch_usersServer.edit(newRow)
|
||||
rootVue.$Message.success('修改成功!')
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
async delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await wch_usersServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.init()
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
gender: null,
|
||||
ntrp_level: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
async exportCsv(row) {
|
||||
await wch_usersServer.exportCsv(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
394
demo/src/views/business/hot_city_qr.vue
Normal file
394
demo/src/views/business/hot_city_qr.vue
Normal file
@@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增城市二维码</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem label="城市名称" :label-width="80">
|
||||
<Input v-model="gridOption.param.seachOption.city_name" style="width: 200px" placeholder="请输入城市名称"
|
||||
@on-enter="query(1)" clearable />
|
||||
</FormItem>
|
||||
<FormItem label="状态" :label-width="60">
|
||||
<Select v-model="gridOption.param.seachOption.is_active" style="width: 120px" clearable>
|
||||
<Option :value="1">启用</Option>
|
||||
<Option :value="0">禁用</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="warning" @click="batchDelete" class="ml10" :disabled="selectedIds.length === 0">
|
||||
批量删除 ({{ selectedIds.length }})
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query" @on-selection-change="handleSelectionChange"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import hotCityQrServer from '@/api/business/hot_city_qr_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["city_name"] = [{ required: true, message: '请填写城市名称', trigger: 'blur' }];
|
||||
rules["qr_code_url"] = [{ required: true, message: '请上传二维码图片', trigger: 'change' }];
|
||||
rules["sort_order"] = [{ required: false, type: "number", message: '请填写排序顺序', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
selectedIds: [],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
city_name: '',
|
||||
is_active: ''
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{
|
||||
type: 'selection',
|
||||
width: 60,
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '城市名称',
|
||||
key: 'city_name',
|
||||
minWidth: 120,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '二维码图片',
|
||||
key: 'qr_code_url',
|
||||
minWidth: 150,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
if (params.row.qr_code_url) {
|
||||
return h('div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px'
|
||||
}
|
||||
}, [
|
||||
h('img', {
|
||||
attrs: {
|
||||
src: params.row.qr_code_url,
|
||||
alt: '二维码'
|
||||
},
|
||||
style: {
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
this.previewImage(params.row.qr_code_url)
|
||||
}
|
||||
}
|
||||
}),
|
||||
h('a', {
|
||||
attrs: {
|
||||
href: params.row.qr_code_url,
|
||||
target: '_blank'
|
||||
},
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
color: '#2d8cf0'
|
||||
}
|
||||
}, '查看原图')
|
||||
])
|
||||
}
|
||||
return h('span', '-')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '描述说明',
|
||||
key: 'description',
|
||||
minWidth: 200,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
key: 'sort_order',
|
||||
width: 100,
|
||||
sortable: true,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: {
|
||||
color: 'blue'
|
||||
}
|
||||
}, params.row.sort_order || 0)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'is_active',
|
||||
width: 100,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: {
|
||||
color: params.row.is_active === 1 ? 'success' : 'default'
|
||||
}
|
||||
}, params.row.is_active === 1 ? '启用' : '禁用')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time',
|
||||
width: 160,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
key: 'update_time',
|
||||
width: 160,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
size: 'small',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{
|
||||
title: '城市名称',
|
||||
key: 'city_name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: '请输入城市名称,如:北京、上海'
|
||||
},
|
||||
{
|
||||
title: '二维码图片',
|
||||
key: 'qr_code_url',
|
||||
com: 'UploadSingle',
|
||||
required: true,
|
||||
uploadType: 'image',
|
||||
tip: '建议上传正方形图片,支持 JPG、PNG 格式'
|
||||
},
|
||||
{
|
||||
title: '描述说明',
|
||||
key: 'description',
|
||||
com: 'TextArea',
|
||||
placeholder: '请输入描述说明(选填)',
|
||||
rows: 3
|
||||
},
|
||||
{
|
||||
title: '排序顺序',
|
||||
key: 'sort_order',
|
||||
data_type: 'number',
|
||||
placeholder: '数字越小越靠前,默认为 0',
|
||||
min: 0,
|
||||
max: 9999
|
||||
},
|
||||
{
|
||||
title: '是否启用',
|
||||
key: 'is_active',
|
||||
com: 'i-switch',
|
||||
data_type: 'boolean',
|
||||
activeValue: 1,
|
||||
inactiveValue: 0,
|
||||
activeText: '启用',
|
||||
inactiveText: '禁用'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
// 查询列表
|
||||
query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
|
||||
const params = {
|
||||
page: this.gridOption.param.pageOption.page,
|
||||
pageSize: this.gridOption.param.pageOption.pageSize,
|
||||
...this.gridOption.param.seachOption
|
||||
}
|
||||
|
||||
hotCityQrServer.page(params).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.gridOption.data = res.data.list || []
|
||||
this.gridOption.param.pageOption.total = res.data.total || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 重置查询
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
city_name: '',
|
||||
is_active: ''
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
|
||||
// 显示新增弹窗
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.addShow({
|
||||
city_name: '',
|
||||
qr_code_url: '',
|
||||
description: '',
|
||||
sort_order: 0,
|
||||
is_active: 1
|
||||
}, (data) => {
|
||||
hotCityQrServer.add(data).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('添加成功')
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '添加失败')
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 显示编辑弹窗
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.editShow(row, (data) => {
|
||||
hotCityQrServer.edit(row.id, data).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('编辑成功')
|
||||
this.query()
|
||||
} else {
|
||||
this.$Message.error(res.message || '编辑失败')
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 删除确认
|
||||
delConfirm(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除城市"${row.city_name}"的二维码配置吗?`,
|
||||
onOk: () => {
|
||||
hotCityQrServer.del(row.id).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('删除成功')
|
||||
this.query()
|
||||
} else {
|
||||
this.$Message.error(res.message || '删除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 选择变化
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedIds = selection.map(item => item.id)
|
||||
},
|
||||
|
||||
// 批量删除
|
||||
batchDelete() {
|
||||
if (this.selectedIds.length === 0) {
|
||||
this.$Message.warning('请先选择要删除的记录')
|
||||
return
|
||||
}
|
||||
|
||||
this.$Modal.confirm({
|
||||
title: '确认批量删除',
|
||||
content: `确定要删除选中的 ${this.selectedIds.length} 条记录吗?`,
|
||||
onOk: () => {
|
||||
hotCityQrServer.batchDel(this.selectedIds).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('批量删除成功')
|
||||
this.selectedIds = []
|
||||
this.query()
|
||||
} else {
|
||||
this.$Message.error(res.message || '批量删除失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 预览图片
|
||||
previewImage(url) {
|
||||
this.$Modal.info({
|
||||
title: '二维码预览',
|
||||
render: (h) => {
|
||||
return h('div', {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
padding: '20px'
|
||||
}
|
||||
}, [
|
||||
h('img', {
|
||||
attrs: {
|
||||
src: url,
|
||||
alt: '二维码'
|
||||
},
|
||||
style: {
|
||||
maxWidth: '100%',
|
||||
maxHeight: '500px'
|
||||
}
|
||||
})
|
||||
])
|
||||
},
|
||||
width: 600
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
187
demo/src/views/message/msg_notifications.vue
Normal file
187
demo/src/views/message/msg_notifications.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="消息类型">
|
||||
<Select v-model="gridOption.param.seachOption.type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="system">系统消息</Option>
|
||||
<Option value="game">球局消息</Option>
|
||||
<Option value="payment">支付消息</Option>
|
||||
<Option value="activity">活动消息</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="推送状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="pending">待发送</Option>
|
||||
<Option value="sent">已发送</Option>
|
||||
<Option value="failed">发送失败</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import msg_notificationsServer from '@/api/message/msg_notifications_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["title"] = [{ required: true, message: '请填写消息标题' }];
|
||||
rules["content"] = [{ required: true, message: '请填写消息内容' }];
|
||||
rules["type"] = [{ required: true, message: '请填写消息类型' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'title', value: '消息标题' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'title',
|
||||
value: '',
|
||||
type: null,
|
||||
status: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 120 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 120 },
|
||||
{ title: '消息标题', key: 'title', minWidth: 200 },
|
||||
{ title: '消息内容', key: 'content', minWidth: 300 },
|
||||
{ title: '消息类型', key: 'type', minWidth: 120 },
|
||||
{ title: '推送状态', key: 'status', minWidth: 120 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '发送',
|
||||
type: 'success',
|
||||
click: () => {
|
||||
this.sendMessage(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '用户ID', key: 'user_id', type: 'number', required: true },
|
||||
{ title: '消息标题', key: 'title', type: 'text', required: true },
|
||||
{ title: '消息内容', key: 'content', type: 'textarea', required: true },
|
||||
{ title: '消息类型', key: 'type', type: 'select', required: true, options: [
|
||||
{ value: 'game_reminder', label: '球局提醒' },
|
||||
{ value: 'payment_notification', label: '支付通知' },
|
||||
{ value: 'system_announcement', label: '系统公告' },
|
||||
{ value: 'activity_notification', label: '活动通知' }
|
||||
]},
|
||||
{ title: '推送时间', key: 'send_time', type: 'datetime' },
|
||||
{ title: '推送状态', key: 'status', type: 'select', options: [
|
||||
{ value: 'pending', label: '待发送' },
|
||||
{ value: 'sent', label: '已发送' },
|
||||
{ value: 'failed', label: '发送失败' }
|
||||
]}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
msg_notificationsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
sendMessage(row) {
|
||||
this.$Message.info('发送功能暂未实现');
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await msg_notificationsServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
msg_notificationsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '消息通知.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'title',
|
||||
value: '',
|
||||
type: null,
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
243
demo/src/views/ntrp/ntr_questions.vue
Normal file
243
demo/src/views/ntrp/ntr_questions.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="启用状态">
|
||||
<Select v-model="gridOption.param.seachOption.is_active" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="1">启用</Option>
|
||||
<Option :value="0">禁用</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules" @on-save="handleSave"></editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import ntr_questionsServer from '@/api/ntrp/ntr_questions_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["question_title"] = [{ required: true, message: '请填写题目标题' }];
|
||||
rules["question_content"] = [{ required: true, message: '请填写题目内容' }];
|
||||
rules["question_data"] = [{ required: true, message: '请填写题目数据' }];
|
||||
rules["sort_order"] = [{ required: true, type: "number", message: '请填写排序', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: 'question_title', value: '题目标题' },
|
||||
{ key: 'question_content', value: '题目内容' }
|
||||
],
|
||||
seachTypePlaceholder: '请选择搜索类型',
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'question_title',
|
||||
value: '',
|
||||
is_active: null // 启用状态筛选
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
// 列表列配置:按照规范排序(系统→核心→详细→配置→时间→操作)
|
||||
listColumns: [
|
||||
// 系统字段
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
|
||||
// 核心业务字段
|
||||
{ title: '题目标题', key: 'question_title', minWidth: 200 },
|
||||
{ title: '题目内容', key: 'question_content', minWidth: 300 },
|
||||
|
||||
// 配置字段
|
||||
{ title: '排序', key: 'sort_order', minWidth: 80, align: 'center' },
|
||||
{
|
||||
title: '启用状态',
|
||||
key: 'is_active',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
render: (h, params) => {
|
||||
const isActive = params.row.is_active;
|
||||
return h('Tag', {
|
||||
props: {
|
||||
color: isActive ? 'success' : 'default'
|
||||
}
|
||||
}, isActive ? '启用' : '禁用');
|
||||
}
|
||||
},
|
||||
|
||||
// 时间字段
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
|
||||
// 操作列
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
// 编辑字段配置:按照规范排序和配置
|
||||
editColumns: [
|
||||
// 核心业务字段
|
||||
{
|
||||
title: '题目标题',
|
||||
key: 'question_title',
|
||||
com: 'Input',
|
||||
disabled: false,
|
||||
required: true,
|
||||
maxlength: 200,
|
||||
placeholder: '请输入题目标题'
|
||||
},
|
||||
{
|
||||
title: '题目内容',
|
||||
key: 'question_content',
|
||||
com: 'TextArea',
|
||||
rows: 4,
|
||||
disabled: false,
|
||||
required: true,
|
||||
maxlength: 1000,
|
||||
placeholder: '请输入题目内容'
|
||||
},
|
||||
{
|
||||
title: '题目数据(JSON)',
|
||||
key: 'question_data',
|
||||
com: 'TextArea',
|
||||
rows: 6,
|
||||
disabled: false,
|
||||
required: true,
|
||||
placeholder: '请输入题目选项数据(JSON格式)'
|
||||
},
|
||||
|
||||
// 配置字段
|
||||
{
|
||||
title: '排序',
|
||||
key: 'sort_order',
|
||||
com: 'InputNumber',
|
||||
data_type: 'number',
|
||||
min: 0,
|
||||
max: 9999,
|
||||
disabled: false,
|
||||
required: true,
|
||||
placeholder: '数字越小越靠前'
|
||||
},
|
||||
{
|
||||
title: '是否启用',
|
||||
key: 'is_active',
|
||||
com: 'Radio',
|
||||
disabled: false,
|
||||
required: true,
|
||||
options: [
|
||||
{ value: 1, label: '启用' },
|
||||
{ value: 0, label: '禁用' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
ntr_questionsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
async handleSave({ data, isEdit }) {
|
||||
try {
|
||||
if (isEdit) {
|
||||
await ntr_questionsServer.edit(data);
|
||||
this.$Message.success('编辑成功!');
|
||||
} else {
|
||||
await ntr_questionsServer.add(data);
|
||||
this.$Message.success('新增成功!');
|
||||
}
|
||||
this.query(this.gridOption.param.pageOption.page);
|
||||
} catch (error) {
|
||||
this.$Message.error(error.message || '操作失败!');
|
||||
}
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await ntr_questionsServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'question_title',
|
||||
value: '',
|
||||
is_active: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
exportCsv() {
|
||||
ntr_questionsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '题库管理.csv');
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
261
demo/src/views/ntrp/ntr_records.vue
Normal file
261
demo/src/views/ntrp/ntr_records.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="NTRP等级">
|
||||
<Select v-model="gridOption.param.seachOption.ntrp_level" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option :value="1">1.0</Option>
|
||||
<Option :value="2">2.0</Option>
|
||||
<Option :value="3">3.0</Option>
|
||||
<Option :value="4">4.0</Option>
|
||||
<Option :value="5">5.0</Option>
|
||||
<Option :value="6">6.0</Option>
|
||||
<Option :value="7">7.0</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv" class="ml10">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules" @on-save="handleSave"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import ntr_recordsServer from '@/api/ntrp/ntr_records_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["test_score"] = [{ required: true, type: "number", message: '请填写测试分数', trigger: 'change' }];
|
||||
rules["ntrp_level"] = [{ required: true, type: "number", message: '请填写NTRP等级', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
// 搜索类型:只包含适合文本搜索的字段
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'nickname', value: '用户昵称' }
|
||||
],
|
||||
seachTypePlaceholder: '请选择搜索类型',
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
ntrp_level: null // NTRP等级筛选
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
// 列表列配置:按照规范排序(系统→核心→关联→详细→时间→操作)
|
||||
listColumns: [
|
||||
// 系统字段
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
|
||||
// 关联字段
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 100 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 120 },
|
||||
|
||||
// 核心业务字段
|
||||
{ title: '测试分数', key: 'test_score', minWidth: 100 },
|
||||
{ title: 'NTRP等级', key: 'ntrp_level', minWidth: 100,
|
||||
render: (h, params) => {
|
||||
const level = params.row.ntrp_level;
|
||||
const colors = {
|
||||
1: 'default', 2: 'default', 3: 'blue',
|
||||
4: 'green', 5: 'orange', 6: 'red', 7: 'purple'
|
||||
};
|
||||
return h('Tag', {
|
||||
props: { color: colors[level] || 'default' }
|
||||
}, level ? `${level}.0` : '-');
|
||||
}
|
||||
},
|
||||
|
||||
// 详细信息
|
||||
{ title: '测试结果', key: 'test_result', minWidth: 200 },
|
||||
|
||||
// 时间字段
|
||||
{ title: '测试时间', key: 'test_time', minWidth: 150 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 150 },
|
||||
|
||||
// 操作列
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
// 编辑字段配置:按照规范排序和配置
|
||||
editColumns: [
|
||||
// 关联字段(只读)
|
||||
{
|
||||
title: '用户ID',
|
||||
key: 'user_id',
|
||||
com: 'InputNumber',
|
||||
data_type: 'number',
|
||||
disabled: true, // 用户ID不可修改
|
||||
required: true
|
||||
},
|
||||
|
||||
// 核心业务字段
|
||||
{
|
||||
title: '测试分数',
|
||||
key: 'test_score',
|
||||
com: 'InputNumber',
|
||||
data_type: 'number',
|
||||
min: 0,
|
||||
max: 100,
|
||||
disabled: false,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
title: 'NTRP等级',
|
||||
key: 'ntrp_level',
|
||||
com: 'Select',
|
||||
disabled: false,
|
||||
required: true,
|
||||
source: [
|
||||
{ key: 1, value: '1.0' },
|
||||
{ key: 2, value: '2.0' },
|
||||
{ key: 3, value: '3.0' },
|
||||
{ key: 4, value: '4.0' },
|
||||
{ key: 5, value: '5.0' },
|
||||
{ key: 6, value: '6.0' },
|
||||
{ key: 7, value: '7.0' }
|
||||
]
|
||||
},
|
||||
|
||||
// 时间字段
|
||||
{
|
||||
title: '测试时间',
|
||||
key: 'test_time',
|
||||
com: 'DatePicker',
|
||||
data_type: 'date',
|
||||
type: 'datetime',
|
||||
disabled: false
|
||||
},
|
||||
|
||||
// 详细信息
|
||||
{
|
||||
title: '测试结果',
|
||||
key: 'test_result',
|
||||
com: 'TextArea',
|
||||
rows: 4,
|
||||
disabled: false
|
||||
},
|
||||
|
||||
// 辅助字段
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
com: 'TextArea',
|
||||
rows: 3,
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
ntr_recordsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
async handleSave({ data, isEdit }) {
|
||||
try {
|
||||
if (isEdit) {
|
||||
await ntr_recordsServer.edit(data);
|
||||
this.$Message.success('编辑成功!');
|
||||
} else {
|
||||
await ntr_recordsServer.add(data);
|
||||
this.$Message.success('新增成功!');
|
||||
}
|
||||
this.query(this.gridOption.param.pageOption.page);
|
||||
} catch (error) {
|
||||
this.$Message.error(error.message || '操作失败!');
|
||||
}
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await ntr_recordsServer.del(row)
|
||||
this.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
ntrp_level: null
|
||||
};
|
||||
this.query(1);
|
||||
},
|
||||
exportCsv() {
|
||||
ntr_recordsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '测试记录.csv');
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
402
demo/src/views/order/frozen_funds.vue
Normal file
402
demo/src/views/order/frozen_funds.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="batchUnfreeze" :disabled="selectedRows.length === 0">
|
||||
批量解冻
|
||||
</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="冻结类型">
|
||||
<Select v-model="gridOption.param.seachOption.freezeType" style="width: 120px" clearable>
|
||||
<Option value="game_deposit">球局定金</Option>
|
||||
<Option value="withdraw">提现冻结</Option>
|
||||
<Option value="risk_control">风控冻结</Option>
|
||||
<Option value="system">系统冻结</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="冻结状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable>
|
||||
<Option value="frozen">冻结中</Option>
|
||||
<Option value="unfrozen">已解冻</Option>
|
||||
<Option value="expired">已过期</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query" @on-selection-change="onSelectionChange"></tables>
|
||||
</div>
|
||||
|
||||
<!-- 批量解冻模态框 -->
|
||||
<Modal v-model="batchUnfreezeModal" title="批量解冻资金" @on-ok="confirmBatchUnfreeze">
|
||||
<Form :model="batchUnfreezeForm" :label-width="100">
|
||||
<FormItem label="解冻原因">
|
||||
<Input v-model="batchUnfreezeForm.reason" type="textarea"
|
||||
placeholder="请输入解冻原因" :rows="3"></Input>
|
||||
</FormItem>
|
||||
<FormItem label="选中记录">
|
||||
<div class="selected-records">
|
||||
<Tag v-for="record in selectedRows" :key="record.id" closable>
|
||||
{{ record.id }} - ¥{{ (record.amount / 100).toFixed(2) }}
|
||||
</Tag>
|
||||
</div>
|
||||
</FormItem>
|
||||
<FormItem label="解冻金额">
|
||||
<div class="total-amount">
|
||||
总计:¥{{ (totalSelectedAmount / 100).toFixed(2) }}
|
||||
</div>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<!-- 解冻详情模态框 -->
|
||||
<Modal v-model="unfreezeDetailModal" title="解冻详情" width="600">
|
||||
<Form :model="unfreezeDetail" :label-width="100">
|
||||
<FormItem label="用户ID">
|
||||
<span>{{ unfreezeDetail.user_id }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="冻结金额">
|
||||
<span>¥{{ (unfreezeDetail.amount / 100).toFixed(2) }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="冻结原因">
|
||||
<span>{{ unfreezeDetail.freeze_reason }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="冻结时间">
|
||||
<span>{{ unfreezeDetail.freeze_time }}</span>
|
||||
</FormItem>
|
||||
<FormItem label="解冻原因">
|
||||
<Input v-model="unfreezeDetail.unfreeze_reason" type="textarea"
|
||||
placeholder="请输入解冻原因" :rows="3"></Input>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
<Button @click="unfreezeDetailModal = false">取消</Button>
|
||||
<Button type="primary" @click="confirmUnfreeze">确认解冻</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import frozenFundsServer from '@/api/order/frozen_funds_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedRows: [],
|
||||
batchUnfreezeModal: false,
|
||||
batchUnfreezeForm: {
|
||||
reason: ''
|
||||
},
|
||||
unfreezeDetailModal: false,
|
||||
unfreezeDetail: {},
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
freezeType: '',
|
||||
status: ''
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: {}
|
||||
},
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'transaction_id', value: '交易ID' },
|
||||
{ key: 'related_id', value: '关联ID' },
|
||||
{ key: 'freeze_reason', value: '冻结原因' }
|
||||
],
|
||||
listColumns: [
|
||||
{
|
||||
type: 'selection',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'id'
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
key: 'user_id'
|
||||
},
|
||||
{
|
||||
title: '冻结金额',
|
||||
key: 'amount',
|
||||
render: (h, params) => {
|
||||
return h('span', { style: { color: '#f56c6c' } }, `¥${(params.row.amount / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '冻结类型',
|
||||
key: 'freeze_type',
|
||||
render: (h, params) => {
|
||||
const typeMap = {
|
||||
'game_deposit': { text: '球局定金', color: 'blue' },
|
||||
'withdraw': { text: '提现冻结', color: 'orange' },
|
||||
'risk_control': { text: '风控冻结', color: 'red' },
|
||||
'system': { text: '系统冻结', color: 'gray' }
|
||||
}
|
||||
const type = typeMap[params.row.freeze_type] || { text: params.row.freeze_type, color: 'default' }
|
||||
return h('Tag', { props: { color: type.color } }, type.text)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '冻结状态',
|
||||
key: 'status',
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'frozen': { text: '冻结中', color: 'red' },
|
||||
'unfrozen': { text: '已解冻', color: 'green' },
|
||||
'expired': { text: '已过期', color: 'gray' }
|
||||
}
|
||||
const status = statusMap[params.row.status] || { text: params.row.status, color: 'default' }
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '冻结原因',
|
||||
key: 'freeze_reason',
|
||||
render: (h, params) => {
|
||||
return h('span', {
|
||||
attrs: { title: params.row.freeze_reason },
|
||||
style: {
|
||||
display: 'block',
|
||||
maxWidth: '180px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}
|
||||
}, params.row.freeze_reason)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '冻结时间',
|
||||
key: 'freeze_time'
|
||||
},
|
||||
{
|
||||
title: '解冻时间',
|
||||
key: 'unfreeze_time',
|
||||
render: (h, params) => {
|
||||
return h('span', params.row.unfreeze_time || '-')
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '解冻',
|
||||
type: 'primary',
|
||||
disabled: params.row.status !== 'frozen',
|
||||
click: () => {
|
||||
this.showUnfreezeDetail(params.row)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.viewDetail(params.row)
|
||||
}
|
||||
}
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return selected ? selected.value : '请选择搜索类型'
|
||||
},
|
||||
totalSelectedAmount() {
|
||||
return this.selectedRows.reduce((total, row) => total + row.amount, 0)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
async query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
|
||||
// 构建查询参数
|
||||
const params = {
|
||||
page: page,
|
||||
pageSize: this.gridOption.param.pageOption.pageSize,
|
||||
seachOption: this.gridOption.param.seachOption
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用API获取冻结资金列表
|
||||
const res = await frozenFundsServer.getFrozenFundsList(params)
|
||||
if (res.code === 0) {
|
||||
this.gridOption.data = res.data.rows || []
|
||||
this.gridOption.param.pageOption.total = res.data.total || 0
|
||||
} else {
|
||||
this.$Message.error(res.message || '获取冻结资金列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取冻结资金列表失败:', error)
|
||||
this.$Message.error('获取冻结资金列表失败')
|
||||
}
|
||||
},
|
||||
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
freezeType: '',
|
||||
status: ''
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
|
||||
onSelectionChange(selection) {
|
||||
this.selectedRows = selection
|
||||
},
|
||||
|
||||
batchUnfreeze() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$Message.warning('请先选择要解冻的记录')
|
||||
return
|
||||
}
|
||||
this.batchUnfreezeModal = true
|
||||
},
|
||||
|
||||
async confirmBatchUnfreeze() {
|
||||
if (!this.batchUnfreezeForm.reason) {
|
||||
this.$Message.error('请输入解冻原因')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
ids: this.selectedRows.map(row => row.id),
|
||||
reason: this.batchUnfreezeForm.reason
|
||||
}
|
||||
|
||||
const res = await frozenFundsServer.batchUnfreezeFunds(params)
|
||||
if (res.code === 0) {
|
||||
this.$Message.success(res.message || `成功解冻 ${this.selectedRows.length} 条记录`)
|
||||
this.batchUnfreezeModal = false
|
||||
this.batchUnfreezeForm = { reason: '' }
|
||||
this.selectedRows = []
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '批量解冻失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量解冻失败:', error)
|
||||
this.$Message.error('批量解冻失败')
|
||||
}
|
||||
},
|
||||
|
||||
showUnfreezeDetail(row) {
|
||||
this.unfreezeDetail = { ...row }
|
||||
this.unfreezeDetailModal = true
|
||||
},
|
||||
|
||||
async confirmUnfreeze() {
|
||||
if (!this.unfreezeDetail.unfreeze_reason) {
|
||||
this.$Message.error('请输入解冻原因')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
id: this.unfreezeDetail.id,
|
||||
unfreeze_reason: this.unfreezeDetail.unfreeze_reason
|
||||
}
|
||||
|
||||
const res = await frozenFundsServer.unfreezeFund(params)
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('资金解冻成功')
|
||||
this.unfreezeDetailModal = false
|
||||
this.unfreezeDetail = {}
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '解冻失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解冻失败:', error)
|
||||
this.$Message.error('解冻失败')
|
||||
}
|
||||
},
|
||||
|
||||
async viewDetail(row) {
|
||||
try {
|
||||
const res = await frozenFundsServer.getFrozenFundDetail({ id: row.id })
|
||||
if (res.code === 0) {
|
||||
const detail = res.data
|
||||
this.$Modal.info({
|
||||
title: '冻结详情',
|
||||
content: `
|
||||
<div>
|
||||
<p><strong>冻结ID:</strong> ${detail.id}</p>
|
||||
<p><strong>用户ID:</strong> ${detail.user_id}</p>
|
||||
<p><strong>用户昵称:</strong> ${detail.user_nickname || '未知'}</p>
|
||||
<p><strong>冻结金额:</strong> ¥${(detail.amount / 100).toFixed(2)}</p>
|
||||
<p><strong>冻结类型:</strong> ${detail.freeze_type}</p>
|
||||
<p><strong>冻结状态:</strong> ${detail.status}</p>
|
||||
<p><strong>冻结原因:</strong> ${detail.freeze_reason}</p>
|
||||
<p><strong>冻结时间:</strong> ${detail.freeze_time}</p>
|
||||
${detail.unfreeze_time ? `<p><strong>解冻时间:</strong> ${detail.unfreeze_time}</p>` : ''}
|
||||
</div>
|
||||
`
|
||||
})
|
||||
} else {
|
||||
this.$Message.error(res.message || '获取详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error)
|
||||
this.$Message.error('获取详情失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.selected-records {
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dcdee2;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.total-amount {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
592
demo/src/views/order/pay_orders.vue
Normal file
592
demo/src/views/order/pay_orders.vue
Normal file
@@ -0,0 +1,592 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddModal">新增订单</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="订单状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable>
|
||||
<Option :value="0">待支付</Option>
|
||||
<Option :value="1">已支付</Option>
|
||||
<Option :value="2">已结束</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="订单类型">
|
||||
<Select v-model="gridOption.param.seachOption.order_type" style="width: 120px" clearable>
|
||||
<Option value="game_deposit">球局定金</Option>
|
||||
<Option value="venue_booking">场地预订</Option>
|
||||
<Option value="recharge">充值</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="default" @click="exportOrders">导出订单</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑订单模态框 -->
|
||||
<Modal v-model="orderModal" :title="orderModalTitle" width="800" @on-ok="saveOrder">
|
||||
<Form :model="orderForm" :rules="orderRules" ref="orderForm" :label-width="100">
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="订单号" prop="order_no">
|
||||
<Input v-model="orderForm.order_no" placeholder="请输入订单号"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="用户ID" prop="user_id">
|
||||
<Input v-model="orderForm.user_id" placeholder="请输入用户ID"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="业务类型" prop="order_type">
|
||||
<Select v-model="orderForm.order_type" placeholder="请选择业务类型">
|
||||
<Option value="game_deposit">球局定金</Option>
|
||||
<Option value="venue_booking">场地预订</Option>
|
||||
<Option value="recharge">充值</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="业务ID" prop="business_id">
|
||||
<Input v-model="orderForm.business_id" placeholder="请输入业务ID"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="订单金额" prop="amount">
|
||||
<Input v-model="orderForm.amount" placeholder="请输入金额(分)" type="number"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="订单状态" prop="order_status">
|
||||
<Select v-model="orderForm.order_status" placeholder="请选择订单状态">
|
||||
<Option :value="0">待支付</Option>
|
||||
<Option :value="1">已支付</Option>
|
||||
<Option :value="2">已结束</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="支付方式" prop="payment_method">
|
||||
<Select v-model="orderForm.payment_method" placeholder="请选择支付方式">
|
||||
<Option value="wechat">微信支付</Option>
|
||||
<Option value="alipay">支付宝</Option>
|
||||
<Option value="balance">余额支付</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="退款状态" prop="refund_status">
|
||||
<Select v-model="orderForm.refund_status" placeholder="请选择退款状态">
|
||||
<Option :value="0">无退款</Option>
|
||||
<Option :value="1">退款中</Option>
|
||||
<Option :value="2">已退款</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<FormItem label="订单描述" prop="description">
|
||||
<Input v-model="orderForm.description" type="textarea"
|
||||
placeholder="请输入订单描述" :rows="3"></Input>
|
||||
</FormItem>
|
||||
<FormItem label="备注" prop="remark">
|
||||
<Input v-model="orderForm.remark" type="textarea"
|
||||
placeholder="请输入备注" :rows="2"></Input>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<!-- 订单详情模态框 -->
|
||||
<Modal v-model="detailModal" title="订单详情" width="800">
|
||||
<div v-if="orderDetail">
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>订单ID:</label>
|
||||
<span>{{ orderDetail.id }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>订单号:</label>
|
||||
<span>{{ orderDetail.order_no }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>用户ID:</label>
|
||||
<span>{{ orderDetail.user_id }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>业务类型:</label>
|
||||
<Tag :color="getBusinessTypeColor(orderDetail.order_type)">
|
||||
{{ getBusinessTypeText(orderDetail.order_type) }}
|
||||
</Tag>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>订单金额:</label>
|
||||
<span class="amount-text">¥{{ (orderDetail.amount / 100).toFixed(2) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>订单状态:</label>
|
||||
<Tag :color="getStatusColor(orderDetail.order_status)">
|
||||
{{ getStatusText(orderDetail.order_status) }}
|
||||
</Tag>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>支付方式:</label>
|
||||
<span>{{ getPaymentMethodText(orderDetail.payment_method) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>退款状态:</label>
|
||||
<Tag :color="getRefundStatusColor(orderDetail.refund_status)">
|
||||
{{ getRefundStatusText(orderDetail.refund_status) }}
|
||||
</Tag>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>创建时间:</label>
|
||||
<span>{{ orderDetail.create_time }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>支付时间:</label>
|
||||
<span>{{ orderDetail.pay_time || '-' }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" v-if="orderDetail.description">
|
||||
<Col span="24">
|
||||
<div class="detail-item">
|
||||
<label>订单描述:</label>
|
||||
<span>{{ orderDetail.description }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" v-if="orderDetail.remark">
|
||||
<Col span="24">
|
||||
<div class="detail-item">
|
||||
<label>备注:</label>
|
||||
<span>{{ orderDetail.remark }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import paymentOrdersServer from '@/api/order/payment_orders_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
orderModal: false,
|
||||
orderModalTitle: '',
|
||||
orderForm: {
|
||||
order_no: '',
|
||||
user_id: '',
|
||||
order_type: '',
|
||||
business_id: '',
|
||||
amount: '',
|
||||
order_status: 0,
|
||||
payment_method: '',
|
||||
refund_status: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
},
|
||||
orderRules: {
|
||||
order_no: [{ required: true, message: '请输入订单号', trigger: 'blur' }],
|
||||
user_id: [{ required: true, message: '请输入用户ID', trigger: 'blur' }],
|
||||
order_type: [{ required: true, message: '请选择业务类型', trigger: 'change' }],
|
||||
amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }],
|
||||
order_status: [{ required: true, message: '请选择订单状态', trigger: 'change' }]
|
||||
},
|
||||
detailModal: false,
|
||||
orderDetail: null,
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'order_no',
|
||||
value: '',
|
||||
status: '',
|
||||
order_type: ''
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: {}
|
||||
},
|
||||
seachTypes: [
|
||||
{ key: 'order_no', value: '订单号' },
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'business_id', value: '业务ID' },
|
||||
{ key: 'description', value: '订单描述' }
|
||||
],
|
||||
listColumns: [
|
||||
{
|
||||
title: '订单ID',
|
||||
key: 'id',
|
||||
minWidth: 80
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
key: 'order_no',
|
||||
minWidth: 180
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
key: 'user_id',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
title: '业务类型',
|
||||
key: 'order_type',
|
||||
minWidth: 120,
|
||||
render: (h, params) => {
|
||||
return h('Tag', { props: { color: this.getBusinessTypeColor(params.row.order_type) } },
|
||||
this.getBusinessTypeText(params.row.order_type))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '业务ID',
|
||||
key: 'business_id',
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
title: '订单金额',
|
||||
key: 'amount',
|
||||
minWidth: 120
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
key: 'order_status',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', { props: { color: this.getStatusColor(params.row.order_status) } },
|
||||
this.getStatusText(params.row.order_status))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '支付方式',
|
||||
key: 'payment_method',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('span', this.getPaymentMethodText(params.row.payment_method))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '退款状态',
|
||||
key: 'refund_status',
|
||||
minWidth: 100,
|
||||
render: (h, params) => {
|
||||
return h('Tag', { props: { color: this.getRefundStatusColor(params.row.refund_status) } },
|
||||
this.getRefundStatusText(params.row.refund_status))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time',
|
||||
minWidth: 160
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 250,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.editOrder(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.viewDetail(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '取消',
|
||||
type: 'warning',
|
||||
click: () => {
|
||||
this.cancelOrder(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return selected ? selected.value : '请选择搜索类型'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
if (page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
}
|
||||
|
||||
paymentOrdersServer.getPaymentOrdersList(this.gridOption.param).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.gridOption.data = res.data.rows || res.data
|
||||
this.gridOption.param.pageOption.total = res.data.count || res.data.total || 0
|
||||
} else {
|
||||
this.$Message.error(res.message || '查询失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$Message.error('查询失败: ' + error.message)
|
||||
})
|
||||
},
|
||||
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'order_no',
|
||||
value: '',
|
||||
status: '',
|
||||
order_type: ''
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
|
||||
showAddModal() {
|
||||
this.orderForm = {
|
||||
order_no: '',
|
||||
user_id: '',
|
||||
order_type: '',
|
||||
business_id: '',
|
||||
amount: '',
|
||||
order_status: 0,
|
||||
payment_method: '',
|
||||
refund_status: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
}
|
||||
this.orderModalTitle = '新增订单'
|
||||
this.orderModal = true
|
||||
},
|
||||
|
||||
editOrder(row) {
|
||||
this.orderForm = { ...row }
|
||||
this.orderModalTitle = '编辑订单'
|
||||
this.orderModal = true
|
||||
},
|
||||
|
||||
saveOrder() {
|
||||
this.$refs.orderForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.orderForm.id) {
|
||||
// 编辑订单
|
||||
paymentOrdersServer.updatePaymentOrder(this.orderForm).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('订单更新成功')
|
||||
this.orderModal = false
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '更新失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$Message.error('更新失败: ' + error.message)
|
||||
})
|
||||
} else {
|
||||
// 新增订单
|
||||
paymentOrdersServer.addPaymentOrder(this.orderForm).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('订单创建成功')
|
||||
this.orderModal = false
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '创建失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$Message.error('创建失败: ' + error.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
cancelOrder(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '取消订单',
|
||||
content: `确定要取消订单 ${row.order_no} 吗?`,
|
||||
onOk: async () => {
|
||||
paymentOrdersServer.cancelPaymentOrder(row.id).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('订单已取消')
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '取消失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$Message.error('取消失败: ' + error.message)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
viewDetail(row) {
|
||||
this.orderDetail = row
|
||||
this.detailModal = true
|
||||
},
|
||||
|
||||
exportOrders() {
|
||||
paymentOrdersServer.exportPaymentOrders(this.gridOption.param).then(res => {
|
||||
if (res.code === 0) {
|
||||
funTool.downloadFile(res, '订单列表.csv')
|
||||
} else {
|
||||
this.$Message.error(res.message || '导出失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$Message.error('导出失败: ' + error.message)
|
||||
})
|
||||
},
|
||||
|
||||
getBusinessTypeColor(type) {
|
||||
const colorMap = {
|
||||
'game_deposit': 'blue',
|
||||
'venue_booking': 'green',
|
||||
'recharge': 'orange'
|
||||
}
|
||||
return colorMap[type] || 'default'
|
||||
},
|
||||
|
||||
getBusinessTypeText(type) {
|
||||
const textMap = {
|
||||
'game_deposit': '球局定金',
|
||||
'venue_booking': '场地预订',
|
||||
'recharge': '充值'
|
||||
}
|
||||
return textMap[type] || type
|
||||
},
|
||||
|
||||
getStatusColor(status) {
|
||||
const colorMap = {
|
||||
0: 'orange', // 待支付
|
||||
1: 'green', // 已支付
|
||||
2: 'blue' // 已结束
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
},
|
||||
|
||||
getStatusText(status) {
|
||||
const textMap = {
|
||||
0: '待支付',
|
||||
1: '已支付',
|
||||
2: '已结束'
|
||||
}
|
||||
return textMap[status] || status
|
||||
},
|
||||
|
||||
getPaymentMethodText(method) {
|
||||
const textMap = {
|
||||
'wechat': '微信支付',
|
||||
'alipay': '支付宝',
|
||||
'balance': '余额支付'
|
||||
}
|
||||
return textMap[method] || method
|
||||
},
|
||||
|
||||
getRefundStatusColor(status) {
|
||||
const colorMap = {
|
||||
0: 'default', // 无退款
|
||||
1: 'orange', // 退款中
|
||||
2: 'green' // 已退款
|
||||
}
|
||||
return colorMap[status] || 'default'
|
||||
},
|
||||
|
||||
getRefundStatusText(status) {
|
||||
const textMap = {
|
||||
0: '无退款',
|
||||
1: '退款中',
|
||||
2: '已退款'
|
||||
}
|
||||
return textMap[status] || status
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.detail-item label {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
234
demo/src/views/order/transfer_details.vue
Normal file
234
demo/src/views/order/transfer_details.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="明细状态">
|
||||
<Select v-model="gridOption.param.seachOption.detail_status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="WAIT_USER_CONFIRM">处理中</Option>
|
||||
<Option value="SUCCESS">成功</Option>
|
||||
<Option value="FAIL">失败</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import transfer_detailsServer from '@/api/order/transfer_details_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["out_bill_no"] = [{ required: true, message: '请填写商户明细号', trigger: 'blur' }];
|
||||
rules["openid"] = [{ required: true, message: '请填写收款用户openid', trigger: 'blur' }];
|
||||
rules["transfer_amount"] = [{ required: true, type: "number", message: '请填写转账金额', trigger: 'change' }];
|
||||
rules["transfer_remark"] = [{ required: true, message: '请填写转账备注', trigger: 'blur' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'out_bill_no', value: '商户明细号' },
|
||||
{ key: 'transfer_bill_no', value: '微信交易号' },
|
||||
{ key: 'openid', value: '收款用户openid' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'out_bill_no',
|
||||
value: '',
|
||||
detail_status: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id' },
|
||||
{ title: '商户明细号', key: 'out_bill_no' },
|
||||
{ title: '微信交易号', key: 'transfer_bill_no' },
|
||||
{ title: '收款用户openid', key: 'openid' },
|
||||
{ title: '收款用户昵称', key: 'user_nickname' },
|
||||
{
|
||||
title: '转账金额',
|
||||
key: 'transfer_amount',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
return h('span', { style: { color: '#67c23a', fontWeight: 'bold' } },
|
||||
`¥${(params.row.transfer_amount / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '转账备注',
|
||||
key: 'transfer_remark',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
return h('span', {
|
||||
attrs: { title: params.row.transfer_remark },
|
||||
style: {
|
||||
display: 'block',
|
||||
maxWidth: '150px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}
|
||||
}, params.row.transfer_remark)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '明细状态',
|
||||
key: 'detail_status',
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'WAIT_USER_CONFIRM': { text: '处理中', color: 'orange' },
|
||||
'SUCCESS': { text: '成功', color: 'green' },
|
||||
'FAIL': { text: '失败', color: 'red' }
|
||||
}
|
||||
const status = statusMap[params.row.detail_status] || { text: params.row.detail_status, color: 'default' }
|
||||
return h('Tag', { props: { color: status.color } }, status.text)
|
||||
}
|
||||
},
|
||||
{ title: '失败原因', key: 'fail_reason' },
|
||||
{ title: '创建时间', key: 'create_time' },
|
||||
{ title: '更新时间', key: 'update_time' },
|
||||
{ title: '完成时间', key: 'finish_time' },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '商户明细号', key: 'out_bill_no', type: 'text', required: true },
|
||||
{ title: '微信交易号', key: 'transfer_bill_no', type: 'text' },
|
||||
{ title: '收款用户openid', key: 'openid', type: 'text', required: true },
|
||||
{ title: '收款用户姓名', key: 'user_name', type: 'text' },
|
||||
{ title: '转账金额(分)', key: 'transfer_amount', type: 'number', required: true },
|
||||
{ title: '转账备注', key: 'transfer_remark', type: 'textarea', required: true },
|
||||
{ title: '明细状态', key: 'detail_status', type: 'select', options: [
|
||||
{ value: 'WAIT_USER_CONFIRM', label: '处理中' },
|
||||
{ value: 'SUCCESS', label: '成功' },
|
||||
{ value: 'FAIL', label: '失败' }
|
||||
]},
|
||||
{ title: '失败原因', key: 'fail_reason', type: 'textarea' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
async query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
try {
|
||||
const res = await transfer_detailsServer.page(this.gridOption.param);
|
||||
if (res.code === 0) {
|
||||
this.gridOption.data = res.data.rows || [];
|
||||
this.gridOption.param.pageOption.total = res.data.count || 0;
|
||||
} else {
|
||||
this.$Message.error(res.message || '获取转账详情失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取转账详情失败:', error);
|
||||
this.$Message.error('获取转账详情失败');
|
||||
}
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
try {
|
||||
const res = await transfer_detailsServer.del(row);
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('删除成功!');
|
||||
this.query(1);
|
||||
} else {
|
||||
this.$Message.error(res.message || '删除失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
this.$Message.error('删除失败');
|
||||
}
|
||||
})
|
||||
},
|
||||
async exportCsv() {
|
||||
try {
|
||||
const res = await transfer_detailsServer.exportCsv(this.gridOption.param);
|
||||
if (res.code === 0) {
|
||||
funTool.downloadFile(res.data, '转账详情.csv');
|
||||
} else {
|
||||
this.$Message.error(res.message || '导出失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error);
|
||||
this.$Message.error('导出失败');
|
||||
}
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'out_bill_no',
|
||||
value: '',
|
||||
detail_status: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
return this.gridOption.columns.filter(p => p.is_show_edit === 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
286
demo/src/views/order/wallet_transactions.vue
Normal file
286
demo/src/views/order/wallet_transactions.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="交易类型">
|
||||
<Select v-model="gridOption.param.seachOption.transaction_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="recharge">充值</Option>
|
||||
<Option value="withdraw">提现</Option>
|
||||
<Option value="game_deposit">球局定金</Option>
|
||||
<Option value="game_refund">球局退款</Option>
|
||||
<Option value="transfer">转账</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="交易状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="pending">待处理</Option>
|
||||
<Option value="success">成功</Option>
|
||||
<Option value="failed">失败</Option>
|
||||
<Option value="cancelled">已取消</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"></editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import walletTransactionsServer from '@/api/ball/wallet_transactions_server.js'
|
||||
|
||||
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["status"] = [{ required: true, message: '请选择状态' }];
|
||||
rules["remark"] = [{ message: '请填写备注' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: "user_id", value: "用户ID" },
|
||||
{ key: "nickname", value: "用户昵称" },
|
||||
{ key: "transaction_no", value: "交易流水号" }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: "user_id",
|
||||
value: "",
|
||||
transaction_type: null,
|
||||
status: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
rules,
|
||||
columns: [
|
||||
{ key: 'id', title: 'ID', minWidth: 80, is_show_edit: 0 },
|
||||
{
|
||||
key: "user_id",
|
||||
title: "用户",
|
||||
disabled: true,
|
||||
is_show_edit: 0,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
render: (h, params) => {
|
||||
const userInfo = params.row.user_info;
|
||||
if (userInfo && userInfo.nickname) {
|
||||
return h('div', [
|
||||
h('img', {
|
||||
attrs: {
|
||||
src: userInfo.avatar_url || '/default-avatar.png',
|
||||
alt: userInfo.nickname,
|
||||
title: userInfo.nickname
|
||||
},
|
||||
style: {
|
||||
minWidth: 80,
|
||||
height: '24px',
|
||||
borderRadius: '50%',
|
||||
marginRight: '8px',
|
||||
verticalAlign: 'middle'
|
||||
}
|
||||
}),
|
||||
h('span', { style: { verticalAlign: 'middle' } }, userInfo.nickname)
|
||||
]);
|
||||
}
|
||||
return h('span', `用户ID: ${params.row.user_id}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "transaction_type",
|
||||
title: "交易类型",
|
||||
disabled: true,
|
||||
is_show_edit: 0,
|
||||
is_show_list: 1,
|
||||
com: "Select",
|
||||
render: (h, params) => {
|
||||
const freezeAction = params.row.freeze_action;
|
||||
let typeText = '';
|
||||
let color = 'default';
|
||||
|
||||
if (params.row.transaction_type === 'income') {
|
||||
if (freezeAction === 'freeze') {
|
||||
typeText = '收入(冻结)';
|
||||
color = 'orange';
|
||||
} else if (freezeAction === 'unfreeze') {
|
||||
typeText = '收入(解冻)';
|
||||
color = 'green';
|
||||
} else {
|
||||
typeText = '收入';
|
||||
color = 'green';
|
||||
}
|
||||
} else if (params.row.transaction_type === 'expense') {
|
||||
if (freezeAction === 'unfreeze') {
|
||||
typeText = '支出(解冻)';
|
||||
color = 'purple';
|
||||
} else {
|
||||
typeText = '支出';
|
||||
color = 'red';
|
||||
}
|
||||
} else {
|
||||
typeText = params.row.transaction_type || '未知';
|
||||
}
|
||||
|
||||
return h('Tag', { props: { color: color } }, typeText);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "amount",
|
||||
title: "交易金额",
|
||||
disabled: true,
|
||||
is_show_edit: 0,
|
||||
is_show_list: 1,
|
||||
com: "InputNumber",
|
||||
render: (h, params) => {
|
||||
const amount = parseFloat(params.row.amount || 0);
|
||||
return h('span', {
|
||||
style: {
|
||||
color: amount > 0 ? '#19be6b' : '#ed4014',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}, `${amount > 0 ? '+' : ''}¥${amount.toFixed(2)}`);
|
||||
}
|
||||
},
|
||||
|
||||
{ key: "description", title: "交易描述", disabled: true, is_show_edit: 0, is_show_list: 1, com: "Input" },
|
||||
{
|
||||
key: "related_id",
|
||||
title: "关联ID",
|
||||
disabled: true,
|
||||
is_show_edit: 0,
|
||||
is_show_list: 1,
|
||||
com: "Input",
|
||||
render: (h, params) => {
|
||||
const relatedId = params.row.related_id;
|
||||
if (relatedId) {
|
||||
return h('span', relatedId);
|
||||
}
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '-');
|
||||
}
|
||||
},
|
||||
{ key: "create_time", title: "交易时间", disabled: true, is_show_edit: 0, is_show_list: 1, com: "DatePicker" }
|
||||
],
|
||||
data: [],
|
||||
seachTypes: this.seachTypes,
|
||||
apiServer: walletTransactionsServer,
|
||||
showAdd: false,
|
||||
showEdit: true,
|
||||
showDel: false,
|
||||
showExport: true
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '用户', key: 'user_id', minWidth: 120 },
|
||||
{ title: '交易类型', key: 'transaction_type', minWidth: 120 },
|
||||
{ title: '交易金额', key: 'amount', minWidth: 120 },
|
||||
{ title: '交易描述', key: 'description', minWidth: 200 },
|
||||
{ title: '关联ID', key: 'related_id', minWidth: 120 },
|
||||
{ title: '交易时间', key: 'create_time', minWidth: 150 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
}
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '状态', key: 'status', type: 'select', required: true,
|
||||
source: [
|
||||
{ key: 'pending', value: '待处理' },
|
||||
{ key: 'completed', value: '已完成' },
|
||||
{ key: 'failed', value: '失败' }
|
||||
]
|
||||
},
|
||||
{ title: '备注', key: 'remark', type: 'textarea' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
walletTransactionsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
async showAddWarp() {
|
||||
// 交易记录不支持手动新增
|
||||
this.$Message.warning('交易记录由系统自动生成,无需手动添加');
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
async showDelWarp(row) {
|
||||
// 交易记录不支持删除
|
||||
this.$Message.warning('交易记录不支持删除操作');
|
||||
},
|
||||
exportCsv() {
|
||||
walletTransactionsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '交易记录.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
transaction_type: null,
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
},
|
||||
editColumns() {
|
||||
let editTempColumns = this.gridOption.columns.filter(p => p.is_show_edit === 1)
|
||||
return editTempColumns
|
||||
},
|
||||
listColumns() {
|
||||
let listTempColumns = this.gridOption.columns.filter(p => p.is_show_list === 1)
|
||||
return listTempColumns
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
513
demo/src/views/order/wch_wallets.vue
Normal file
513
demo/src/views/order/wch_wallets.vue
Normal file
@@ -0,0 +1,513 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddModal">新增钱包</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="余额范围">
|
||||
<InputNumber v-model="gridOption.param.seachOption.minBalance" placeholder="最小余额"
|
||||
style="width: 100px"></InputNumber>
|
||||
<span class="ml5 mr5">-</span>
|
||||
<InputNumber v-model="gridOption.param.seachOption.maxBalance" placeholder="最大余额"
|
||||
style="width: 100px"></InputNumber>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="default" @click="exportWallets">导出钱包</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑钱包模态框 -->
|
||||
<Modal v-model="walletModal" :title="walletModalTitle" width="600" @on-ok="saveWallet">
|
||||
<Form :model="walletForm" :rules="walletRules" ref="walletForm" :label-width="100">
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="用户ID" prop="user_id">
|
||||
<Input v-model="walletForm.user_id" placeholder="请输入用户ID"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="可用余额" prop="balance">
|
||||
<Input v-model="walletForm.balance" placeholder="请输入可用余额(分)" type="number"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<FormItem label="冻结余额" prop="frozen_balance">
|
||||
<Input v-model="walletForm.frozen_balance" placeholder="请输入冻结余额(分)" type="number"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<FormItem label="总收入" prop="total_income">
|
||||
<Input v-model="walletForm.total_income" placeholder="请输入总收入(分)" type="number"></Input>
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<FormItem label="钱包密码" prop="password">
|
||||
<Input v-model="walletForm.password" type="password" placeholder="请输入钱包密码"></Input>
|
||||
</FormItem>
|
||||
<FormItem label="备注" prop="remark">
|
||||
<Input v-model="walletForm.remark" type="textarea" placeholder="请输入备注" :rows="3"></Input>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<!-- 钱包详情模态框 -->
|
||||
<Modal v-model="detailModal" title="钱包详情" width="800">
|
||||
<div v-if="walletDetail">
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>钱包ID:</label>
|
||||
<span>{{ walletDetail.id }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>用户ID:</label>
|
||||
<span>{{ walletDetail.user_id }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>总余额:</label>
|
||||
<span class="amount-text">¥{{ (walletDetail.total_balance / 100).toFixed(2) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>可用余额:</label>
|
||||
<span class="amount-text available">¥{{ (walletDetail.balance / 100).toFixed(2) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>冻结余额:</label>
|
||||
<span class="amount-text frozen">¥{{ (walletDetail.frozen_balance / 100).toFixed(2) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>总收入:</label>
|
||||
<span class="amount-text income">¥{{ (walletDetail.total_income / 100).toFixed(2) }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16">
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>创建时间:</label>
|
||||
<span>{{ walletDetail.create_time }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="12">
|
||||
<div class="detail-item">
|
||||
<label>更新时间:</label>
|
||||
<span>{{ walletDetail.last_modify_time }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row :gutter="16" v-if="walletDetail.remark">
|
||||
<Col span="24">
|
||||
<div class="detail-item">
|
||||
<label>备注:</label>
|
||||
<span>{{ walletDetail.remark }}</span>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!-- 钱包操作模态框 -->
|
||||
<Modal v-model="operationModal" title="钱包操作" width="500" @on-ok="confirmOperation">
|
||||
<Form :model="operationForm" :rules="operationRules" ref="operationForm" :label-width="100">
|
||||
<FormItem label="操作类型" prop="type">
|
||||
<RadioGroup v-model="operationForm.type">
|
||||
<Radio label="recharge">充值</Radio>
|
||||
<Radio label="withdraw">提现</Radio>
|
||||
<Radio label="freeze">冻结</Radio>
|
||||
<Radio label="unfreeze">解冻</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="操作金额" prop="amount">
|
||||
<Input v-model="operationForm.amount" placeholder="请输入金额(分)" type="number"></Input>
|
||||
</FormItem>
|
||||
<FormItem label="操作原因" prop="reason">
|
||||
<Input v-model="operationForm.reason" type="textarea" placeholder="请输入操作原因" :rows="3"></Input>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import wchWalletsServer from '@/api/order/wch_wallets_server.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
walletModal: false,
|
||||
walletModalTitle: '',
|
||||
walletForm: {
|
||||
user_id: '',
|
||||
balance: '',
|
||||
frozen_balance: '',
|
||||
total_income: '',
|
||||
password: '',
|
||||
remark: ''
|
||||
},
|
||||
walletRules: {
|
||||
user_id: [{ required: true, message: '请输入用户ID', trigger: 'blur' }],
|
||||
balance: [{ required: true, message: '请输入可用余额', trigger: 'blur' }],
|
||||
frozen_balance: [{ required: true, message: '请输入冻结余额', trigger: 'blur' }]
|
||||
},
|
||||
detailModal: false,
|
||||
walletDetail: null,
|
||||
operationModal: false,
|
||||
operationForm: {
|
||||
type: '',
|
||||
amount: '',
|
||||
reason: ''
|
||||
},
|
||||
operationRules: {
|
||||
type: [{ required: true, message: '请选择操作类型', trigger: 'change' }],
|
||||
amount: [{ required: true, message: '请输入操作金额', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '请输入操作原因', trigger: 'blur' }]
|
||||
},
|
||||
currentWallet: null,
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
minBalance: null,
|
||||
maxBalance: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: {}
|
||||
},
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'id', value: '钱包ID' },
|
||||
{ key: 'remark', value: '备注' }
|
||||
],
|
||||
listColumns: [
|
||||
{
|
||||
title: '钱包ID',
|
||||
key: 'id'
|
||||
},
|
||||
{
|
||||
title: '用户ID',
|
||||
key: 'user_id'
|
||||
},
|
||||
{
|
||||
title: '可用余额',
|
||||
key: 'balance',
|
||||
render: (h, params) => {
|
||||
return h('span', { style: { color: '#67c23a', fontWeight: 'bold' } },
|
||||
`¥${(params.row.balance / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '冻结余额',
|
||||
key: 'frozen_balance',
|
||||
render: (h, params) => {
|
||||
return h('span', { style: { color: '#e6a23c', fontWeight: 'bold' } },
|
||||
`¥${(params.row.frozen_balance / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '总余额',
|
||||
key: 'total_balance',
|
||||
render: (h, params) => {
|
||||
const total = params.row.balance + params.row.frozen_balance
|
||||
return h('span', { style: { color: '#409eff', fontWeight: 'bold' } },
|
||||
`¥${(total / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '总收入',
|
||||
key: 'total_income',
|
||||
render: (h, params) => {
|
||||
return h('span', { style: { color: '#67c23a' } },
|
||||
`¥${(params.row.total_income / 100).toFixed(2)}`)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'create_time'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.editWallet(params.row)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '详情',
|
||||
type: 'info',
|
||||
click: () => {
|
||||
this.viewDetail(params.row)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
type: 'warning',
|
||||
click: () => {
|
||||
this.walletOperation(params.row)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '交易记录',
|
||||
type: 'success',
|
||||
click: () => {
|
||||
this.viewTransactions(params.row)
|
||||
}
|
||||
}
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key)
|
||||
return selected ? selected.value : '请选择搜索类型'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
async query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
|
||||
// 构建查询参数
|
||||
const params = {
|
||||
page: page,
|
||||
pageSize: this.gridOption.param.pageOption.pageSize,
|
||||
seachOption: this.gridOption.param.seachOption
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用API获取钱包列表
|
||||
const res = await wchWalletsServer.getWalletsList(params)
|
||||
if (res.code === 0) {
|
||||
this.gridOption.data = res.data.rows || []
|
||||
this.gridOption.param.pageOption.total = res.data.count || 0
|
||||
} else {
|
||||
this.$Message.error(res.message || '获取钱包列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取钱包列表失败:', error)
|
||||
this.$Message.error('获取钱包列表失败')
|
||||
}
|
||||
},
|
||||
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
minBalance: null,
|
||||
maxBalance: null
|
||||
}
|
||||
this.query(1)
|
||||
},
|
||||
|
||||
showAddModal() {
|
||||
this.walletForm = {
|
||||
user_id: '',
|
||||
balance: '',
|
||||
frozen_balance: '',
|
||||
total_income: '',
|
||||
password: '',
|
||||
remark: ''
|
||||
}
|
||||
this.walletModalTitle = '新增钱包'
|
||||
this.walletModal = true
|
||||
},
|
||||
|
||||
editWallet(row) {
|
||||
this.walletForm = { ...row }
|
||||
this.walletModalTitle = '编辑钱包'
|
||||
this.walletModal = true
|
||||
},
|
||||
|
||||
saveWallet() {
|
||||
this.$refs.walletForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const params = { ...this.walletForm }
|
||||
|
||||
try {
|
||||
// 调用API保存钱包
|
||||
const apiCall = params.id ? wchWalletsServer.updateWallet(params) : wchWalletsServer.addWallet(params)
|
||||
const res = await apiCall
|
||||
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('钱包保存成功')
|
||||
this.walletModal = false
|
||||
this.query(1)
|
||||
} else {
|
||||
this.$Message.error(res.message || '保存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存钱包失败:', error)
|
||||
this.$Message.error('保存失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async viewDetail(row) {
|
||||
try {
|
||||
// 调用API获取钱包详情
|
||||
const res = await wchWalletsServer.getWalletDetail({ id: row.id })
|
||||
if (res.code === 0) {
|
||||
this.walletDetail = res.data.wallet
|
||||
this.detailModal = true
|
||||
} else {
|
||||
this.$Message.error(res.message || '获取钱包详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取钱包详情失败:', error)
|
||||
this.$Message.error('获取钱包详情失败')
|
||||
}
|
||||
},
|
||||
|
||||
walletOperation(row) {
|
||||
this.currentWallet = row
|
||||
this.operationForm = {
|
||||
type: '',
|
||||
amount: '',
|
||||
reason: ''
|
||||
}
|
||||
this.operationModal = true
|
||||
},
|
||||
|
||||
confirmOperation() {
|
||||
this.$refs.operationForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$Message.success('钱包操作完成')
|
||||
this.operationModal = false
|
||||
this.operationForm = { type: '', amount: '', reason: '' }
|
||||
this.currentWallet = null
|
||||
this.query(1)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
viewTransactions(row) {
|
||||
this.$Message.info('跳转到交易记录页面')
|
||||
// 这里可以跳转到交易记录页面,传递钱包ID参数
|
||||
},
|
||||
|
||||
async exportWallets() {
|
||||
// 构建导出参数
|
||||
const params = {
|
||||
seachOption: this.gridOption.param.seachOption
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用API导出钱包数据
|
||||
const res = await wchWalletsServer.exportWallets(params)
|
||||
if (res.code === 0) {
|
||||
this.$Message.success('导出成功')
|
||||
// 这里可以添加下载文件的逻辑
|
||||
} else {
|
||||
this.$Message.error(res.message || '导出失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导出钱包数据失败:', error)
|
||||
this.$Message.error('导出失败')
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.detail-item label {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.amount-text.available {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.amount-text.frozen {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.amount-text.income {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.amount-text.expense {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.mr5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
345
demo/src/views/statistics/resources.vue
Normal file
345
demo/src/views/statistics/resources.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddModal">新增资源</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="(item, index) in seachTypes" :key="index" :value="item.key">{{ item.title }}
|
||||
</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="资源类型">
|
||||
<Select v-model="gridOption.param.seachOption.resource_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="image">图片</Option>
|
||||
<Option value="video">视频</Option>
|
||||
<Option value="audio">音频</Option>
|
||||
<Option value="document">文档</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="状态">
|
||||
<Select v-model="gridOption.param.seachOption.status" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="active">正常</Option>
|
||||
<Option value="inactive">禁用</Option>
|
||||
<Option value="deleted">已删除</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<Modal v-model="editWarp" :title="editTitle" @on-ok="save" @on-cancel="cancel" width="600">
|
||||
<Form ref="formValidate" :model="formValidate" :rules="ruleValidate" :label-width="100">
|
||||
<FormItem v-for="(item, index) in editColumns" :key="index" :label="item.title" :prop="item.key">
|
||||
<Input v-if="item.type === 'text'" v-model="formValidate[item.key]"
|
||||
:placeholder="'请输入' + item.title" />
|
||||
<Input v-else-if="item.type === 'textarea'" v-model="formValidate[item.key]" type="textarea"
|
||||
:placeholder="'请输入' + item.title" :rows="4" />
|
||||
<Input v-else-if="item.type === 'number'" v-model="formValidate[item.key]" type="number"
|
||||
:placeholder="'请输入' + item.title" />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList, add, edit, del, exportData } from '@/api/ball/resources_server'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import funTool from '@/libs/funTool'
|
||||
|
||||
export default {
|
||||
name: 'resources',
|
||||
data() {
|
||||
// 定义表单验证规则
|
||||
const rules = {
|
||||
resource_name: [{ required: true, message: '请输入资源名称', trigger: 'blur' }],
|
||||
resource_type: [{ required: true, message: '请输入资源类型', trigger: 'blur' }],
|
||||
resource_url: [{ required: true, message: '请输入资源链接', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
return {
|
||||
editWarp: false,
|
||||
editTitle: '新增',
|
||||
formValidate: {},
|
||||
ruleValidate: rules,
|
||||
seachTypes: [
|
||||
{ key: 'user_id', title: '用户ID' }
|
||||
],
|
||||
gridOption: {
|
||||
data: [],
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
resource_type: null,
|
||||
status: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id' },
|
||||
{
|
||||
title: '用户',
|
||||
key: 'user_id',
|
||||
render: (h, params) => {
|
||||
const userInfo = params.row.user_info;
|
||||
if (userInfo && userInfo.nickname) {
|
||||
return h('div', [
|
||||
h('img', {
|
||||
attrs: {
|
||||
src: userInfo.avatar_url || '/default-avatar.png',
|
||||
alt: userInfo.nickname,
|
||||
title: userInfo.nickname
|
||||
},
|
||||
style: {
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
borderRadius: '50%',
|
||||
marginRight: '8px',
|
||||
verticalAlign: 'middle'
|
||||
}
|
||||
}),
|
||||
h('span', { style: { verticalAlign: 'middle' } }, userInfo.nickname)
|
||||
]);
|
||||
}
|
||||
return h('span', `用户ID: ${params.row.user_id}`);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '资源类型',
|
||||
key: 'resource_type',
|
||||
render: (h, params) => {
|
||||
const typeMap = {
|
||||
'image': { text: '图片', color: 'blue' },
|
||||
'video': { text: '视频', color: 'purple' },
|
||||
'audio': { text: '音频', color: 'orange' },
|
||||
'document': { text: '文档', color: 'green' },
|
||||
'other': { text: '其他', color: 'default' }
|
||||
};
|
||||
const type = typeMap[params.row.resource_type] || { text: params.row.resource_type || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: type.color } }, type.text);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '文件预览',
|
||||
key: 'file_url',
|
||||
render: (h, params) => {
|
||||
const fileUrl = params.row.file_url;
|
||||
const resourceType = params.row.resource_type;
|
||||
|
||||
if (!fileUrl) {
|
||||
return h('span', { style: { color: '#c5c8ce' } }, '无文件');
|
||||
}
|
||||
|
||||
// 图片预览
|
||||
if (resourceType === 'image') {
|
||||
return h('img', {
|
||||
attrs: {
|
||||
src: fileUrl,
|
||||
alt: params.row.original_name || '图片',
|
||||
title: '点击查看大图'
|
||||
},
|
||||
style: {
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
objectFit: 'cover',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer'
|
||||
},
|
||||
on: {
|
||||
click: () => {
|
||||
window.open(fileUrl, '_blank');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 其他文件类型显示链接
|
||||
return h('a', {
|
||||
attrs: {
|
||||
href: fileUrl,
|
||||
target: '_blank',
|
||||
title: '点击下载/查看文件'
|
||||
},
|
||||
style: {
|
||||
color: '#2d8cf0',
|
||||
textDecoration: 'none'
|
||||
}
|
||||
}, '查看文件');
|
||||
}
|
||||
},
|
||||
{ title: '文件大小(字节)', key: 'file_size' },
|
||||
{ title: 'MIME类型', key: 'mime_type' },
|
||||
{
|
||||
title: '是否公开',
|
||||
key: 'is_public',
|
||||
render: (h, params) => {
|
||||
return h('Tag', {
|
||||
props: { color: params.row.is_public == 1 ? 'green' : 'default' }
|
||||
}, params.row.is_public == 1 ? '是' : '否');
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'status',
|
||||
render: (h, params) => {
|
||||
const statusMap = {
|
||||
'active': { text: '正常', color: 'green' },
|
||||
'deleted': { text: '已删除', color: 'red' },
|
||||
'pending': { text: '待审核', color: 'orange' }
|
||||
};
|
||||
const status = statusMap[params.row.status] || { text: params.row.status || '未知', color: 'default' };
|
||||
return h('Tag', { props: { color: status.color } }, status.text);
|
||||
}
|
||||
},
|
||||
{ title: '创建时间', key: 'create_time' },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.del(params.row)
|
||||
}
|
||||
}
|
||||
];
|
||||
return uiTool.getBtn(h, btns);
|
||||
}
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '资源名称', key: 'resource_name', type: 'text', required: true },
|
||||
{ title: '资源类型', key: 'resource_type', type: 'text', required: true },
|
||||
{ title: '资源链接', key: 'resource_url', type: 'text', required: true },
|
||||
{ title: '资源描述', key: 'resource_description', type: 'textarea', required: false }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1)
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page
|
||||
getList(this.gridOption.param).then(res => {
|
||||
if (res && res.data) {
|
||||
this.gridOption.data = res.data.rows || res.data
|
||||
this.gridOption.param.pageOption.total = res.data.count || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
showAddModal() {
|
||||
this.editTitle = '新增'
|
||||
this.formValidate = {}
|
||||
this.editWarp = true
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.editTitle = '编辑'
|
||||
this.formValidate = { ...row }
|
||||
this.editWarp = true
|
||||
},
|
||||
save() {
|
||||
this.$refs.formValidate.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.formValidate.id) {
|
||||
edit(this.formValidate).then(res => {
|
||||
this.$Message.success('编辑成功')
|
||||
this.editWarp = false
|
||||
this.query(1)
|
||||
})
|
||||
} else {
|
||||
add(this.formValidate).then(res => {
|
||||
this.$Message.success('新增成功')
|
||||
this.editWarp = false
|
||||
this.query(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
cancel() {
|
||||
this.editWarp = false
|
||||
},
|
||||
del(row) {
|
||||
this.$Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '确定要删除这条记录吗?',
|
||||
onOk: () => {
|
||||
del({ id: row.id }).then(res => {
|
||||
this.$Message.success('删除成功')
|
||||
this.query(1)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
exportData(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '资源管理.csv')
|
||||
})
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'user_id',
|
||||
value: '',
|
||||
resource_type: null,
|
||||
status: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.title : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content-view {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.table-head-tool {
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.table-body {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
146
demo/src/views/users/recommend_blocks.vue
Normal file
146
demo/src/views/users/recommend_blocks.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import recommend_blocksServer from '@/api/users/recommend_blocks_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["blocked_user_id"] = [{ required: true, type: "number", message: '请填写被屏蔽用户ID', trigger: 'change' }];
|
||||
rules["block_time"] = [{ required: false, message: '请填写屏蔽时间', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'blocked_user_id', value: '被屏蔽用户ID' },
|
||||
{ key: 'nickname', value: '用户昵称' }
|
||||
],
|
||||
seachTypePlaceholder: '请选择搜索类型',
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'nickname',
|
||||
value: ''
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 100 },
|
||||
{ title: '被屏蔽用户ID', key: 'blocked_user_id', minWidth: 120 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 150 },
|
||||
{ title: '被屏蔽用户昵称', key: 'blocked_nickname', minWidth: 150 },
|
||||
{ title: '屏蔽时间', key: 'block_time', minWidth: 160 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '用户ID', key: 'user_id', type: 'number', required: true },
|
||||
{ title: '被屏蔽用户ID', key: 'blocked_user_id', type: 'number', required: true },
|
||||
{ title: '屏蔽时间', key: 'block_time', type: 'datetime' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
recommend_blocksServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await recommend_blocksServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
recommend_blocksServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '推荐屏蔽.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: ''
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
147
demo/src/views/users/user_follows.vue
Normal file
147
demo/src/views/users/user_follows.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import menuServer from '@/api/system_high/menuServer.js'
|
||||
import user_followsServer from '@/api/users/user_follows_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["follower_id"] = [{ required: true, type: "number", message: '请填写关注者ID', trigger: 'change' }];
|
||||
rules["following_id"] = [{ required: true, type: "number", message: '请填写被关注者ID', trigger: 'change' }];
|
||||
rules["follow_time"] = [{ required: false, message: '请填写关注时间', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'follower_id', value: '关注者ID' },
|
||||
{ key: 'following_id', value: '被关注者ID' },
|
||||
{ key: 'nickname', value: '用户昵称' }
|
||||
],
|
||||
seachTypePlaceholder: '请选择搜索类型',
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'nickname',
|
||||
value: ''
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '关注者ID', key: 'follower_id', minWidth: 100 },
|
||||
{ title: '被关注者ID', key: 'following_id', minWidth: 120 },
|
||||
{ title: '关注者昵称', key: 'nickname', minWidth: 150 },
|
||||
{ title: '被关注者昵称', key: 'followed_nickname', minWidth: 150 },
|
||||
{ title: '关注时间', key: 'follow_time', minWidth: 160 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '关注者ID', key: 'follower_id', type: 'number', required: true },
|
||||
{ title: '被关注者ID', key: 'following_id', type: 'number', required: true },
|
||||
{ title: '关注时间', key: 'follow_time', type: 'datetime' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
user_followsServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await user_followsServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
user_followsServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '用户关注关系.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: ''
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
173
demo/src/views/users/user_tracking.vue
Normal file
173
demo/src/views/users/user_tracking.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<div class="content-view">
|
||||
<div class="table-head-tool">
|
||||
<Button type="primary" @click="showAddWarp">新增</Button>
|
||||
<Form ref="formInline" :model="gridOption.param.seachOption" inline :label-width="80">
|
||||
<FormItem :label-width="20" class="flex">
|
||||
<Select v-model="gridOption.param.seachOption.key" style="width: 120px"
|
||||
:placeholder="seachTypePlaceholder">
|
||||
<Option v-for="item in seachTypes" :value="item.key" :key="item.key">{{ item.value }}</Option>
|
||||
</Select>
|
||||
<Input class="ml10" v-model="gridOption.param.seachOption.value" style="width: 200px" search
|
||||
placeholder="请输入关键字" @on-search="query(1)" />
|
||||
</FormItem>
|
||||
<FormItem label="事件类型">
|
||||
<Select v-model="gridOption.param.seachOption.event_type" style="width: 120px" clearable @on-change="query(1)">
|
||||
<Option value="page_view">页面浏览</Option>
|
||||
<Option value="click">点击</Option>
|
||||
<Option value="search">搜索</Option>
|
||||
<Option value="share">分享</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" @click="query(1)">查询</Button>
|
||||
<Button type="default" @click="resetQuery" class="ml10">重置</Button>
|
||||
<Button type="default" @click="exportCsv">导出</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
<div class="table-body">
|
||||
<tables :columns="listColumns" :value="gridOption.data" :pageOption="gridOption.param.pageOption"
|
||||
@changePage="query"></tables>
|
||||
</div>
|
||||
<editModal ref="editModal" :columns="editColumns" :rules="gridOption.rules"> </editModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import funTool from '@/libs/funTool'
|
||||
import uiTool from '@/libs/uiTool'
|
||||
import user_trackingServer from '@/api/users/user_tracking_server.js'
|
||||
export default {
|
||||
data() {
|
||||
let rules = {}
|
||||
|
||||
rules["user_id"] = [{ required: true, type: "number", message: '请填写用户ID', trigger: 'change' }];
|
||||
rules["event_type"] = [{ required: true, message: '请填写事件类型' }];
|
||||
rules["event_name"] = [{ required: true, message: '请填写事件名称' }];
|
||||
rules["page_path"] = [{ required: false, message: '请填写页面路径' }];
|
||||
rules["duration"] = [{ required: false, type: "number", message: '请填写停留时长', trigger: 'change' }];
|
||||
|
||||
return {
|
||||
seachTypes: [
|
||||
{ key: 'user_id', value: '用户ID' },
|
||||
{ key: 'event_name', value: '事件名称' },
|
||||
{ key: 'nickname', value: '用户昵称' }
|
||||
],
|
||||
gridOption: {
|
||||
param: {
|
||||
seachOption: {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
event_type: null
|
||||
},
|
||||
pageOption: {
|
||||
page: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
data: [],
|
||||
rules: rules
|
||||
},
|
||||
listColumns: [
|
||||
{ title: 'ID', key: 'id', minWidth: 80 },
|
||||
{ title: '用户ID', key: 'user_id', minWidth: 100 },
|
||||
{ title: '用户昵称', key: 'nickname', minWidth: 120 },
|
||||
{ title: '事件类型', key: 'event_type', minWidth: 120 },
|
||||
{ title: '事件名称', key: 'event_name', minWidth: 150 },
|
||||
{ title: '页面路径', key: 'page_path', minWidth: 200 },
|
||||
{ title: '停留时长(秒)', key: 'duration', minWidth: 100 },
|
||||
{ title: 'IP地址', key: 'ip_address', minWidth: 130 },
|
||||
{ title: '创建时间', key: 'create_time', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
type: 'template',
|
||||
render: (h, params) => {
|
||||
let btns = [
|
||||
{
|
||||
title: '编辑',
|
||||
type: 'primary',
|
||||
click: () => {
|
||||
this.showEditWarp(params.row)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '删除',
|
||||
type: 'error',
|
||||
click: () => {
|
||||
this.delConfirm(params.row)
|
||||
},
|
||||
},
|
||||
]
|
||||
return uiTool.getBtn(h, btns)
|
||||
},
|
||||
}
|
||||
],
|
||||
editColumns: [
|
||||
{ title: '用户ID', key: 'user_id', type: 'number', required: true },
|
||||
{ title: '事件类型', key: 'event_type', type: 'select', required: true, options: [
|
||||
{ value: 'user_soure', label: '用户来源' },
|
||||
{ value: 'page_view', label: '页面访问' },
|
||||
{ value: 'button_click', label: '按钮点击' },
|
||||
{ value: 'api_call', label: '接口调用' },
|
||||
{ value: 'user_action', label: '用户行为' },
|
||||
{ value: 'form_submit', label: '表单提交' },
|
||||
{ value: 'search', label: '搜索' },
|
||||
{ value: 'share', label: '分享' },
|
||||
{ value: 'error', label: '错误' }
|
||||
]},
|
||||
{ title: '事件名称', key: 'event_name', type: 'text', required: true },
|
||||
{ title: '页面路径', key: 'page_path', type: 'text' },
|
||||
{ title: '停留时长(秒)', key: 'duration', type: 'number' },
|
||||
{ title: 'IP地址', key: 'ip_address', type: 'text' },
|
||||
{ title: '用户代理', key: 'user_agent', type: 'textarea' }
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.query(1);
|
||||
},
|
||||
methods: {
|
||||
query(page) {
|
||||
this.gridOption.param.pageOption.page = page;
|
||||
user_trackingServer.page(this.gridOption.param).then(res => {
|
||||
this.gridOption.data = res.data.rows;
|
||||
this.gridOption.param.pageOption.total = res.data.count;
|
||||
});
|
||||
},
|
||||
showAddWarp() {
|
||||
this.$refs.editModal.showModal();
|
||||
},
|
||||
showEditWarp(row) {
|
||||
this.$refs.editModal.showModal(row);
|
||||
},
|
||||
delConfirm(row) {
|
||||
uiTool.delConfirm(async () => {
|
||||
await user_trackingServer.del(row)
|
||||
rootVue.$Message.success('删除成功!')
|
||||
this.query(1)
|
||||
})
|
||||
},
|
||||
exportCsv() {
|
||||
user_trackingServer.exportCsv(this.gridOption.param).then(res => {
|
||||
funTool.downloadFile(res, '用户行为追踪.csv');
|
||||
});
|
||||
},
|
||||
resetQuery() {
|
||||
this.gridOption.param.seachOption = {
|
||||
key: 'nickname',
|
||||
value: '',
|
||||
event_type: null
|
||||
};
|
||||
this.query(1);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
seachTypePlaceholder() {
|
||||
const selected = this.seachTypes.find(item => item.key === this.gridOption.param.seachOption.key);
|
||||
return selected ? selected.value : '请选择搜索类型';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user