Merge branch 'master' of http://115.190.64.57:8000/xiaozhang/zhibo
This commit is contained in:
commit
2d88a55348
93
Zhibo/admin/src/api/fatePool.js
Normal file
93
Zhibo/admin/src/api/fatePool.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 缘池管理 API
|
||||
*/
|
||||
|
||||
// ========== 话题管理 ==========
|
||||
|
||||
// 话题列表
|
||||
export function fatePoolTopicListApi(params) {
|
||||
return request({
|
||||
url: '/admin/fate/pool/topic/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 话题详情
|
||||
export function fatePoolTopicInfoApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/topic/info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 新增话题
|
||||
export function fatePoolTopicSaveApi(data) {
|
||||
return request({
|
||||
url: '/admin/fate/pool/topic/save',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新话题
|
||||
export function fatePoolTopicUpdateApi(data) {
|
||||
return request({
|
||||
url: '/admin/fate/pool/topic/update',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除话题
|
||||
export function fatePoolTopicDeleteApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/topic/delete/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 修改话题状态
|
||||
export function fatePoolTopicStatusApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/topic/status/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 话题用户发布信息管理 ==========
|
||||
|
||||
// 发布信息列表
|
||||
export function fatePoolPostListApi(params) {
|
||||
return request({
|
||||
url: '/admin/fate/pool/post/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 发布信息详情
|
||||
export function fatePoolPostInfoApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/post/info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 删除发布信息
|
||||
export function fatePoolPostDeleteApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/post/delete/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 修改发布信息状态
|
||||
export function fatePoolPostStatusApi(id) {
|
||||
return request({
|
||||
url: `/admin/fate/pool/post/status/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
146
Zhibo/admin/src/api/wishTree.js
Normal file
146
Zhibo/admin/src/api/wishTree.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 许愿树管理 API
|
||||
*/
|
||||
|
||||
// ========== 许愿树管理 ==========
|
||||
|
||||
// 许愿树列表
|
||||
export function wishTreeListApi(params) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 许愿树详情(含节点)
|
||||
export function wishTreeInfoApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 新增许愿树(含节点)
|
||||
export function wishTreeSaveApi(data) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/save',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新许愿树(含节点)
|
||||
export function wishTreeUpdateApi(data) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/update',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除许愿树
|
||||
export function wishTreeDeleteApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/delete/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 启用/停用许愿树
|
||||
export function wishTreeActivateApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/activate/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 节点管理 ==========
|
||||
|
||||
// 节点列表
|
||||
export function wishTreeNodeListApi(params) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/node/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 节点详情
|
||||
export function wishTreeNodeInfoApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/node/info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 新增节点
|
||||
export function wishTreeNodeSaveApi(data) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/node/save',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新节点
|
||||
export function wishTreeNodeUpdateApi(data) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/node/update',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除节点
|
||||
export function wishTreeNodeDeleteApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/node/delete/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 修改节点状态
|
||||
export function wishTreeNodeStatusApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/node/status/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 用户留言管理 ==========
|
||||
|
||||
// 留言列表(按许愿树查询)
|
||||
export function wishTreeMessageListApi(params) {
|
||||
return request({
|
||||
url: '/admin/wish/tree/message/list',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 留言详情
|
||||
export function wishTreeMessageInfoApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/message/info/${id}`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 删除留言
|
||||
export function wishTreeMessageDeleteApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/message/delete/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 修改留言状态
|
||||
export function wishTreeMessageStatusApi(id) {
|
||||
return request({
|
||||
url: `/admin/wish/tree/message/status/${id}`,
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@ import contentManageRouter from './modules/contentManage'; // 内容管理
|
|||
import feedbackManageRouter from './modules/feedbackManage'; // 用户反馈
|
||||
import agentManageRouter from './modules/agentManage'; // 代理管理
|
||||
import systemSettingRouter from './modules/systemSetting'; // 系统设置
|
||||
import fatePoolRouter from './modules/fatePool'; // 缘池
|
||||
import wishTreeRouter from './modules/wishTree'; // 许愿树
|
||||
|
||||
/**
|
||||
* Note: sub-menu only appear when route children.length >= 1
|
||||
|
|
@ -84,6 +86,10 @@ export const constantRoutes = [
|
|||
liveManageRouter,
|
||||
// 4. 社交互动
|
||||
socialManageRouter,
|
||||
// 4.5 缘池
|
||||
fatePoolRouter,
|
||||
// 4.6 许愿树
|
||||
wishTreeRouter,
|
||||
// 5. 礼物打赏
|
||||
giftManageRouter,
|
||||
// 6. 虚拟道具
|
||||
|
|
|
|||
39
Zhibo/admin/src/router/modules/fatePool.js
Normal file
39
Zhibo/admin/src/router/modules/fatePool.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import Layout from '@/layout';
|
||||
|
||||
/**
|
||||
* 缘池路由
|
||||
*/
|
||||
const fatePoolRouter = {
|
||||
path: '/fatePool',
|
||||
component: Layout,
|
||||
redirect: '/fatePool/topic',
|
||||
name: 'FatePool',
|
||||
alwaysShow: true,
|
||||
meta: {
|
||||
title: '缘池',
|
||||
icon: 'el-icon-connection',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/fatePool/topic',
|
||||
component: () => import('@/views/fatePool/topic/index'),
|
||||
name: 'FatePoolTopic',
|
||||
meta: { title: '话题管理', icon: '' },
|
||||
},
|
||||
{
|
||||
path: '/fatePool/topicUser',
|
||||
component: () => import('@/views/fatePool/topicUser/index'),
|
||||
name: 'FatePoolTopicUser',
|
||||
meta: { title: '用户发布', icon: '' },
|
||||
},
|
||||
{
|
||||
path: '/fatePool/topic/post/:topicId',
|
||||
component: () => import('@/views/fatePool/topic/post'),
|
||||
name: 'FatePoolTopicPost',
|
||||
meta: { title: '用户发布', icon: '' },
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default fatePoolRouter;
|
||||
45
Zhibo/admin/src/router/modules/wishTree.js
Normal file
45
Zhibo/admin/src/router/modules/wishTree.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import Layout from '@/layout';
|
||||
|
||||
/**
|
||||
* 许愿树路由
|
||||
*/
|
||||
const wishTreeRouter = {
|
||||
path: '/wishTree',
|
||||
component: Layout,
|
||||
redirect: '/wishTree/tree',
|
||||
name: 'WishTree',
|
||||
alwaysShow: true,
|
||||
meta: {
|
||||
title: '许愿树',
|
||||
icon: 'el-icon-magic-stick',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/wishTree/tree',
|
||||
component: () => import('@/views/wishTree/tree/index'),
|
||||
name: 'WishTreeList',
|
||||
meta: { title: '许愿树列表', icon: '' },
|
||||
},
|
||||
{
|
||||
path: '/wishTree/node',
|
||||
component: () => import('@/views/wishTree/node/index'),
|
||||
name: 'WishTreeNode',
|
||||
meta: { title: '留言节点', icon: '' },
|
||||
},
|
||||
{
|
||||
path: '/wishTree/message',
|
||||
component: () => import('@/views/wishTree/message/index'),
|
||||
name: 'WishTreeMessage',
|
||||
meta: { title: '用户留言', icon: '' },
|
||||
},
|
||||
{
|
||||
path: '/wishTree/tree/detail/:treeId',
|
||||
component: () => import('@/views/wishTree/tree/detail'),
|
||||
name: 'WishTreeDetail',
|
||||
meta: { title: '许愿树详情', icon: '' },
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default wishTreeRouter;
|
||||
196
Zhibo/admin/src/views/fatePool/topic/index.vue
Normal file
196
Zhibo/admin/src/views/fatePool/topic/index.vue
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<!-- 搜索区域 -->
|
||||
<div class="clearfix" slot="header">
|
||||
<el-form :inline="true" :model="queryParams" size="small">
|
||||
<el-form-item label="话题标题">
|
||||
<el-input v-model="queryParams.title" placeholder="请输入话题标题" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd">新增话题</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column label="封面" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image v-if="scope.row.cover_image" :src="scope.row.cover_image" :preview-src-list="[scope.row.cover_image]" style="width: 60px; height: 60px" fit="cover" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="话题标题" min-width="150" />
|
||||
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="post_count" label="发布数" width="100" align="center" />
|
||||
<el-table-column prop="sort" label="排序" width="80" align="center" />
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleViewPosts(scope.row)">查看发布</el-button>
|
||||
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" @close="handleDialogClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="话题标题" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入话题标题" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="话题描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入话题描述" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="封面图片" prop="cover_image">
|
||||
<el-input v-model="formData.cover_image" placeholder="请输入封面图片URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" :max="9999" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fatePoolTopicListApi, fatePoolTopicSaveApi, fatePoolTopicUpdateApi, fatePoolTopicDeleteApi, fatePoolTopicStatusApi } from '@/api/fatePool';
|
||||
|
||||
export default {
|
||||
name: 'FatePoolTopic',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, title: '', status: '' },
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增话题',
|
||||
formData: { id: null, title: '', description: '', cover_image: '', sort: 0, status: 1 },
|
||||
formRules: { title: [{ required: true, message: '请输入话题标题', trigger: 'blur' }] },
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fatePoolTopicListApi(this.queryParams);
|
||||
// 响应拦截器已处理,成功时直接返回data
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, title: '', status: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增话题';
|
||||
this.formData = { id: null, title: '', description: '', cover_image: '', sort: 0, status: 1 };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.dialogTitle = '编辑话题';
|
||||
this.formData = { ...row };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
handleViewPosts(row) {
|
||||
this.$router.push({ path: `/fatePool/topic/post/${row.id}`, params: { topicId: row.id } });
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该话题吗?删除后该话题下的所有用户发布信息也会被删除。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await fatePoolTopicDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await fatePoolTopicStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.formRef.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
this.$message.warning('请填写必填项');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const api = this.formData.id ? fatePoolTopicUpdateApi : fatePoolTopicSaveApi;
|
||||
await api(this.formData);
|
||||
this.$message.success(this.formData.id ? '编辑成功' : '新增成功');
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.$message.error('操作失败');
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDialogClose() {
|
||||
this.$refs.formRef && this.$refs.formRef.resetFields();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
179
Zhibo/admin/src/views/fatePool/topic/post.vue
Normal file
179
Zhibo/admin/src/views/fatePool/topic/post.vue
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-button type="text" icon="el-icon-back" @click="$router.back()">返回</el-button>
|
||||
<span style="margin-left: 10px; font-size: 16px; font-weight: bold;">{{ topicTitle }} - 用户发布信息</span>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" :model="queryParams" size="small" style="margin-bottom: 20px;">
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
|
||||
<el-option label="显示" :value="1" />
|
||||
<el-option label="隐藏" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :src="scope.row.user_avatar" :size="40" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user_nickname" label="昵称" width="120" />
|
||||
<el-table-column prop="content" label="发布内容" min-width="250" show-overflow-tooltip />
|
||||
<el-table-column label="图片" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.images && scope.row.images.length" type="text" @click="handleViewImages(scope.row)">查看({{ scope.row.images.length }})</el-button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="发布时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleView(scope.row)">详情</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="发布详情" :visible.sync="detailVisible" width="600px">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户昵称">{{ detailData.user_nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布时间">{{ detailData.create_time }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布内容" :span="2">{{ detailData.content }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detailData.images && detailData.images.length" style="margin-top: 20px">
|
||||
<div style="margin-bottom: 10px; font-weight: bold">图片:</div>
|
||||
<el-image v-for="(img, index) in detailData.images" :key="index" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="图片预览" :visible.sync="imageVisible" width="800px">
|
||||
<div style="text-align: center">
|
||||
<el-image v-for="(img, index) in previewImages" :key="index" :src="img" :preview-src-list="previewImages" style="width: 150px; height: 150px; margin: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fatePoolTopicInfoApi, fatePoolPostListApi, fatePoolPostDeleteApi, fatePoolPostStatusApi } from '@/api/fatePool';
|
||||
|
||||
export default {
|
||||
name: 'FatePoolTopicPost',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
topicId: null,
|
||||
topicTitle: '',
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, topic_id: '', nickname: '', status: '' },
|
||||
detailVisible: false,
|
||||
detailData: {},
|
||||
imageVisible: false,
|
||||
previewImages: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.topicId = this.$route.params.topicId;
|
||||
this.queryParams.topic_id = this.topicId;
|
||||
this.getTopicInfo();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getTopicInfo() {
|
||||
try {
|
||||
const res = await fatePoolTopicInfoApi(this.topicId);
|
||||
this.topicTitle = res.title || '';
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fatePoolPostListApi(this.queryParams);
|
||||
this.tableData = (res.list || []).map(item => ({
|
||||
...item,
|
||||
images: item.images ? (typeof item.images === 'string' ? JSON.parse(item.images) : item.images) : [],
|
||||
}));
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, topic_id: this.topicId, nickname: '', status: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleView(row) {
|
||||
this.detailData = row;
|
||||
this.detailVisible = true;
|
||||
},
|
||||
handleViewImages(row) {
|
||||
this.previewImages = row.images;
|
||||
this.imageVisible = true;
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该发布信息吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await fatePoolPostDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await fatePoolPostStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
181
Zhibo/admin/src/views/fatePool/topicUser/index.vue
Normal file
181
Zhibo/admin/src/views/fatePool/topicUser/index.vue
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-form :inline="true" :model="queryParams" size="small">
|
||||
<el-form-item label="话题">
|
||||
<el-select v-model="queryParams.topic_id" placeholder="请选择话题" clearable>
|
||||
<el-option v-for="item in topicList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
|
||||
<el-option label="显示" :value="1" />
|
||||
<el-option label="隐藏" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="topic_title" label="所属话题" width="150" />
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :src="scope.row.user_avatar" :size="40" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user_nickname" label="昵称" width="120" />
|
||||
<el-table-column prop="content" label="发布内容" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="图片" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.images && scope.row.images.length" type="text" @click="handleViewImages(scope.row)">查看({{ scope.row.images.length }})</el-button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="发布时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleView(scope.row)">详情</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="发布详情" :visible.sync="detailVisible" width="600px">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户昵称">{{ detailData.user_nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布时间">{{ detailData.create_time }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属话题">{{ detailData.topic_title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ detailData.status === 1 ? '显示' : '隐藏' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布内容" :span="2">{{ detailData.content }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detailData.images && detailData.images.length" style="margin-top: 20px">
|
||||
<div style="margin-bottom: 10px; font-weight: bold">图片:</div>
|
||||
<el-image v-for="(img, index) in detailData.images" :key="index" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="图片预览" :visible.sync="imageVisible" width="800px">
|
||||
<div style="text-align: center">
|
||||
<el-image v-for="(img, index) in previewImages" :key="index" :src="img" :preview-src-list="previewImages" style="width: 150px; height: 150px; margin: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fatePoolTopicListApi, fatePoolPostListApi, fatePoolPostDeleteApi, fatePoolPostStatusApi } from '@/api/fatePool';
|
||||
|
||||
export default {
|
||||
name: 'FatePoolTopicUser',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
topicList: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, topic_id: '', nickname: '', status: '' },
|
||||
detailVisible: false,
|
||||
detailData: {},
|
||||
imageVisible: false,
|
||||
previewImages: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTopicList();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getTopicList() {
|
||||
try {
|
||||
const res = await fatePoolTopicListApi({ page: 1, limit: 1000, status: 1 });
|
||||
this.topicList = res.list || [];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await fatePoolPostListApi(this.queryParams);
|
||||
this.tableData = (res.list || []).map(item => ({
|
||||
...item,
|
||||
images: item.images ? (typeof item.images === 'string' ? JSON.parse(item.images) : item.images) : [],
|
||||
}));
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, topic_id: '', nickname: '', status: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleView(row) {
|
||||
this.detailData = row;
|
||||
this.detailVisible = true;
|
||||
},
|
||||
handleViewImages(row) {
|
||||
this.previewImages = row.images;
|
||||
this.imageVisible = true;
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该发布信息吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await fatePoolPostDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await fatePoolPostStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
213
Zhibo/admin/src/views/wishTree/message/index.vue
Normal file
213
Zhibo/admin/src/views/wishTree/message/index.vue
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-form :inline="true" :model="queryParams" size="small">
|
||||
<el-form-item label="许愿树">
|
||||
<el-select v-model="queryParams.tree_id" placeholder="请选择许愿树" clearable @change="handleTreeChange">
|
||||
<el-option v-for="item in treeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="节点">
|
||||
<el-select v-model="queryParams.node_id" placeholder="请选择节点" clearable>
|
||||
<el-option v-for="item in nodeList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
|
||||
<el-option label="显示" :value="1" />
|
||||
<el-option label="隐藏" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="node_title" label="所属节点" width="120" />
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :src="scope.row.user_avatar" :size="40" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user_nickname" label="昵称" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.is_anonymous === 1 ? '匿名用户' : scope.row.user_nickname }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="留言内容" min-width="250" show-overflow-tooltip />
|
||||
<el-table-column label="图片" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.images && scope.row.images.length" type="text" @click="handleViewImages(scope.row)">查看({{ scope.row.images.length }})</el-button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="匿名" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.is_anonymous === 1" type="warning" size="small">是</el-tag>
|
||||
<el-tag v-else type="info" size="small">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="留言时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleView(scope.row)">详情</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="留言详情" :visible.sync="detailVisible" width="600px">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户昵称">{{ detailData.is_anonymous === 1 ? '匿名用户' : detailData.user_nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否匿名">{{ detailData.is_anonymous === 1 ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属节点">{{ detailData.node_title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="留言时间">{{ detailData.create_time }}</el-descriptions-item>
|
||||
<el-descriptions-item label="留言内容" :span="2">{{ detailData.content }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detailData.images && detailData.images.length" style="margin-top: 20px">
|
||||
<div style="margin-bottom: 10px; font-weight: bold">图片:</div>
|
||||
<el-image v-for="(img, index) in detailData.images" :key="index" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="图片预览" :visible.sync="imageVisible" width="800px">
|
||||
<div style="text-align: center">
|
||||
<el-image v-for="(img, index) in previewImages" :key="index" :src="img" :preview-src-list="previewImages" style="width: 150px; height: 150px; margin: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { wishTreeListApi, wishTreeInfoApi, wishTreeMessageListApi, wishTreeMessageDeleteApi, wishTreeMessageStatusApi } from '@/api/wishTree';
|
||||
|
||||
export default {
|
||||
name: 'WishTreeMessage',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
treeList: [],
|
||||
nodeList: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, tree_id: '', node_id: '', nickname: '', status: '' },
|
||||
detailVisible: false,
|
||||
detailData: {},
|
||||
imageVisible: false,
|
||||
previewImages: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTreeList();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getTreeList() {
|
||||
try {
|
||||
const res = await wishTreeListApi({ page: 1, limit: 1000 });
|
||||
this.treeList = res.list || [];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getNodeList(treeId) {
|
||||
try {
|
||||
const res = await wishTreeInfoApi(treeId);
|
||||
this.nodeList = res.nodes || [];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await wishTreeMessageListApi(this.queryParams);
|
||||
this.tableData = (res.list || []).map(item => ({
|
||||
...item,
|
||||
images: item.images ? (typeof item.images === 'string' ? JSON.parse(item.images) : item.images) : [],
|
||||
}));
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleTreeChange(val) {
|
||||
this.queryParams.node_id = '';
|
||||
this.nodeList = [];
|
||||
if (val) {
|
||||
this.getNodeList(val);
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, tree_id: '', node_id: '', nickname: '', status: '' };
|
||||
this.nodeList = [];
|
||||
this.getList();
|
||||
},
|
||||
handleView(row) {
|
||||
this.detailData = row;
|
||||
this.detailVisible = true;
|
||||
},
|
||||
handleViewImages(row) {
|
||||
this.previewImages = row.images;
|
||||
this.imageVisible = true;
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该留言吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await wishTreeMessageDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await wishTreeMessageStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
209
Zhibo/admin/src/views/wishTree/node/index.vue
Normal file
209
Zhibo/admin/src/views/wishTree/node/index.vue
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-form :inline="true" :model="queryParams" size="small">
|
||||
<el-form-item label="许愿树">
|
||||
<el-select v-model="queryParams.tree_id" placeholder="请选择许愿树" clearable>
|
||||
<el-option v-for="item in treeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="节点标题">
|
||||
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
|
||||
<el-option label="启用" :value="1" />
|
||||
<el-option label="禁用" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd">新增节点</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="tree_name" label="所属许愿树" width="150" />
|
||||
<el-table-column prop="title" label="节点标题" min-width="150" />
|
||||
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="开启时间" width="180" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.open_time">{{ scope.row.open_time }}</span>
|
||||
<el-tag v-else type="success" size="small">立即开启</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="message_count" label="留言数" width="80" align="center" />
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="600px" @close="handleDialogClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="许愿树" prop="tree_id">
|
||||
<el-select v-model="formData.tree_id" placeholder="请选择许愿树" style="width: 100%">
|
||||
<el-option v-for="item in treeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="节点标题" prop="title">
|
||||
<el-input v-model="formData.title" placeholder="请输入节点标题" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入描述" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="图标URL" prop="icon">
|
||||
<el-input v-model="formData.icon" placeholder="请输入图标URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开启时间" prop="open_time">
|
||||
<el-date-picker v-model="formData.open_time" type="datetime" placeholder="不设置则立即开启" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" :max="9999" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { wishTreeListApi, wishTreeNodeListApi, wishTreeNodeSaveApi, wishTreeNodeUpdateApi, wishTreeNodeDeleteApi, wishTreeNodeStatusApi } from '@/api/wishTree';
|
||||
|
||||
export default {
|
||||
name: 'WishTreeNode',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
treeList: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, tree_id: '', title: '', status: '' },
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增节点',
|
||||
formData: { id: null, tree_id: '', title: '', description: '', icon: '', open_time: null, sort: 0, status: 1 },
|
||||
formRules: {
|
||||
tree_id: [{ required: true, message: '请选择许愿树', trigger: 'change' }],
|
||||
title: [{ required: true, message: '请输入节点标题', trigger: 'blur' }],
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getTreeList();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getTreeList() {
|
||||
try {
|
||||
const res = await wishTreeListApi({ page: 1, limit: 1000 });
|
||||
this.treeList = res.list || [];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await wishTreeNodeListApi(this.queryParams);
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, tree_id: '', title: '', status: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增节点';
|
||||
this.formData = { id: null, tree_id: '', title: '', description: '', icon: '', open_time: null, sort: 0, status: 1 };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.dialogTitle = '编辑节点';
|
||||
this.formData = { ...row };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该节点吗?删除后该节点下的所有留言也会被删除。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await wishTreeNodeDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await wishTreeNodeStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.formRef.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
try {
|
||||
const api = this.formData.id ? wishTreeNodeUpdateApi : wishTreeNodeSaveApi;
|
||||
await api(this.formData);
|
||||
this.$message.success(this.formData.id ? '编辑成功' : '新增成功');
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDialogClose() {
|
||||
this.$refs.formRef && this.$refs.formRef.resetFields();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
199
Zhibo/admin/src/views/wishTree/tree/detail.vue
Normal file
199
Zhibo/admin/src/views/wishTree/tree/detail.vue
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-button type="text" icon="el-icon-back" @click="$router.back()">返回</el-button>
|
||||
<span style="margin-left: 10px; font-size: 16px; font-weight: bold;">{{ treeName }} - 用户留言</span>
|
||||
</div>
|
||||
|
||||
<el-form :inline="true" :model="queryParams" size="small" style="margin-bottom: 20px;">
|
||||
<el-form-item label="节点">
|
||||
<el-select v-model="queryParams.node_id" placeholder="全部节点" clearable>
|
||||
<el-option v-for="item in nodeList" :key="item.id" :label="item.title" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称">
|
||||
<el-input v-model="queryParams.nickname" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择" clearable>
|
||||
<el-option label="显示" :value="1" />
|
||||
<el-option label="隐藏" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="node_title" label="所属节点" width="120" />
|
||||
<el-table-column label="头像" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-avatar :src="scope.row.user_avatar" :size="40" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user_nickname" label="昵称" width="100">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.is_anonymous === 1 ? '匿名用户' : scope.row.user_nickname }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="留言内容" min-width="250" show-overflow-tooltip />
|
||||
<el-table-column label="图片" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.images && scope.row.images.length" type="text" @click="handleViewImages(scope.row)">查看({{ scope.row.images.length }})</el-button>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="匿名" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.is_anonymous === 1" type="warning" size="small">是</el-tag>
|
||||
<el-tag v-else type="info" size="small">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" @change="handleStatusChange(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="留言时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleView(scope.row)">详情</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="留言详情" :visible.sync="detailVisible" width="600px">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户昵称">{{ detailData.is_anonymous === 1 ? '匿名用户' : detailData.user_nickname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否匿名">{{ detailData.is_anonymous === 1 ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属节点">{{ detailData.node_title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="留言时间">{{ detailData.create_time }}</el-descriptions-item>
|
||||
<el-descriptions-item label="留言内容" :span="2">{{ detailData.content }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-if="detailData.images && detailData.images.length" style="margin-top: 20px">
|
||||
<div style="margin-bottom: 10px; font-weight: bold">图片:</div>
|
||||
<el-image v-for="(img, index) in detailData.images" :key="index" :src="img" :preview-src-list="detailData.images" style="width: 100px; height: 100px; margin-right: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="图片预览" :visible.sync="imageVisible" width="800px">
|
||||
<div style="text-align: center">
|
||||
<el-image v-for="(img, index) in previewImages" :key="index" :src="img" :preview-src-list="previewImages" style="width: 150px; height: 150px; margin: 10px" fit="cover" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { wishTreeInfoApi, wishTreeMessageListApi, wishTreeMessageDeleteApi, wishTreeMessageStatusApi } from '@/api/wishTree';
|
||||
|
||||
export default {
|
||||
name: 'WishTreeDetail',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
treeId: null,
|
||||
treeName: '',
|
||||
nodeList: [],
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, tree_id: '', node_id: '', nickname: '', status: '' },
|
||||
detailVisible: false,
|
||||
detailData: {},
|
||||
imageVisible: false,
|
||||
previewImages: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.treeId = this.$route.params.treeId;
|
||||
this.queryParams.tree_id = this.treeId;
|
||||
this.getTreeInfo();
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getTreeInfo() {
|
||||
try {
|
||||
const res = await wishTreeInfoApi(this.treeId);
|
||||
this.treeName = res.name || '';
|
||||
this.nodeList = res.nodes || [];
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await wishTreeMessageListApi(this.queryParams);
|
||||
this.tableData = (res.list || []).map(item => ({
|
||||
...item,
|
||||
images: item.images ? (typeof item.images === 'string' ? JSON.parse(item.images) : item.images) : [],
|
||||
}));
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, tree_id: this.treeId, node_id: '', nickname: '', status: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleView(row) {
|
||||
this.detailData = row;
|
||||
this.detailVisible = true;
|
||||
},
|
||||
handleViewImages(row) {
|
||||
this.previewImages = row.images;
|
||||
this.imageVisible = true;
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该留言吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await wishTreeMessageDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleStatusChange(row) {
|
||||
try {
|
||||
await wishTreeMessageStatusApi(row.id);
|
||||
this.$message.success('状态修改成功');
|
||||
} catch (error) {
|
||||
row.status = row.status === 1 ? 0 : 1;
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
226
Zhibo/admin/src/views/wishTree/tree/index.vue
Normal file
226
Zhibo/admin/src/views/wishTree/tree/index.vue
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix" slot="header">
|
||||
<el-form :inline="true" :model="queryParams" size="small">
|
||||
<el-form-item label="许愿树名称">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd">新增许愿树</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column label="封面" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image v-if="scope.row.cover_image" :src="scope.row.cover_image" :preview-src-list="[scope.row.cover_image]" style="width: 60px; height: 60px" fit="cover" />
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="许愿树名称" min-width="150" />
|
||||
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="node_count" label="节点数" width="80" align="center" />
|
||||
<el-table-column prop="message_count" label="留言数" width="80" align="center" />
|
||||
<el-table-column label="启用状态" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.is_active === 1" type="success">已启用</el-tag>
|
||||
<el-tag v-else type="info">已停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="160" align="center" />
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="handleViewMessages(scope.row)">查看留言</el-button>
|
||||
<el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button v-if="scope.row.is_active !== 1" type="text" size="small" style="color: #67c23a" @click="handleActivate(scope.row)">启用</el-button>
|
||||
<el-button v-else type="text" size="small" style="color: #e6a23c" @click="handleActivate(scope.row)">停用</el-button>
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top: 20px; text-align: right" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.page" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.limit" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="800px" @close="handleDialogClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入许愿树名称" maxlength="100" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" :rows="2" placeholder="请输入描述" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="封面图片">
|
||||
<el-input v-model="formData.cover_image" placeholder="请输入封面图片URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="背景图片">
|
||||
<el-input v-model="formData.background_image" placeholder="请输入背景图片URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="formData.sort" :min="0" :max="9999" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">节点设置</el-divider>
|
||||
<el-button type="primary" size="small" @click="addNode" style="margin-bottom: 15px;">添加节点</el-button>
|
||||
<el-table :data="formData.nodes" border size="small" style="margin-bottom: 20px;">
|
||||
<el-table-column prop="title" label="节点标题" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.title" size="small" placeholder="节点标题" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.description" size="small" placeholder="节点描述" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="open_time" label="开启时间" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-date-picker v-model="scope.row.open_time" type="datetime" size="small" placeholder="不设置则立即开启" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sort" label="排序" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number v-model="scope.row.sort" size="small" :min="0" :max="999" controls-position="right" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" style="color: #f56c6c" @click="removeNode(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { wishTreeListApi, wishTreeInfoApi, wishTreeSaveApi, wishTreeUpdateApi, wishTreeDeleteApi, wishTreeActivateApi } from '@/api/wishTree';
|
||||
|
||||
export default {
|
||||
name: 'WishTreeList',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryParams: { page: 1, limit: 10, name: '' },
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增许愿树',
|
||||
formData: { id: null, name: '', description: '', cover_image: '', background_image: '', sort: 0, nodes: [] },
|
||||
formRules: { name: [{ required: true, message: '请输入许愿树名称', trigger: 'blur' }] },
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
async getList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await wishTreeListApi(this.queryParams);
|
||||
this.tableData = res.list || [];
|
||||
this.total = res.total || 0;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryParams.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryParams = { page: 1, limit: 10, name: '' };
|
||||
this.getList();
|
||||
},
|
||||
handleAdd() {
|
||||
this.dialogTitle = '新增许愿树';
|
||||
this.formData = { id: null, name: '', description: '', cover_image: '', background_image: '', sort: 0, nodes: [] };
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
async handleEdit(row) {
|
||||
this.dialogTitle = '编辑许愿树';
|
||||
try {
|
||||
const res = await wishTreeInfoApi(row.id);
|
||||
this.formData = { ...res, nodes: res.nodes || [] };
|
||||
this.dialogVisible = true;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleViewMessages(row) {
|
||||
this.$router.push({ path: `/wishTree/tree/detail/${row.id}` });
|
||||
},
|
||||
addNode() {
|
||||
this.formData.nodes.push({ title: '', description: '', open_time: null, sort: 0, status: 1 });
|
||||
},
|
||||
removeNode(index) {
|
||||
this.formData.nodes.splice(index, 1);
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$confirm('确定要删除该许愿树吗?删除后所有节点和用户留言也会被删除。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
try {
|
||||
await wishTreeDeleteApi(row.id);
|
||||
this.$message.success('删除成功');
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleActivate(row) {
|
||||
const action = row.is_active === 1 ? '停用' : '启用';
|
||||
try {
|
||||
await wishTreeActivateApi(row.id);
|
||||
this.$message.success(`${action}成功`);
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.formRef.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
try {
|
||||
const api = this.formData.id ? wishTreeUpdateApi : wishTreeSaveApi;
|
||||
await api(this.formData);
|
||||
this.$message.success(this.formData.id ? '编辑成功' : '新增成功');
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDialogClose() {
|
||||
this.$refs.formRef && this.$refs.formRef.resetFields();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.page = val;
|
||||
this.getList();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.divBox { padding: 20px; }
|
||||
</style>
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package com.zbkj.admin.controller;
|
||||
|
||||
import com.zbkj.common.page.CommonPage;
|
||||
import com.zbkj.common.result.CommonResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 缘池话题用户发布信息管理 Controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/fate/pool/post")
|
||||
@Api(tags = "缘池话题发布信息管理")
|
||||
@Validated
|
||||
public class FatePoolPostController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 发布信息列表
|
||||
*/
|
||||
@ApiOperation(value = "发布信息列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "topic_id", required = false) Integer topicId,
|
||||
@RequestParam(value = "nickname", required = false) String nickname,
|
||||
@RequestParam(value = "status", required = false) Integer status) {
|
||||
try {
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (topicId != null) {
|
||||
whereSql.append(" AND topic_id = ?");
|
||||
params.add(topicId);
|
||||
}
|
||||
if (nickname != null && !nickname.isEmpty()) {
|
||||
whereSql.append(" AND user_nickname LIKE ?");
|
||||
params.add("%" + nickname + "%");
|
||||
}
|
||||
if (status != null) {
|
||||
whereSql.append(" AND status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_fate_pool_topic_post" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
|
||||
// 关联查询话题标题
|
||||
String sql = "SELECT p.*, t.title as topic_title FROM eb_fate_pool_topic_post p " +
|
||||
"LEFT JOIN eb_fate_pool_topic t ON p.topic_id = t.id" +
|
||||
whereSql.toString().replace("topic_id", "p.topic_id").replace("user_nickname", "p.user_nickname").replace("status", "p.status") +
|
||||
" ORDER BY p.id DESC LIMIT ?, ?";
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取发布信息列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布信息详情
|
||||
*/
|
||||
@ApiOperation(value = "发布信息详情")
|
||||
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getInfo(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT * FROM eb_fate_pool_topic_post WHERE id = ?";
|
||||
Map<String, Object> data = jdbcTemplate.queryForMap(sql, id);
|
||||
return CommonResult.success(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取发布信息详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除发布信息
|
||||
*/
|
||||
@ApiOperation(value = "删除发布信息")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
jdbcTemplate.update("DELETE FROM eb_fate_pool_topic_post WHERE id = ?", id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除发布信息失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改发布信息状态
|
||||
*/
|
||||
@ApiOperation(value = "修改发布信息状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateStatus(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "UPDATE eb_fate_pool_topic_post SET status = IF(status = 1, 0, 1), update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("状态修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改发布信息状态失败", e);
|
||||
return CommonResult.failed("状态修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package com.zbkj.admin.controller;
|
||||
|
||||
import com.zbkj.common.page.CommonPage;
|
||||
import com.zbkj.common.result.CommonResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 缘池话题管理 Controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/fate/pool/topic")
|
||||
@Api(tags = "缘池话题管理")
|
||||
@Validated
|
||||
public class FatePoolTopicController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 话题列表
|
||||
*/
|
||||
@ApiOperation(value = "话题列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "title", required = false) String title,
|
||||
@RequestParam(value = "status", required = false) Integer status) {
|
||||
try {
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (title != null && !title.isEmpty()) {
|
||||
whereSql.append(" AND title LIKE ?");
|
||||
params.add("%" + title + "%");
|
||||
}
|
||||
if (status != null) {
|
||||
whereSql.append(" AND status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_fate_pool_topic" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
|
||||
// 查询列表,包含发布数统计
|
||||
String sql = "SELECT t.*, (SELECT COUNT(*) FROM eb_fate_pool_topic_post WHERE topic_id = t.id) as post_count " +
|
||||
"FROM eb_fate_pool_topic t" + whereSql + " ORDER BY t.sort DESC, t.id DESC LIMIT ?, ?";
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取话题列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 话题详情
|
||||
*/
|
||||
@ApiOperation(value = "话题详情")
|
||||
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getInfo(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT * FROM eb_fate_pool_topic WHERE id = ?";
|
||||
Map<String, Object> data = jdbcTemplate.queryForMap(sql, id);
|
||||
return CommonResult.success(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取话题详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增话题
|
||||
*/
|
||||
@ApiOperation(value = "新增话题")
|
||||
@RequestMapping(value = "/save", method = RequestMethod.POST)
|
||||
public CommonResult<String> save(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
String sql = "INSERT INTO eb_fate_pool_topic (title, description, cover_image, sort, status, create_time, update_time) VALUES (?, ?, ?, ?, ?, NOW(), NOW())";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("title"),
|
||||
data.get("description"),
|
||||
data.get("cover_image"),
|
||||
data.get("sort") != null ? data.get("sort") : 0,
|
||||
data.get("status") != null ? data.get("status") : 1);
|
||||
return CommonResult.success("新增成功");
|
||||
} catch (Exception e) {
|
||||
log.error("新增话题失败", e);
|
||||
return CommonResult.failed("新增失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新话题
|
||||
*/
|
||||
@ApiOperation(value = "更新话题")
|
||||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> update(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
String sql = "UPDATE eb_fate_pool_topic SET title = ?, description = ?, cover_image = ?, sort = ?, status = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("title"),
|
||||
data.get("description"),
|
||||
data.get("cover_image"),
|
||||
data.get("sort"),
|
||||
data.get("status"),
|
||||
data.get("id"));
|
||||
return CommonResult.success("更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新话题失败", e);
|
||||
return CommonResult.failed("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除话题
|
||||
*/
|
||||
@ApiOperation(value = "删除话题")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
// 先删除话题下的所有发布信息
|
||||
jdbcTemplate.update("DELETE FROM eb_fate_pool_topic_post WHERE topic_id = ?", id);
|
||||
// 再删除话题
|
||||
jdbcTemplate.update("DELETE FROM eb_fate_pool_topic WHERE id = ?", id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除话题失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改话题状态
|
||||
*/
|
||||
@ApiOperation(value = "修改话题状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateStatus(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "UPDATE eb_fate_pool_topic SET status = IF(status = 1, 0, 1), update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("状态修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改话题状态失败", e);
|
||||
return CommonResult.failed("状态修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
package com.zbkj.admin.controller;
|
||||
|
||||
import com.zbkj.common.page.CommonPage;
|
||||
import com.zbkj.common.result.CommonResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 许愿树管理 Controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/wish/tree")
|
||||
@Api(tags = "许愿树管理")
|
||||
@Validated
|
||||
public class WishTreeController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 许愿树列表
|
||||
*/
|
||||
@ApiOperation(value = "许愿树列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "name", required = false) String name) {
|
||||
try {
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
whereSql.append(" AND name LIKE ?");
|
||||
params.add("%" + name + "%");
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_wish_tree" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
|
||||
// 查询列表,包含节点数和留言数统计
|
||||
String sql = "SELECT t.*, " +
|
||||
"(SELECT COUNT(*) FROM eb_wish_tree_node WHERE tree_id = t.id) as node_count, " +
|
||||
"(SELECT COUNT(*) FROM eb_wish_tree_message WHERE tree_id = t.id) as message_count " +
|
||||
"FROM eb_wish_tree t" + whereSql + " ORDER BY t.sort DESC, t.id DESC LIMIT ?, ?";
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取许愿树列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 许愿树详情(含节点)
|
||||
*/
|
||||
@ApiOperation(value = "许愿树详情")
|
||||
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getInfo(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT * FROM eb_wish_tree WHERE id = ?";
|
||||
Map<String, Object> data = jdbcTemplate.queryForMap(sql, id);
|
||||
|
||||
// 查询节点列表
|
||||
String nodeSql = "SELECT * FROM eb_wish_tree_node WHERE tree_id = ? ORDER BY sort DESC, id ASC";
|
||||
List<Map<String, Object>> nodes = jdbcTemplate.queryForList(nodeSql, id);
|
||||
data.put("nodes", nodes);
|
||||
|
||||
return CommonResult.success(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取许愿树详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增许愿树(含节点)
|
||||
*/
|
||||
@ApiOperation(value = "新增许愿树")
|
||||
@RequestMapping(value = "/save", method = RequestMethod.POST)
|
||||
public CommonResult<String> save(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
// 插入许愿树
|
||||
String sql = "INSERT INTO eb_wish_tree (name, description, cover_image, background_image, sort, is_active, create_time, update_time) VALUES (?, ?, ?, ?, ?, 0, NOW(), NOW())";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("name"),
|
||||
data.get("description"),
|
||||
data.get("cover_image"),
|
||||
data.get("background_image"),
|
||||
data.get("sort") != null ? data.get("sort") : 0);
|
||||
|
||||
// 获取新插入的ID
|
||||
Integer treeId = jdbcTemplate.queryForObject("SELECT LAST_INSERT_ID()", Integer.class);
|
||||
|
||||
// 插入节点
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> nodes = (List<Map<String, Object>>) data.get("nodes");
|
||||
if (nodes != null && !nodes.isEmpty()) {
|
||||
String nodeSql = "INSERT INTO eb_wish_tree_node (tree_id, title, description, icon, open_time, sort, status, create_time, update_time) VALUES (?, ?, ?, ?, ?, ?, 1, NOW(), NOW())";
|
||||
for (Map<String, Object> node : nodes) {
|
||||
jdbcTemplate.update(nodeSql,
|
||||
treeId,
|
||||
node.get("title"),
|
||||
node.get("description"),
|
||||
node.get("icon"),
|
||||
node.get("open_time"),
|
||||
node.get("sort") != null ? node.get("sort") : 0);
|
||||
}
|
||||
}
|
||||
|
||||
return CommonResult.success("新增成功");
|
||||
} catch (Exception e) {
|
||||
log.error("新增许愿树失败", e);
|
||||
return CommonResult.failed("新增失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新许愿树(含节点)
|
||||
*/
|
||||
@ApiOperation(value = "更新许愿树")
|
||||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> update(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
Integer treeId = (Integer) data.get("id");
|
||||
|
||||
// 更新许愿树
|
||||
String sql = "UPDATE eb_wish_tree SET name = ?, description = ?, cover_image = ?, background_image = ?, sort = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("name"),
|
||||
data.get("description"),
|
||||
data.get("cover_image"),
|
||||
data.get("background_image"),
|
||||
data.get("sort"),
|
||||
treeId);
|
||||
|
||||
// 删除旧节点,重新插入
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_node WHERE tree_id = ?", treeId);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> nodes = (List<Map<String, Object>>) data.get("nodes");
|
||||
if (nodes != null && !nodes.isEmpty()) {
|
||||
String nodeSql = "INSERT INTO eb_wish_tree_node (tree_id, title, description, icon, open_time, sort, status, create_time, update_time) VALUES (?, ?, ?, ?, ?, ?, 1, NOW(), NOW())";
|
||||
for (Map<String, Object> node : nodes) {
|
||||
jdbcTemplate.update(nodeSql,
|
||||
treeId,
|
||||
node.get("title"),
|
||||
node.get("description"),
|
||||
node.get("icon"),
|
||||
node.get("open_time"),
|
||||
node.get("sort") != null ? node.get("sort") : 0);
|
||||
}
|
||||
}
|
||||
|
||||
return CommonResult.success("更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新许愿树失败", e);
|
||||
return CommonResult.failed("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除许愿树
|
||||
*/
|
||||
@ApiOperation(value = "删除许愿树")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
// 删除留言
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_message WHERE tree_id = ?", id);
|
||||
// 删除节点
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_node WHERE tree_id = ?", id);
|
||||
// 删除许愿树
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree WHERE id = ?", id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除许愿树失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/停用许愿树
|
||||
*/
|
||||
@ApiOperation(value = "启用/停用许愿树")
|
||||
@RequestMapping(value = "/activate/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> activate(@PathVariable Integer id) {
|
||||
try {
|
||||
// 查询当前状态
|
||||
Integer currentStatus = jdbcTemplate.queryForObject("SELECT is_active FROM eb_wish_tree WHERE id = ?", Integer.class, id);
|
||||
if (currentStatus != null && currentStatus == 1) {
|
||||
// 当前是启用状态,改为停用
|
||||
jdbcTemplate.update("UPDATE eb_wish_tree SET is_active = 0, update_time = NOW() WHERE id = ?", id);
|
||||
} else {
|
||||
// 当前是停用状态,改为启用
|
||||
jdbcTemplate.update("UPDATE eb_wish_tree SET is_active = 1, update_time = NOW() WHERE id = ?", id);
|
||||
}
|
||||
return CommonResult.success("操作成功");
|
||||
} catch (Exception e) {
|
||||
log.error("启用/停用许愿树失败", e);
|
||||
return CommonResult.failed("操作失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
package com.zbkj.admin.controller;
|
||||
|
||||
import com.zbkj.common.page.CommonPage;
|
||||
import com.zbkj.common.result.CommonResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 许愿树用户留言管理 Controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/wish/tree/message")
|
||||
@Api(tags = "许愿树用户留言管理")
|
||||
@Validated
|
||||
public class WishTreeMessageController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 留言列表
|
||||
*/
|
||||
@ApiOperation(value = "留言列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "tree_id", required = false) Integer treeId,
|
||||
@RequestParam(value = "node_id", required = false) Integer nodeId,
|
||||
@RequestParam(value = "nickname", required = false) String nickname,
|
||||
@RequestParam(value = "status", required = false) Integer status) {
|
||||
try {
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (treeId != null) {
|
||||
whereSql.append(" AND m.tree_id = ?");
|
||||
params.add(treeId);
|
||||
}
|
||||
if (nodeId != null) {
|
||||
whereSql.append(" AND m.node_id = ?");
|
||||
params.add(nodeId);
|
||||
}
|
||||
if (nickname != null && !nickname.isEmpty()) {
|
||||
whereSql.append(" AND m.user_nickname LIKE ?");
|
||||
params.add("%" + nickname + "%");
|
||||
}
|
||||
if (status != null) {
|
||||
whereSql.append(" AND m.status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_wish_tree_message m" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
|
||||
// 查询列表,关联节点标题
|
||||
String sql = "SELECT m.*, n.title as node_title " +
|
||||
"FROM eb_wish_tree_message m " +
|
||||
"LEFT JOIN eb_wish_tree_node n ON m.node_id = n.id" +
|
||||
whereSql + " ORDER BY m.id DESC LIMIT ?, ?";
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取留言列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 留言详情
|
||||
*/
|
||||
@ApiOperation(value = "留言详情")
|
||||
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getInfo(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT m.*, n.title as node_title FROM eb_wish_tree_message m " +
|
||||
"LEFT JOIN eb_wish_tree_node n ON m.node_id = n.id WHERE m.id = ?";
|
||||
Map<String, Object> data = jdbcTemplate.queryForMap(sql, id);
|
||||
return CommonResult.success(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取留言详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除留言
|
||||
*/
|
||||
@ApiOperation(value = "删除留言")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_message WHERE id = ?", id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除留言失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改留言状态
|
||||
*/
|
||||
@ApiOperation(value = "修改留言状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateStatus(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "UPDATE eb_wish_tree_message SET status = IF(status = 1, 0, 1), update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("状态修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改留言状态失败", e);
|
||||
return CommonResult.failed("状态修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
package com.zbkj.admin.controller;
|
||||
|
||||
import com.zbkj.common.page.CommonPage;
|
||||
import com.zbkj.common.result.CommonResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 许愿树节点管理 Controller
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/admin/wish/tree/node")
|
||||
@Api(tags = "许愿树节点管理")
|
||||
@Validated
|
||||
public class WishTreeNodeController {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
/**
|
||||
* 节点列表
|
||||
*/
|
||||
@ApiOperation(value = "节点列表")
|
||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||
public CommonResult<CommonPage<Map<String, Object>>> getList(
|
||||
@RequestParam(value = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(value = "limit", defaultValue = "10") Integer limit,
|
||||
@RequestParam(value = "tree_id", required = false) Integer treeId,
|
||||
@RequestParam(value = "title", required = false) String title,
|
||||
@RequestParam(value = "status", required = false) Integer status) {
|
||||
try {
|
||||
StringBuilder whereSql = new StringBuilder(" WHERE 1=1");
|
||||
List<Object> params = new ArrayList<>();
|
||||
|
||||
if (treeId != null) {
|
||||
whereSql.append(" AND n.tree_id = ?");
|
||||
params.add(treeId);
|
||||
}
|
||||
if (title != null && !title.isEmpty()) {
|
||||
whereSql.append(" AND n.title LIKE ?");
|
||||
params.add("%" + title + "%");
|
||||
}
|
||||
if (status != null) {
|
||||
whereSql.append(" AND n.status = ?");
|
||||
params.add(status);
|
||||
}
|
||||
|
||||
String countSql = "SELECT COUNT(*) FROM eb_wish_tree_node n" + whereSql;
|
||||
Integer total = jdbcTemplate.queryForObject(countSql, params.toArray(), Integer.class);
|
||||
|
||||
String sql = "SELECT n.*, t.name as tree_name, " +
|
||||
"(SELECT COUNT(*) FROM eb_wish_tree_message WHERE node_id = n.id) as message_count " +
|
||||
"FROM eb_wish_tree_node n " +
|
||||
"LEFT JOIN eb_wish_tree t ON n.tree_id = t.id" +
|
||||
whereSql + " ORDER BY n.tree_id, n.sort DESC, n.id ASC LIMIT ?, ?";
|
||||
params.add((page - 1) * limit);
|
||||
params.add(limit);
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params.toArray());
|
||||
|
||||
CommonPage<Map<String, Object>> result = new CommonPage<>();
|
||||
result.setList(list);
|
||||
result.setTotal(total != null ? total.longValue() : 0L);
|
||||
result.setPage(page);
|
||||
result.setLimit(limit);
|
||||
result.setTotalPage((int) Math.ceil((double) (total != null ? total : 0) / limit));
|
||||
|
||||
return CommonResult.success(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取节点列表失败", e);
|
||||
return CommonResult.failed("获取列表失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点详情
|
||||
*/
|
||||
@ApiOperation(value = "节点详情")
|
||||
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
|
||||
public CommonResult<Map<String, Object>> getInfo(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "SELECT n.*, t.name as tree_name FROM eb_wish_tree_node n " +
|
||||
"LEFT JOIN eb_wish_tree t ON n.tree_id = t.id WHERE n.id = ?";
|
||||
Map<String, Object> data = jdbcTemplate.queryForMap(sql, id);
|
||||
return CommonResult.success(data);
|
||||
} catch (Exception e) {
|
||||
log.error("获取节点详情失败", e);
|
||||
return CommonResult.failed("获取详情失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增节点
|
||||
*/
|
||||
@ApiOperation(value = "新增节点")
|
||||
@RequestMapping(value = "/save", method = RequestMethod.POST)
|
||||
public CommonResult<String> save(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
String sql = "INSERT INTO eb_wish_tree_node (tree_id, title, description, icon, open_time, sort, status, create_time, update_time) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("tree_id"),
|
||||
data.get("title"),
|
||||
data.get("description"),
|
||||
data.get("icon"),
|
||||
data.get("open_time"),
|
||||
data.get("sort") != null ? data.get("sort") : 0,
|
||||
data.get("status") != null ? data.get("status") : 1);
|
||||
return CommonResult.success("新增成功");
|
||||
} catch (Exception e) {
|
||||
log.error("新增节点失败", e);
|
||||
return CommonResult.failed("新增失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新节点
|
||||
*/
|
||||
@ApiOperation(value = "更新节点")
|
||||
@RequestMapping(value = "/update", method = RequestMethod.POST)
|
||||
public CommonResult<String> update(@RequestBody Map<String, Object> data) {
|
||||
try {
|
||||
String sql = "UPDATE eb_wish_tree_node SET tree_id = ?, title = ?, description = ?, icon = ?, open_time = ?, sort = ?, status = ?, update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql,
|
||||
data.get("tree_id"),
|
||||
data.get("title"),
|
||||
data.get("description"),
|
||||
data.get("icon"),
|
||||
data.get("open_time"),
|
||||
data.get("sort"),
|
||||
data.get("status"),
|
||||
data.get("id"));
|
||||
return CommonResult.success("更新成功");
|
||||
} catch (Exception e) {
|
||||
log.error("更新节点失败", e);
|
||||
return CommonResult.failed("更新失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除节点
|
||||
*/
|
||||
@ApiOperation(value = "删除节点")
|
||||
@RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> delete(@PathVariable Integer id) {
|
||||
try {
|
||||
// 先删除节点下的所有留言
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_message WHERE node_id = ?", id);
|
||||
// 再删除节点
|
||||
jdbcTemplate.update("DELETE FROM eb_wish_tree_node WHERE id = ?", id);
|
||||
return CommonResult.success("删除成功");
|
||||
} catch (Exception e) {
|
||||
log.error("删除节点失败", e);
|
||||
return CommonResult.failed("删除失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改节点状态
|
||||
*/
|
||||
@ApiOperation(value = "修改节点状态")
|
||||
@RequestMapping(value = "/status/{id}", method = RequestMethod.POST)
|
||||
public CommonResult<String> updateStatus(@PathVariable Integer id) {
|
||||
try {
|
||||
String sql = "UPDATE eb_wish_tree_node SET status = IF(status = 1, 0, 1), update_time = NOW() WHERE id = ?";
|
||||
jdbcTemplate.update(sql, id);
|
||||
return CommonResult.success("状态修改成功");
|
||||
} catch (Exception e) {
|
||||
log.error("修改节点状态失败", e);
|
||||
return CommonResult.failed("状态修改失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
320
缘池与许愿树管理端功能总结.md
Normal file
320
缘池与许愿树管理端功能总结.md
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
# 缘池与许愿树管理端功能总结
|
||||
|
||||
> 更新日期:2025-12-30
|
||||
|
||||
---
|
||||
|
||||
## 一、文件结构
|
||||
|
||||
### 1.1 前端文件
|
||||
|
||||
```
|
||||
Zhibo/admin/src/
|
||||
├── api/
|
||||
│ ├── fatePool.js # 缘池API
|
||||
│ └── wishTree.js # 许愿树API
|
||||
├── router/modules/
|
||||
│ ├── fatePool.js # 缘池路由
|
||||
│ └── wishTree.js # 许愿树路由
|
||||
└── views/
|
||||
├── fatePool/ # 缘池页面
|
||||
│ ├── topic/
|
||||
│ │ ├── index.vue # 话题管理
|
||||
│ │ └── post.vue # 话题发布详情
|
||||
│ └── topicUser/
|
||||
│ └── index.vue # 用户发布列表
|
||||
└── wishTree/ # 许愿树页面
|
||||
├── tree/
|
||||
│ ├── index.vue # 许愿树列表
|
||||
│ └── detail.vue # 许愿树留言详情
|
||||
├── node/
|
||||
│ └── index.vue # 节点管理
|
||||
└── message/
|
||||
└── index.vue # 用户留言列表
|
||||
```
|
||||
|
||||
### 1.2 后端文件
|
||||
|
||||
```
|
||||
Zhibo/zhibo-h/crmeb-admin/src/main/java/com/zbkj/admin/controller/
|
||||
├── FatePoolTopicController.java # 缘池话题管理
|
||||
├── FatePoolPostController.java # 缘池用户发布管理
|
||||
├── WishTreeController.java # 许愿树管理
|
||||
├── WishTreeNodeController.java # 许愿树节点管理
|
||||
└── WishTreeMessageController.java # 许愿树留言管理
|
||||
```
|
||||
|
||||
### 1.3 数据库表
|
||||
|
||||
```
|
||||
eb_fate_pool_topic # 缘池话题表
|
||||
eb_fate_pool_topic_post # 缘池用户发布表
|
||||
eb_wish_tree # 许愿树表
|
||||
eb_wish_tree_node # 许愿树节点表
|
||||
eb_wish_tree_message # 许愿树留言表
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、缘池功能
|
||||
|
||||
### 2.1 话题管理
|
||||
|
||||
**页面路径:** `/fatePool/topic`
|
||||
**页面文件:** `views/fatePool/topic/index.vue`
|
||||
|
||||
| 功能 | 说明 |
|
||||
|-----|------|
|
||||
| 话题列表 | 分页展示话题,显示封面、标题、描述、发布数 |
|
||||
| 搜索 | 按标题模糊搜索、按状态筛选 |
|
||||
| 新增话题 | 填写标题、描述、封面图URL、排序、状态 |
|
||||
| 编辑话题 | 修改话题信息 |
|
||||
| 删除话题 | 删除话题及其下所有发布信息 |
|
||||
| 状态切换 | 启用/禁用话题 |
|
||||
| 查看发布 | 跳转到该话题的用户发布列表 |
|
||||
|
||||
**接口列表:**
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| 话题列表 | GET | `/api/admin/fate/pool/topic/list` | 分页查询 |
|
||||
| 话题详情 | GET | `/api/admin/fate/pool/topic/info/{id}` | 获取单个 |
|
||||
| 新增话题 | POST | `/api/admin/fate/pool/topic/save` | 创建 |
|
||||
| 更新话题 | POST | `/api/admin/fate/pool/topic/update` | 修改 |
|
||||
| 删除话题 | POST | `/api/admin/fate/pool/topic/delete/{id}` | 删除 |
|
||||
| 修改状态 | POST | `/api/admin/fate/pool/topic/status/{id}` | 切换状态 |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 用户发布管理
|
||||
|
||||
**页面路径:** `/fatePool/topicUser`
|
||||
**页面文件:** `views/fatePool/topicUser/index.vue`
|
||||
|
||||
| 功能 | 说明 |
|
||||
|-----|------|
|
||||
| 发布列表 | 分页展示用户发布信息 |
|
||||
| 搜索 | 按话题筛选、按昵称搜索、按状态筛选 |
|
||||
| 查看详情 | 弹窗展示完整内容和图片 |
|
||||
| 删除发布 | 删除用户发布信息 |
|
||||
| 状态切换 | 显示/隐藏发布信息 |
|
||||
|
||||
**接口列表:**
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| 发布列表 | GET | `/api/admin/fate/pool/post/list` | 分页查询 |
|
||||
| 发布详情 | GET | `/api/admin/fate/pool/post/info/{id}` | 获取单个 |
|
||||
| 删除发布 | POST | `/api/admin/fate/pool/post/delete/{id}` | 删除 |
|
||||
| 修改状态 | POST | `/api/admin/fate/pool/post/status/{id}` | 切换状态 |
|
||||
|
||||
---
|
||||
|
||||
## 三、许愿树功能
|
||||
|
||||
### 3.1 许愿树管理
|
||||
|
||||
**页面路径:** `/wishTree/tree`
|
||||
**页面文件:** `views/wishTree/tree/index.vue`
|
||||
|
||||
| 功能 | 说明 |
|
||||
|-----|------|
|
||||
| 许愿树列表 | 分页展示许愿树,显示封面、名称、节点数、留言数 |
|
||||
| 搜索 | 按名称模糊搜索 |
|
||||
| 新增许愿树 | 填写名称、描述、封面图、背景图、排序,可同时添加节点 |
|
||||
| 编辑许愿树 | 修改许愿树信息和节点 |
|
||||
| 删除许愿树 | 删除许愿树及其所有节点和留言 |
|
||||
| 启用/停用 | 设置许愿树启用状态 |
|
||||
| 查看留言 | 跳转到该许愿树的留言列表 |
|
||||
|
||||
**接口列表:**
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| 许愿树列表 | GET | `/api/admin/wish/tree/list` | 分页查询 |
|
||||
| 许愿树详情 | GET | `/api/admin/wish/tree/info/{id}` | 获取详情(含节点) |
|
||||
| 新增许愿树 | POST | `/api/admin/wish/tree/save` | 创建(含节点) |
|
||||
| 更新许愿树 | POST | `/api/admin/wish/tree/update` | 修改(含节点) |
|
||||
| 删除许愿树 | POST | `/api/admin/wish/tree/delete/{id}` | 删除 |
|
||||
| 启用/停用 | POST | `/api/admin/wish/tree/activate/{id}` | 切换启用状态 |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 节点管理
|
||||
|
||||
**页面路径:** `/wishTree/node`
|
||||
**页面文件:** `views/wishTree/node/index.vue`
|
||||
|
||||
| 功能 | 说明 |
|
||||
|-----|------|
|
||||
| 节点列表 | 分页展示所有节点,显示所属许愿树、标题、开启时间、留言数 |
|
||||
| 搜索 | 按许愿树筛选、按标题搜索、按状态筛选 |
|
||||
| 新增节点 | 选择许愿树、填写标题、描述、图标、开启时间、排序 |
|
||||
| 编辑节点 | 修改节点信息 |
|
||||
| 删除节点 | 删除节点及其所有留言 |
|
||||
| 状态切换 | 启用/禁用节点 |
|
||||
|
||||
**接口列表:**
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| 节点列表 | GET | `/api/admin/wish/tree/node/list` | 分页查询 |
|
||||
| 节点详情 | GET | `/api/admin/wish/tree/node/info/{id}` | 获取单个 |
|
||||
| 新增节点 | POST | `/api/admin/wish/tree/node/save` | 创建 |
|
||||
| 更新节点 | POST | `/api/admin/wish/tree/node/update` | 修改 |
|
||||
| 删除节点 | POST | `/api/admin/wish/tree/node/delete/{id}` | 删除 |
|
||||
| 修改状态 | POST | `/api/admin/wish/tree/node/status/{id}` | 切换状态 |
|
||||
|
||||
---
|
||||
|
||||
### 3.3 用户留言管理
|
||||
|
||||
**页面路径:** `/wishTree/message`
|
||||
**页面文件:** `views/wishTree/message/index.vue`
|
||||
|
||||
| 功能 | 说明 |
|
||||
|-----|------|
|
||||
| 留言列表 | 分页展示用户留言,显示节点、用户信息、内容、匿名状态 |
|
||||
| 搜索 | 按许愿树/节点筛选、按昵称搜索、按状态筛选 |
|
||||
| 查看详情 | 弹窗展示完整内容和图片 |
|
||||
| 删除留言 | 删除用户留言 |
|
||||
| 状态切换 | 显示/隐藏留言 |
|
||||
|
||||
**接口列表:**
|
||||
|
||||
| 接口 | 方法 | 路径 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| 留言列表 | GET | `/api/admin/wish/tree/message/list` | 分页查询 |
|
||||
| 留言详情 | GET | `/api/admin/wish/tree/message/info/{id}` | 获取单个 |
|
||||
| 删除留言 | POST | `/api/admin/wish/tree/message/delete/{id}` | 删除 |
|
||||
| 修改状态 | POST | `/api/admin/wish/tree/message/status/{id}` | 切换状态 |
|
||||
|
||||
---
|
||||
|
||||
## 四、接口参数说明
|
||||
|
||||
### 4.1 通用分页参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|-----|------|------|------|
|
||||
| page | Integer | 否 | 页码,默认1 |
|
||||
| limit | Integer | 否 | 每页数量,默认10 |
|
||||
|
||||
### 4.2 缘池话题接口
|
||||
|
||||
**列表查询参数:**
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"title": "关键词",
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
**新增/更新参数:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"title": "话题标题",
|
||||
"description": "话题描述",
|
||||
"cover_image": "https://xxx.com/cover.jpg",
|
||||
"sort": 100,
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 许愿树接口
|
||||
|
||||
**新增/更新参数(含节点):**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "许愿树名称",
|
||||
"description": "描述",
|
||||
"cover_image": "https://xxx.com/cover.jpg",
|
||||
"background_image": "https://xxx.com/bg.jpg",
|
||||
"sort": 100,
|
||||
"nodes": [
|
||||
{
|
||||
"title": "节点标题",
|
||||
"description": "节点描述",
|
||||
"icon": "https://xxx.com/icon.png",
|
||||
"open_time": "2026-01-01 00:00:00",
|
||||
"sort": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 节点接口
|
||||
|
||||
**列表查询参数:**
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"tree_id": 1,
|
||||
"title": "关键词",
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
**新增/更新参数:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"tree_id": 1,
|
||||
"title": "节点标题",
|
||||
"description": "节点描述",
|
||||
"icon": "https://xxx.com/icon.png",
|
||||
"open_time": "2026-01-01 00:00:00",
|
||||
"sort": 0,
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 留言接口
|
||||
|
||||
**列表查询参数:**
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"tree_id": 1,
|
||||
"node_id": 1,
|
||||
"nickname": "昵称",
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、菜单入口
|
||||
|
||||
管理后台左侧菜单:
|
||||
|
||||
```
|
||||
├── 缘池
|
||||
│ ├── 话题管理 /fatePool/topic
|
||||
│ └── 用户发布 /fatePool/topicUser
|
||||
└── 许愿树
|
||||
├── 许愿树列表 /wishTree/tree
|
||||
├── 留言节点 /wishTree/node
|
||||
└── 用户留言 /wishTree/message
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
1. **响应格式**:后端返回 `{code: 200, message: null, data: ...}`,前端 axios 拦截器会直接返回 `data` 部分
|
||||
|
||||
2. **图片字段**:`images` 字段存储为 JSON 字符串,前端需要解析
|
||||
|
||||
3. **开启时间**:节点的 `open_time` 为空表示立即开启
|
||||
|
||||
4. **级联删除**:
|
||||
- 删除话题会同时删除该话题下所有发布信息
|
||||
- 删除许愿树会同时删除所有节点和留言
|
||||
- 删除节点会同时删除该节点下所有留言
|
||||
Loading…
Reference in New Issue
Block a user