feat: 新增积分外部页面(免认证三页 + 配套基础设施)
前端: - 新增 EmptyLayout 空壳布局(无侧边栏/导航) - 新增 requestNoAuth Axios 实例(不注入 token) - 新增 integralExternal 路由模块(/integral-external/*) - permission.js 加入 whiteListPrefixes 前缀白名单跳过登录 - 新增 phoneDesensitize 手机号脱敏过滤器 - 新增三个免认证页面: · 积分订单页(/integral-external/order) · 用户积分页(/integral-external/user,手机号脱敏) · 用户积分明细子页(/integral-external/user/integral-detail) 后端: - 新增 ExternalIntegralController(无 @PreAuthorize) · GET /api/external/integral/order/list · GET /api/external/integral/user/list · POST /api/external/integral/log/list - WebSecurityConfig 加入 /api/external/integral/** permitAll 文档与工具: - 新增 coding plan、schedule、测试报告 - 新增 start-backend.sh / start-frontend.sh 本地启动脚本 - 新增 .mvn/wrapper/maven-wrapper.properties Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
206
backend-adminend/src/views/integral-external/user/index.vue
Normal file
206
backend-adminend/src/views/integral-external/user/index.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="divBox relative">
|
||||
<el-card class="box-card">
|
||||
<div class="clearfix">
|
||||
<div class="container">
|
||||
<el-form inline size="small" :model="userFrom" label-width="90px">
|
||||
<el-row>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="用户搜索:">
|
||||
<el-input
|
||||
v-model="userFrom.keywords"
|
||||
placeholder="请输入昵称或手机号"
|
||||
clearable
|
||||
class="selWidth"
|
||||
@keyup.enter.native="seachList"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item label="时间选择:">
|
||||
<el-date-picker
|
||||
v-model="timeVal"
|
||||
value-format="yyyy-MM-dd"
|
||||
format="yyyy-MM-dd"
|
||||
size="small"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
@change="onchangeTime"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="small" @click="seachList">搜索</el-button>
|
||||
<el-button size="small" @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="tableData.data"
|
||||
size="mini"
|
||||
highlight-current-row
|
||||
:header-cell-style="{ fontWeight: 'bold' }"
|
||||
>
|
||||
<el-table-column label="用户信息" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<div class="user-info-cell">
|
||||
<el-avatar :size="36" :src="scope.row.avatar" icon="el-icon-user" class="mr10" />
|
||||
<div>
|
||||
<div>{{ scope.row.nickname || '-' }}</div>
|
||||
<div class="uid-text">UID: {{ scope.row.uid }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" min-width="130">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.phone | phoneDesensitize }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="积分" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<span class="integral-val">{{ scope.row.integral != null ? scope.row.integral : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="个人奖金" min-width="110">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.selfBonus != null ? ('¥' + scope.row.selfBonus) : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="注册时间" min-width="150" />
|
||||
<el-table-column label="操作" min-width="120" fixed="right" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" @click="viewIntegralDetail(scope.row)">
|
||||
查看积分明细
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="block mt20">
|
||||
<el-pagination
|
||||
:page-sizes="[15, 30, 45, 60]"
|
||||
:page-size="userFrom.limit"
|
||||
:current-page="userFrom.page"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getExternalUserList } from '@/api/integralExternal';
|
||||
|
||||
export default {
|
||||
name: 'IntegralExternalUser',
|
||||
data() {
|
||||
return {
|
||||
listLoading: false,
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0,
|
||||
},
|
||||
userFrom: {
|
||||
keywords: '',
|
||||
dateLimit: '',
|
||||
page: 1,
|
||||
limit: 15,
|
||||
},
|
||||
timeVal: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.listLoading = true;
|
||||
const params = { ...this.userFrom };
|
||||
if (!params.keywords) delete params.keywords;
|
||||
if (!params.dateLimit) delete params.dateLimit;
|
||||
|
||||
getExternalUserList(params)
|
||||
.then((res) => {
|
||||
this.tableData.data = res.list || [];
|
||||
this.tableData.total = res.total || 0;
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
seachList() {
|
||||
this.userFrom.page = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.userFrom = { keywords: '', dateLimit: '', page: 1, limit: 15 };
|
||||
this.timeVal = [];
|
||||
this.getList();
|
||||
},
|
||||
onchangeTime(e) {
|
||||
this.timeVal = e;
|
||||
this.userFrom.dateLimit = e ? e.join(',') : '';
|
||||
this.seachList();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.userFrom.limit = val;
|
||||
this.getList();
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.userFrom.page = val;
|
||||
this.getList();
|
||||
},
|
||||
viewIntegralDetail(row) {
|
||||
this.$router.push({
|
||||
path: '/integral-external/user/integral-detail',
|
||||
query: {
|
||||
uid: row.uid,
|
||||
nickname: row.nickname || '',
|
||||
integral: row.integral != null ? row.integral : '',
|
||||
selfBonus: row.selfBonus != null ? row.selfBonus : '',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.user-info-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.uid-text {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
.integral-val {
|
||||
font-weight: bold;
|
||||
color: #e6a23c;
|
||||
}
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.block {
|
||||
text-align: right;
|
||||
}
|
||||
.selWidth {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user