mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-11 03:18:56 +08:00
feat(manager): 优化列表操作列规范、会员分组统计、漏斗图重构与订单交互
1. 订单交互:移除 orderList 缓存机制修复默认带入订单号问题,支持 query 联动; 2. 会员模块:会员分组新增人数统计与联动,全量规范会员提现、充值、日志等操作列样式; 3. 商品与销量:虚拟销量优化首列缩略图与对齐方式,规范品牌与商品分组操作; 4. 数据统计:重构交易转化漏斗图为自适应 SVG 渐变图与指标卡片; 5. 系统与营销:统一菜单/部门操作列样式,优化 OSS 模块、营销活动与首页待办对齐。
This commit is contained in:
@@ -1,20 +1,29 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="member-recharge-page search">
|
||||
<!-- 顶部筛选表单 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="客户ID" prop="memberId">
|
||||
<el-input
|
||||
v-model="searchForm.memberId"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="memberName">
|
||||
<el-input
|
||||
v-model="searchForm.memberName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="充值单号" prop="rechargeSn">
|
||||
@@ -22,59 +31,106 @@
|
||||
v-model="searchForm.rechargeSn"
|
||||
placeholder="请输入充值单号"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 190px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付时间">
|
||||
<el-form-item label="充值时间">
|
||||
<el-date-picker
|
||||
v-model="selectDate"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
clearable
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
placeholder="选择起始时间"
|
||||
style="width: 360px"
|
||||
style="width: 320px"
|
||||
@change="selectDateRange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card>
|
||||
|
||||
<!-- 充值数据表格 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>会员充值记录</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getDataList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
ref="table"
|
||||
class="mt_10"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="rechargeSn" label="订单号" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="充值金额" width="160" sortable>
|
||||
<!-- 客户ID -->
|
||||
<el-table-column prop="memberId" label="客户ID" min-width="170" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="140" show-overflow-tooltip />
|
||||
|
||||
<!-- 充值单号 -->
|
||||
<el-table-column prop="rechargeSn" label="充值单号" min-width="180" show-overflow-tooltip />
|
||||
|
||||
<!-- 充值金额 -->
|
||||
<el-table-column label="充值金额" width="140" align="right" sortable prop="rechargeMoney">
|
||||
<template #default="{ row }">
|
||||
<priceColorScheme v-if="row" :value="row.rechargeMoney" :color="$mainColor" unit="+" />
|
||||
<span v-if="row" class="recharge-money-text">
|
||||
+¥ {{ formatMoney(row.rechargeMoney) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="充值方式" width="120">
|
||||
|
||||
<!-- 充值方式 -->
|
||||
<el-table-column label="充值方式" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ rechargeWayText(row.rechargeWay) }}</span>
|
||||
{{ row ? rechargeWayText(row.rechargeWay) : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付状态" width="120">
|
||||
|
||||
<!-- 支付状态 -->
|
||||
<el-table-column label="支付状态" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row" :type="row.payStatus === 'PAID' ? 'success' : 'danger'">
|
||||
{{ row.payStatus === "PAID" ? "已付款" : "未付款" }}
|
||||
</el-tag>
|
||||
{{ row ? (row.payStatus === "PAID" ? "已付款" : "未付款") : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 第三方流水号 -->
|
||||
<el-table-column prop="receivableNo" label="第三方流水号" min-width="180" show-overflow-tooltip />
|
||||
|
||||
<!-- 充值时间 -->
|
||||
<el-table-column prop="createTime" label="充值时间" width="170" align="center" />
|
||||
|
||||
<!-- 支付时间 -->
|
||||
<el-table-column prop="payTime" label="支付时间" width="170" align="center" />
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="110" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<a v-if="row.memberId" class="link-text" @click="viewMemberDetail(row)">客户详情</a>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="充值时间" width="190" />
|
||||
<el-table-column prop="payTime" label="支付时间" width="190" />
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -91,49 +147,85 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { getUserRecharge } from "@/api/member";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "recharge" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const selectDate = ref(null);
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(
|
||||
|
||||
const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
|
||||
getUserRecharge,
|
||||
{
|
||||
pageSize: 20,
|
||||
immediate: false,
|
||||
form: { sort: "createTime", order: "desc", startDate: "", endDate: "", memberName: "" },
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
memberId: "",
|
||||
memberName: "",
|
||||
rechargeSn: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
beforeRequest: (p) => {
|
||||
const params = { ...p };
|
||||
if (selectDate.value && selectDate.value.length === 2) {
|
||||
params.startDate = selectDate.value[0];
|
||||
params.endDate = selectDate.value[1];
|
||||
} else {
|
||||
params.startDate = "";
|
||||
params.endDate = "";
|
||||
}
|
||||
return params;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function rechargeWayText(way) {
|
||||
if (way === "ALIPAY") return "支付宝";
|
||||
if (way === "WECHAT") return "微信";
|
||||
if (way === "WECHAT") return "微信支付";
|
||||
if (way === "BANK_TRANSFER") return "线下转账";
|
||||
return "";
|
||||
return way || "-";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
|
||||
function formatMoney(val) {
|
||||
const num = Number(val || 0);
|
||||
return num.toFixed(2);
|
||||
}
|
||||
|
||||
function viewMemberDetail(row) {
|
||||
if (!row || !row.memberId) return;
|
||||
customRouterPush({ name: "member-detail", query: { id: row.memberId } });
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
selectDate.value = null;
|
||||
searchForm.memberId = "";
|
||||
searchForm.memberName = "";
|
||||
searchForm.rechargeSn = "";
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
searchForm.pageNumber = 1;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
@@ -145,6 +237,84 @@ function selectDateRange(v) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.member-recharge-page {
|
||||
padding: 16px 20px;
|
||||
background-color: #f6f8fa;
|
||||
min-height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 筛选卡片 */
|
||||
.filter-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 16px 2px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格卡片 */
|
||||
.table-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recharge-money-text {
|
||||
font-size: 14px;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,75 +1,123 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="member-wallet-log-page search">
|
||||
<!-- 顶部筛选表单 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="客户ID" prop="memberId">
|
||||
<el-input
|
||||
v-model="searchForm.memberId"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="memberName">
|
||||
<el-input
|
||||
v-model="searchForm.memberName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付时间">
|
||||
<el-form-item label="变动时间">
|
||||
<el-date-picker
|
||||
v-model="selectDate"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
clearable
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
placeholder="选择起始时间"
|
||||
style="width: 360px"
|
||||
style="width: 320px"
|
||||
@change="selectDateRange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card>
|
||||
|
||||
<!-- 变动记录表格 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>预存款变动明细</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getDataList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
ref="table"
|
||||
class="mt_10"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="100" />
|
||||
<el-table-column label="变动金额" width="150">
|
||||
<!-- 客户ID -->
|
||||
<el-table-column prop="memberId" label="客户ID" min-width="190" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="140" show-overflow-tooltip />
|
||||
|
||||
<!-- 变动金额 -->
|
||||
<el-table-column label="变动金额" width="140" align="right" sortable prop="money">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<priceColorScheme
|
||||
v-if="row.money > 0"
|
||||
:value="row.money"
|
||||
color="green"
|
||||
/>
|
||||
<priceColorScheme
|
||||
v-else-if="row.money < 0"
|
||||
:value="row.money"
|
||||
:color="$mainColor"
|
||||
/>
|
||||
<span v-if="row.money > 0" class="money-increase">
|
||||
+¥ {{ formatMoney(row.money) }}
|
||||
</span>
|
||||
<span v-else-if="row.money < 0" class="money-decrease">
|
||||
-¥ {{ formatMoney(Math.abs(row.money)) }}
|
||||
</span>
|
||||
<span v-else class="money-zero">
|
||||
¥ 0.00
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="变更时间" width="200" />
|
||||
<el-table-column label="业务类型" width="200">
|
||||
|
||||
<!-- 业务类型 -->
|
||||
<el-table-column label="业务类型" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ serviceTypeText(row.serviceType) }}</span>
|
||||
{{ row ? serviceTypeText(row.serviceType) : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 日志详细说明 -->
|
||||
<el-table-column prop="detail" label="变动说明" min-width="260" show-overflow-tooltip />
|
||||
|
||||
<!-- 变动时间 -->
|
||||
<el-table-column prop="createTime" label="变动时间" width="170" align="center" />
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="110" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<a v-if="row.memberId" class="link-text" @click="viewMemberDetail(row)">客户详情</a>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="detail" label="详细" min-width="300" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -86,23 +134,42 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { getUserWallet } from "@/api/member";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "walletLog" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const selectDate = ref(null);
|
||||
const { loading, data, total, searchForm, getDataList } = useTable(
|
||||
|
||||
const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
|
||||
getUserWallet,
|
||||
{
|
||||
pageSize: 20,
|
||||
immediate: false,
|
||||
form: { sort: "createTime", order: "desc", startDate: "", endDate: "", memberName: "" },
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
memberId: "",
|
||||
memberName: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
beforeRequest: (p) => {
|
||||
const params = { ...p };
|
||||
if (selectDate.value && selectDate.value.length === 2) {
|
||||
params.startDate = selectDate.value[0];
|
||||
params.endDate = selectDate.value[1];
|
||||
} else {
|
||||
params.startDate = "";
|
||||
params.endDate = "";
|
||||
}
|
||||
return params;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -113,23 +180,46 @@ function serviceTypeText(type) {
|
||||
if (type === "WALLET_RECHARGE") return "余额充值";
|
||||
return "佣金提成";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
|
||||
function getServiceTagType(type) {
|
||||
if (type === "WALLET_RECHARGE") return "success";
|
||||
if (type === "WALLET_REFUND") return "primary";
|
||||
if (type === "WALLET_PAY") return "warning";
|
||||
if (type === "WALLET_WITHDRAWAL") return "danger";
|
||||
return "info";
|
||||
}
|
||||
|
||||
function formatMoney(val) {
|
||||
const num = Number(val || 0);
|
||||
return num.toFixed(2);
|
||||
}
|
||||
|
||||
function viewMemberDetail(row) {
|
||||
if (!row || !row.memberId) return;
|
||||
customRouterPush({ name: "member-detail", query: { id: row.memberId } });
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
selectDate.value = null;
|
||||
searchForm.memberId = "";
|
||||
searchForm.memberName = "";
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
searchForm.pageNumber = 1;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
@@ -141,6 +231,93 @@ function selectDateRange(v) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.member-wallet-log-page {
|
||||
padding: 16px 20px;
|
||||
background-color: #f6f8fa;
|
||||
min-height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 筛选卡片 */
|
||||
.filter-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 16px 2px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格卡片 */
|
||||
.table-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.money-increase {
|
||||
font-size: 14px;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.money-decrease {
|
||||
font-size: 14px;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.money-zero {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="member-withdraw-apply-page search">
|
||||
<!-- 顶部筛选表单 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="客户ID" prop="memberId">
|
||||
<el-input
|
||||
v-model="searchForm.memberId"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="memberName">
|
||||
<el-input
|
||||
v-model="searchForm.memberName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请编号" prop="sn">
|
||||
<el-input
|
||||
v-model="searchForm.sn"
|
||||
placeholder="请输入申请编号"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="提现状态" prop="applyStatus">
|
||||
<el-select v-model="searchForm.applyStatus" clearable style="width: 240px">
|
||||
<el-select
|
||||
v-model="searchForm.applyStatus"
|
||||
placeholder="全部状态"
|
||||
clearable
|
||||
style="width: 130px"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="申请中" value="APPLY" />
|
||||
<el-option label="审核通过" value="VIA_AUDITING" />
|
||||
<el-option label="提现成功" value="SUCCESS" />
|
||||
<el-option label="用户确认" value="WAIT_USER_CONFIRM" />
|
||||
<el-option label="审核拒绝" value="FAIL_AUDITING" />
|
||||
<el-option label="提现成功" value="SUCCESS" />
|
||||
<el-option label="提现失败" value="ERROR" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -33,55 +56,90 @@
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
clearable
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
placeholder="选择起始时间"
|
||||
style="width: 360px"
|
||||
style="width: 320px"
|
||||
@change="selectDateRange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card>
|
||||
|
||||
<!-- 提现申请数据表格 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>会员提现申请</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getDataList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
ref="table"
|
||||
class="mt_10"
|
||||
style="width: 100%"
|
||||
@sort-change="changeSort"
|
||||
@selection-change="changeSelect"
|
||||
>
|
||||
<el-table-column prop="sn" label="申请编号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="申请金额" width="120">
|
||||
<!-- 客户ID -->
|
||||
<el-table-column prop="memberId" label="客户ID" min-width="170" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="140" show-overflow-tooltip />
|
||||
|
||||
<!-- 申请编号 -->
|
||||
<el-table-column prop="sn" label="申请编号" min-width="180" show-overflow-tooltip />
|
||||
|
||||
<!-- 申请提现金额 -->
|
||||
<el-table-column label="申请金额" width="130" align="right" sortable="custom" prop="applyMoney">
|
||||
<template #default="{ row }">
|
||||
<priceColorScheme v-if="row" :value="row.applyMoney" :color="$mainColor" />
|
||||
<span v-if="row">¥ {{ formatMoney(row.applyMoney) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提现状态" width="120">
|
||||
|
||||
<!-- 提现状态 -->
|
||||
<el-table-column label="提现状态" width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row" :type="applyStatusTagType(row.applyStatus)">
|
||||
{{ applyStatusText(row.applyStatus) }}
|
||||
</el-tag>
|
||||
{{ row ? applyStatusText(row.applyStatus) : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="申请时间" width="170" sortable="custom" />
|
||||
<el-table-column prop="inspectTime" label="审核时间" width="170" />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
|
||||
<!-- 申请时间 -->
|
||||
<el-table-column prop="createTime" label="申请时间" width="170" align="center" sortable="custom" />
|
||||
|
||||
<!-- 审核时间 -->
|
||||
<el-table-column prop="inspectTime" label="审核时间" width="170" align="center" />
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row" class="ops">
|
||||
<a class="link-text" v-if="row.applyStatus === 'APPLY'" @click="openAudit(row)">审核</a>
|
||||
<a class="link-text" v-else @click="openQuery(row)">查看</a>
|
||||
</div>
|
||||
<template v-if="row">
|
||||
<a v-if="row.applyStatus === 'APPLY'" class="link-text" @click="openAudit(row)">审核</a>
|
||||
<a v-else class="link-text" @click="openQuery(row)">查看</a>
|
||||
<template v-if="row.memberId">
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="viewMemberDetail(row)">客户详情</a>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -95,114 +153,170 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 审核弹窗 -->
|
||||
<el-dialog
|
||||
v-model="roleModalVisible"
|
||||
:title="modalTitle"
|
||||
width="500px"
|
||||
title="提现申请审核"
|
||||
width="520px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="申请编号">
|
||||
<span>{{ showList.sn }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称">
|
||||
<span>{{ showList.memberName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请金额">
|
||||
<priceColorScheme :value="showList.applyMoney" :color="$mainColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="提现状态">
|
||||
<span>{{ paramTypeFilter(showList.applyStatus) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请时间">
|
||||
<span>{{ showList.createTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核备注">
|
||||
<el-input v-model="audit" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="dialog-body">
|
||||
<el-descriptions :column="1" border size="small" class="custom-dialog-descriptions">
|
||||
<el-descriptions-item label="申请编号">
|
||||
<span class="font-mono">{{ showList.sn }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户ID">
|
||||
<span class="font-mono">{{ showList.memberId || "-" }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
<span class="font-semibold">{{ showList.memberName }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">
|
||||
<span class="money-text font-semibold">¥ {{ formatMoney(showList.applyMoney) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">
|
||||
<span>{{ showList.createTime }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="remark-form-item" style="margin-top: 14px">
|
||||
<div class="remark-label" style="font-size: 13px; color: #606266; margin-bottom: 6px">
|
||||
审核备注说明(拒绝时必填):
|
||||
</div>
|
||||
<el-input
|
||||
v-model="audit"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入审核备注说明..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<template v-if="showList.applyStatus === 'APPLY'">
|
||||
<el-button @click="submitRole(false)">拒绝</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitRole(true)">通过</el-button>
|
||||
</template>
|
||||
<el-button @click="roleModalVisible = false">取消</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:loading="submitLoading"
|
||||
@click="submitRole(false)"
|
||||
>
|
||||
拒绝
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="submitRole(true)"
|
||||
>
|
||||
通过
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情弹窗 -->
|
||||
<el-dialog
|
||||
v-model="queryModalVisible"
|
||||
:title="modalTitle"
|
||||
width="500px"
|
||||
title="提现申请详情"
|
||||
width="520px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="申请编号:">
|
||||
<span>{{ showList.sn }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称:">
|
||||
<span>{{ showList.memberName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请金额:">
|
||||
<priceColorScheme :value="showList.applyMoney" :color="$mainColor" />
|
||||
</el-form-item>
|
||||
<el-form-item label="提现状态:">
|
||||
<span>{{ paramTypeFilter(showList.applyStatus) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请时间:">
|
||||
<span>{{ showList.createTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核时间:">
|
||||
<span>{{ showList.inspectTime }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核备注:">
|
||||
<span>{{ showList.inspectRemark || "暂无备注" }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="dialog-body">
|
||||
<el-descriptions :column="1" border size="small" class="custom-dialog-descriptions">
|
||||
<el-descriptions-item label="申请编号">
|
||||
<span class="font-mono">{{ showList.sn }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户ID">
|
||||
<span class="font-mono">{{ showList.memberId || "-" }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
<span class="font-semibold">{{ showList.memberName }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请金额">
|
||||
<span class="money-text font-semibold">¥ {{ formatMoney(showList.applyMoney) }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提现状态">
|
||||
<el-tag :type="applyStatusTagType(showList.applyStatus)" size="small">
|
||||
{{ paramTypeFilter(showList.applyStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">
|
||||
<span>{{ showList.createTime }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">
|
||||
<span>{{ showList.inspectTime || "-" }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核备注">
|
||||
<span>{{ showList.inspectRemark || "暂无备注" }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<template #footer>
|
||||
<template v-if="showList.applyStatus === 'APPLY'">
|
||||
<el-button @click="submitRole(false)">拒绝</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitRole(true)">通过</el-button>
|
||||
</template>
|
||||
<el-button v-else @click="queryModalVisible = false">取消</el-button>
|
||||
<el-button @click="queryModalVisible = false">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { getUserWithdrawApply, withdrawApply } from "@/api/member";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "withdrawApply" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const route = useRoute();
|
||||
const searchFormRef = ref(null);
|
||||
// 模板 el-table 的 ref
|
||||
const table = ref(null);
|
||||
const selectDate = ref(null);
|
||||
|
||||
const modalTitle = ref("");
|
||||
const loading = ref(true);
|
||||
const audit = ref("");
|
||||
const roleModalVisible = ref(false);
|
||||
const queryModalVisible = ref(false);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
applyStatus: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const submitLoading = ref(false);
|
||||
const selectList = ref([]);
|
||||
const selectCount = ref(0);
|
||||
const showList = ref({});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
|
||||
getUserWithdrawApply,
|
||||
{
|
||||
pageSize: 20,
|
||||
immediate: false,
|
||||
form: {
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
memberId: "",
|
||||
memberName: "",
|
||||
sn: "",
|
||||
applyStatus: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
beforeRequest: (p) => {
|
||||
const params = { ...p };
|
||||
if (selectDate.value && selectDate.value.length === 2) {
|
||||
params.startDate = selectDate.value[0];
|
||||
params.endDate = selectDate.value[1];
|
||||
} else {
|
||||
params.startDate = "";
|
||||
params.endDate = "";
|
||||
}
|
||||
return params;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function formatMoney(val) {
|
||||
const num = Number(val || 0);
|
||||
return num.toFixed(2);
|
||||
}
|
||||
|
||||
function viewMemberDetail(row) {
|
||||
if (!row || !row.memberId) return;
|
||||
customRouterPush({ name: "member-detail", query: { id: row.memberId } });
|
||||
}
|
||||
|
||||
function paramTypeFilter(val) {
|
||||
if (val === "APPLY") return "申请中";
|
||||
@@ -211,41 +325,47 @@ function paramTypeFilter(val) {
|
||||
if (val === "FAIL_AUDITING") return "审核拒绝";
|
||||
if (val === "SUCCESS") return "提现成功";
|
||||
if (val === "ERROR") return "提现失败";
|
||||
return "未知状态";
|
||||
return val || "未知状态";
|
||||
}
|
||||
|
||||
function applyStatusText(status) {
|
||||
if (status === "APPLY") return "申请中";
|
||||
if (status === "VIA_AUDITING") return "审核通过";
|
||||
if (status === "WAIT_USER_CONFIRM") return "用户确认";
|
||||
if (status === "SUCCESS") return "提现成功";
|
||||
if (status === "ERROR") return "提现失败";
|
||||
return "审核拒绝";
|
||||
if (status === "FAIL_AUDITING") return "审核拒绝";
|
||||
return status || "-";
|
||||
}
|
||||
|
||||
function applyStatusTagType(status) {
|
||||
if (status === "APPLY") return "warning";
|
||||
if (status === "VIA_AUDITING") return "success";
|
||||
if (status === "WAIT_USER_CONFIRM") return "warning";
|
||||
if (status === "SUCCESS") return "primary";
|
||||
if (status === "ERROR") return "primary";
|
||||
if (status === "ERROR") return "danger";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
function openAudit(row) {
|
||||
showList.value = { ...row };
|
||||
audit.value = "";
|
||||
roleModalVisible.value = true;
|
||||
}
|
||||
|
||||
function openQuery(row) {
|
||||
showList.value = { ...row };
|
||||
modalTitle.value = "查看";
|
||||
queryModalVisible.value = true;
|
||||
}
|
||||
|
||||
function submitRole(res) {
|
||||
const params = {};
|
||||
params.applyId = showList.value.id;
|
||||
params.result = res;
|
||||
params.remark = audit.value;
|
||||
if (res === false && params.remark === "") {
|
||||
ElMessage.error("审核备注不能为空");
|
||||
const params = {
|
||||
applyId: showList.value.id,
|
||||
result: res,
|
||||
remark: audit.value ? audit.value.trim() : "",
|
||||
};
|
||||
if (res === false && !params.remark) {
|
||||
ElMessage.error("审核拒绝时备注说明不能为空");
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
@@ -265,50 +385,36 @@ function submitRole(res) {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
|
||||
function handleReset() {
|
||||
selectDate.value = null;
|
||||
searchForm.memberId = "";
|
||||
searchForm.memberName = "";
|
||||
searchForm.sn = "";
|
||||
searchForm.applyStatus = "";
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
searchForm.pageNumber = 1;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
function changeSort(e) {
|
||||
searchForm.sort = e.prop || e.key || "createTime";
|
||||
searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function handleReset() {
|
||||
searchFormRef.value.resetFields();
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
selectDate.value = null;
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
searchForm.memberName = "";
|
||||
getDataList();
|
||||
}
|
||||
function changeSort(e) {
|
||||
searchForm.sort = e.prop || e.key;
|
||||
searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
if (!e.order) {
|
||||
searchForm.order = "";
|
||||
}
|
||||
getDataList();
|
||||
}
|
||||
function clearSelectAll() {
|
||||
if (table.value) {
|
||||
table.value.clearSelection();
|
||||
}
|
||||
}
|
||||
function changeSelect(e) {
|
||||
selectList.value = e;
|
||||
selectCount.value = e.length;
|
||||
}
|
||||
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
@@ -318,30 +424,97 @@ function selectDateRange(v) {
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getUserWithdrawApply(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
if (route.query && route.query.applyStatus) {
|
||||
searchForm.applyStatus = route.query.applyStatus;
|
||||
}
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ops a {
|
||||
color: #2d8cf0;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
.member-withdraw-apply-page {
|
||||
padding: 16px 20px;
|
||||
background-color: #f6f8fa;
|
||||
min-height: calc(100vh - 110px);
|
||||
}
|
||||
.ops span {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
color: #dcdee2;
|
||||
|
||||
.font-mono {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 筛选卡片 */
|
||||
.filter-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 16px 2px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格卡片 */
|
||||
.table-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-dialog-descriptions {
|
||||
:deep(.el-descriptions__label) {
|
||||
width: 100px;
|
||||
background-color: #fafafa;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.money-text {
|
||||
font-size: 15px;
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,32 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="member-experience-log-page search">
|
||||
<!-- 顶部筛选表单 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="90px"
|
||||
label-width="84px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="客户手机号" prop="mobile">
|
||||
<el-form-item label="客户手机号" prop="memberMobile">
|
||||
<el-input
|
||||
v-model="searchForm.memberMobile"
|
||||
clearable
|
||||
placeholder="请输入客户手机号"
|
||||
style="width: 220px"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="规则" prop="ruleKey">
|
||||
<el-select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
|
||||
<el-form-item label="规则类型" prop="ruleKey">
|
||||
<el-select
|
||||
v-model="searchForm.ruleKey"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="全部规则"
|
||||
style="width: 180px"
|
||||
>
|
||||
<el-option label="全部规则" value="" />
|
||||
<el-option
|
||||
v-for="item in ruleOptions"
|
||||
:key="item.value"
|
||||
@@ -27,42 +35,103 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
|
||||
<el-table-column label="客户手机号" width="140" show-overflow-tooltip>
|
||||
<!-- 经验值记录表格 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>客户经验值流水</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getData">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
style="width: 100%"
|
||||
>
|
||||
<!-- 客户ID -->
|
||||
<el-table-column prop="memberId" label="客户ID" min-width="190" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column prop="memberName" label="客户名称" min-width="130" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户手机号 -->
|
||||
<el-table-column label="客户手机号" width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.memberMobile || row.mobile || "-" }}</span>
|
||||
{{ row ? (row.memberMobile || row.mobile || "-") : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规则名称" width="150" show-overflow-tooltip>
|
||||
|
||||
<!-- 规则名称 -->
|
||||
<el-table-column label="规则名称" width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.ruleName || findRuleName(row.ruleKey) || "-" }}</span>
|
||||
{{ row ? (row.ruleName || findRuleName(row.ruleKey) || "-") : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="变化经验值" width="120">
|
||||
|
||||
<!-- 变动前经验值 -->
|
||||
<el-table-column label="变动前经验值" width="120" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ formatExperienceValue(row) }}</span>
|
||||
<span v-if="row">{{ row.beforeExperience != null ? formatNumber(row.beforeExperience) : "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="经验值限额" width="120">
|
||||
|
||||
<!-- 变动经验值 -->
|
||||
<el-table-column label="变动经验值" width="120" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ formatMaxValue(row) }}</span>
|
||||
<span v-if="row" class="experience-increase">
|
||||
+{{ formatNumber(row.variableExperience ?? row.value ?? 0) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 当前经验值 -->
|
||||
<el-table-column label="变动后经验值" width="120" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="experience-after">
|
||||
{{ formatNumber(row.experience ?? 0) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作说明 / 备注 -->
|
||||
<el-table-column label="操作说明" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.content || row.remark || row.description || "-" }}</span>
|
||||
<span>{{ row ? row.content || row.remark || row.description || "-" : "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 创建时间 -->
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" align="center" />
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="110" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<a v-if="row.memberId" class="link-text" @click="viewMemberDetail(row)">客户详情</a>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" />
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -82,8 +151,10 @@
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import * as API_Member from "@/api/member";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const RULE_OPTIONS = [
|
||||
const ruleOptions = [
|
||||
{ value: "CONSUME", label: "消费" },
|
||||
{ value: "REGISTER", label: "注册" },
|
||||
{ value: "SIGN_IN", label: "签到" },
|
||||
@@ -101,7 +172,7 @@ defineOptions({ name: "memberGradeExperienceLog" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const { loading, data, total, searchForm, getDataList: getData } = useTable(
|
||||
const { loading, data, total, searchForm, getDataList: getData, resetSearch } = useTable(
|
||||
API_Member.getMemberExperienceByPage,
|
||||
{
|
||||
pageSize: 20,
|
||||
@@ -111,45 +182,138 @@ const { loading, data, total, searchForm, getDataList: getData } = useTable(
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
function formatNumber(val) {
|
||||
const num = Number(val || 0);
|
||||
return num.toLocaleString();
|
||||
}
|
||||
|
||||
function formatExperienceValue(row) {
|
||||
const v = row.value ?? row.variableExperience ?? row.experience ?? row.variableValue;
|
||||
return v == null ? "-" : v;
|
||||
}
|
||||
function formatMaxValue(row) {
|
||||
const v = row.maxValue ?? row.maxExperience ?? row.limitValue;
|
||||
return v == null ? "-" : v;
|
||||
}
|
||||
function findRuleName(ruleKey) {
|
||||
const hit = ruleOptions.find((item) => item.value === ruleKey);
|
||||
return hit ? hit.label : "";
|
||||
return hit ? hit.label : ruleKey || "";
|
||||
}
|
||||
function init() {
|
||||
getData();
|
||||
|
||||
function viewMemberDetail(row) {
|
||||
if (!row || !row.memberId) return;
|
||||
customRouterPush({ name: "member-detail", query: { id: row.memberId } });
|
||||
}
|
||||
|
||||
function buildParams(base = searchForm) {
|
||||
const params = {
|
||||
pageNumber: base.pageNumber,
|
||||
pageSize: base.pageSize,
|
||||
};
|
||||
if (base.memberMobile) params.mobile = base.memberMobile;
|
||||
if (base.memberMobile) params.mobile = base.memberMobile.trim();
|
||||
if (base.ruleKey) params.ruleKey = base.ruleKey;
|
||||
return params;
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.memberMobile = "";
|
||||
searchForm.ruleKey = "";
|
||||
searchForm.pageNumber = 1;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.member-experience-log-page {
|
||||
padding: 16px 20px;
|
||||
background-color: #f6f8fa;
|
||||
min-height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 筛选卡片 */
|
||||
.filter-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 16px 2px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格卡片 */
|
||||
.table-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.experience-increase {
|
||||
font-size: 13px;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.experience-after {
|
||||
font-size: 13px;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,29 +1,102 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="operation padding-row">
|
||||
<el-button type="primary" @click="openAdd">添加分组</el-button>
|
||||
<div class="member-group-page search">
|
||||
<!-- 顶部筛选与操作栏 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<div class="card-toolbar">
|
||||
<el-form inline :model="searchForm" class="filter-form" @keyup.enter="handleSearch">
|
||||
<el-form-item label="分组名称">
|
||||
<el-input
|
||||
v-model="searchForm.groupName"
|
||||
placeholder="请输入分组名称"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
@clear="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="toolbar-right">
|
||||
<el-button type="primary" @click="openAdd">添加分组</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-table ref="table" v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
|
||||
<!-- 分组数据表格 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>会员分组列表</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getData">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
:data="data"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
class="custom-group-table"
|
||||
>
|
||||
<!-- 分组ID -->
|
||||
<el-table-column prop="id" label="分组ID" min-width="190" />
|
||||
|
||||
<!-- 分组名称 -->
|
||||
<el-table-column prop="groupName" label="分组名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip />
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="180" />
|
||||
<el-table-column prop="updateTime" label="更新时间" min-width="180" />
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
|
||||
<!-- 分组人数 -->
|
||||
<el-table-column prop="memberCount" label="分组人数" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row" class="ops" style="display: flex; justify-content: center">
|
||||
<template v-if="row">
|
||||
<a
|
||||
v-if="row.memberCount > 0"
|
||||
class="link-text"
|
||||
@click="viewGroupMembers(row)"
|
||||
>
|
||||
{{ row.memberCount }}
|
||||
</a>
|
||||
<span v-else>{{ row.memberCount ?? 0 }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 分组描述 -->
|
||||
<el-table-column prop="description" label="分组描述" min-width="240" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ row ? row.description || "-" : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 创建时间 -->
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="170" align="center" />
|
||||
|
||||
<!-- 更新时间 -->
|
||||
<el-table-column prop="updateTime" label="更新时间" min-width="170" align="center" />
|
||||
|
||||
<!-- 操作栏 -->
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<a class="link-text" @click="viewGroupMembers(row)">查看会员</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="openEdit(row)">编辑</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="remove(row)">删除</a>
|
||||
</div>
|
||||
<a class="link-text error-text" @click="remove(row)">删除</a>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -37,34 +110,78 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="addFlag" title="添加分组" width="500px" :close-on-click-modal="false" destroy-on-close>
|
||||
<!-- 添加分组弹窗 -->
|
||||
<el-dialog
|
||||
v-model="addFlag"
|
||||
title="添加分组"
|
||||
width="520px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
|
||||
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
|
||||
<el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
|
||||
<el-form-item label="分组名称" prop="groupName">
|
||||
<el-input
|
||||
v-model="formAdd.groupName"
|
||||
maxlength="30"
|
||||
show-word-limit
|
||||
placeholder="请输入分组名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组描述" prop="description" style="width: 90%">
|
||||
<el-input v-model="formAdd.description" maxlength="200" placeholder="请输入分组描述" />
|
||||
<el-form-item label="分组描述" prop="description">
|
||||
<el-input
|
||||
v-model="formAdd.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入分组描述信息"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="addFlag = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
|
||||
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="editFlag" title="编辑分组" width="500px" :close-on-click-modal="false" destroy-on-close>
|
||||
<!-- 编辑分组弹窗 -->
|
||||
<el-dialog
|
||||
v-model="editFlag"
|
||||
title="编辑分组"
|
||||
width="520px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="90px">
|
||||
<el-input v-model="formEdit.id" style="display: none" />
|
||||
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
|
||||
<el-input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
|
||||
<el-form-item label="分组ID">
|
||||
<span>{{ formEdit.id }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组描述" prop="description" style="width: 90%">
|
||||
<el-input v-model="formEdit.description" maxlength="200" placeholder="请输入分组描述" />
|
||||
<el-form-item label="分组名称" prop="groupName">
|
||||
<el-input
|
||||
v-model="formEdit.groupName"
|
||||
maxlength="30"
|
||||
show-word-limit
|
||||
placeholder="请输入分组名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组描述" prop="description">
|
||||
<el-input
|
||||
v-model="formEdit.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入分组描述信息"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="editFlag = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</el-button>
|
||||
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">
|
||||
确定
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
@@ -72,11 +189,21 @@
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
defineOptions({ name: "memberGroup" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function viewGroupMembers(row) {
|
||||
router.push({
|
||||
name: "memberList",
|
||||
query: { groupId: row.id },
|
||||
});
|
||||
}
|
||||
|
||||
const addForm = ref(null);
|
||||
const editForm = ref(null);
|
||||
|
||||
@@ -84,6 +211,7 @@ const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
groupName: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
@@ -91,44 +219,81 @@ const addFlag = ref(false);
|
||||
const editFlag = ref(false);
|
||||
const submitAddLoading = ref(false);
|
||||
const submitEditLoading = ref(false);
|
||||
|
||||
const formAdd = ref({
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
|
||||
const formEdit = ref({
|
||||
id: "",
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
|
||||
const rulesAdd = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const rulesEdit = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
|
||||
function init() {
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.groupName = "";
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberGroupByPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
API_Member.getMemberGroupByPage(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
const records = res.result.records || [];
|
||||
data.value = records;
|
||||
total.value = res.result.total || 0;
|
||||
|
||||
// 异步获取各分组的会员总数
|
||||
records.forEach((row) => {
|
||||
if (row.memberCount == null) {
|
||||
API_Member.getMemberGroupUsers(row.id, { pageNumber: 1, pageSize: 1 })
|
||||
.then((userRes) => {
|
||||
if (userRes && userRes.success && userRes.result) {
|
||||
row.memberCount = userRes.result.total || 0;
|
||||
} else {
|
||||
row.memberCount = 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
row.memberCount = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
addFlag.value = true;
|
||||
nextTick(() => {
|
||||
@@ -136,20 +301,26 @@ function openAdd() {
|
||||
formAdd.value = { groupName: "", description: "" };
|
||||
});
|
||||
}
|
||||
|
||||
function submitAdd() {
|
||||
addForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitAddLoading.value = true;
|
||||
API_Member.addMemberGroup(formAdd.value).then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
API_Member.addMemberGroup(formAdd.value)
|
||||
.then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitAddLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
editFlag.value = true;
|
||||
submitEditLoading.value = false;
|
||||
@@ -169,48 +340,120 @@ function openEdit(row) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitEdit() {
|
||||
editForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitEditLoading.value = true;
|
||||
const { id, groupName, description } = formEdit.value;
|
||||
API_Member.updateMemberGroup(id, { groupName, description }).then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
API_Member.updateMemberGroup(id, { groupName, description })
|
||||
.then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitEditLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
ElMessageBox.confirm(`确定删除分组 "${row.groupName || "该分组"}" 吗?`, "删除确认", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定删除",
|
||||
cancelButtonText: "取消",
|
||||
})
|
||||
.then(() => {
|
||||
API_Member.deleteMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
getData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ops a {
|
||||
<style scoped lang="scss">
|
||||
.member-group-page {
|
||||
.card-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0;
|
||||
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.link-text {
|
||||
color: #2d8cf0;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ops span {
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
color: #57a3f3;
|
||||
}
|
||||
|
||||
&.error-text {
|
||||
color: #ed4014;
|
||||
|
||||
&:hover {
|
||||
color: #f16643;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.op-split {
|
||||
margin: 0 8px;
|
||||
color: #dcdee2;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -308,6 +308,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import multipleMap from "@/components/map/multiple-map";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
@@ -320,6 +321,8 @@ import defaultPic from "@/assets/default.png";
|
||||
|
||||
defineOptions({ name: "member" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const props = defineProps({
|
||||
selectedMember: {
|
||||
type: Boolean,
|
||||
@@ -511,7 +514,15 @@ function submitSetMemberGroup() {
|
||||
const groupId = memberGroupForm.value.groupId;
|
||||
memberGroupLoading.value = true;
|
||||
Promise.all(
|
||||
memberIds.map((memberId) => API_Member.setMemberUserGroups(memberId, [groupId]))
|
||||
memberIds.map((memberId) =>
|
||||
API_Member.setMemberUserGroups(memberId, [groupId]).then((res) => {
|
||||
if (!res || !res.success) {
|
||||
// 后端 updateUserGroups 若因原无分组返回失败,自动降级尝试 addMemberGroupUsers
|
||||
return API_Member.addMemberGroupUsers(groupId, [memberId]);
|
||||
}
|
||||
return res;
|
||||
})
|
||||
)
|
||||
)
|
||||
.then((results) => {
|
||||
memberGroupLoading.value = false;
|
||||
@@ -758,11 +769,25 @@ function handleSubmitModal() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query && route.query.groupId) {
|
||||
searchForm.value.groupId = String(route.query.groupId);
|
||||
}
|
||||
getData();
|
||||
loadMemberGroupList();
|
||||
loadMemberGradeList();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query.groupId,
|
||||
(val) => {
|
||||
if (val !== undefined) {
|
||||
searchForm.value.groupId = val ? String(val) : "";
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 外部通过 ref 调用(promotions/coupon-activity/coupon-publish.vue 的 memberLayoutRef.getSelection())
|
||||
defineExpose({ getSelection });
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<div class="member-recycle-page search">
|
||||
<!-- 顶部筛选条件表单 -->
|
||||
<el-card shadow="never" class="filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
@@ -14,7 +15,7 @@
|
||||
v-model="searchForm.id"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="username">
|
||||
@@ -22,66 +23,94 @@
|
||||
v-model="searchForm.username"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系方式" prop="mobile">
|
||||
<el-input
|
||||
v-model="searchForm.mobile"
|
||||
placeholder="请输入会员联系方式"
|
||||
placeholder="请输入手机联系方式"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<!-- 回收站会员表格卡片 -->
|
||||
<el-card shadow="never" class="table-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>会员回收站列表</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getData">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
class="mt_10"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="客户ID" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="username" label="客户名称" min-width="150" align="left" show-overflow-tooltip />
|
||||
<el-table-column prop="nickName" label="昵称" min-width="120" align="left" show-overflow-tooltip />
|
||||
<el-table-column label="联系方式" min-width="130">
|
||||
<!-- 客户ID -->
|
||||
<el-table-column prop="id" label="客户ID" min-width="190" show-overflow-tooltip />
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column prop="username" label="客户名称" min-width="150" show-overflow-tooltip />
|
||||
|
||||
<!-- 昵称 -->
|
||||
<el-table-column prop="nickName" label="昵称" min-width="140" show-overflow-tooltip />
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<el-table-column label="联系方式" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.mobile || "" }}</span>
|
||||
{{ row && row.mobile ? row.mobile : "-" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="注册时间" min-width="180" />
|
||||
<el-table-column label="积分数量" min-width="120" align="left">
|
||||
|
||||
<!-- 积分数量 -->
|
||||
<el-table-column label="积分数量" width="120">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.point == void 0 ? "0" : row.point }}</span>
|
||||
{{ row ? (row.point == void 0 ? "0" : row.point) : "0" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
|
||||
<!-- 注册时间 -->
|
||||
<el-table-column prop="createTime" label="注册时间" min-width="180" align="center" />
|
||||
|
||||
<!-- 操作栏 -->
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row" class="ops" style="display: flex; justify-content: center">
|
||||
<template v-if="row">
|
||||
<template v-if="selectedMember">
|
||||
<a class="link-text" @click="callback(row)">选择</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="callback(row)">
|
||||
{{ row.___selected ? "已选择" : "选择" }}
|
||||
</a>
|
||||
</template>
|
||||
<a class="link-text" @click="detail(row)">查看</a>
|
||||
<template v-if="!selectedMember">
|
||||
<template v-else>
|
||||
<a class="link-text" @click="detail(row)">查看</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="enable(row)">启用</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="editPerm(row)">编辑</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -95,23 +124,38 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="descFlag" :title="descTitle" width="500px" destroy-on-close>
|
||||
<el-form ref="formValidateRef" :model="formValidate" :rules="ruleValidate" label-width="80px">
|
||||
<!-- 编辑会员弹窗 -->
|
||||
<el-dialog
|
||||
v-model="descFlag"
|
||||
:title="descTitle"
|
||||
width="540px"
|
||||
destroy-on-close
|
||||
class="edit-member-dialog"
|
||||
>
|
||||
<el-form ref="formValidateRef" :model="formValidate" :rules="ruleValidate" label-width="90px">
|
||||
<el-form-item label="客户ID">
|
||||
<span>{{ formValidate.id }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="头像">
|
||||
<img alt="会员头像" :src="formValidate.face" class="face" />
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
class="upload"
|
||||
@click="openPicSelector"
|
||||
>修改</el-button>
|
||||
<input type="file" style="display: none" id="file" />
|
||||
<div class="avatar-edit-box">
|
||||
<el-avatar :size="50" :src="formValidate.face || undefined" class="dialog-avatar">
|
||||
{{ (formValidate.username || "用")[0] }}
|
||||
</el-avatar>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
class="upload-btn"
|
||||
@click="openPicSelector"
|
||||
>
|
||||
修改头像
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="name">
|
||||
<el-input v-model="formValidate.username" style="width: 200px" disabled />
|
||||
<el-input v-model="formValidate.username" style="width: 260px" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称" prop="name">
|
||||
<el-input v-model="formValidate.nickName" style="width: 200px" />
|
||||
<el-input v-model="formValidate.nickName" placeholder="请输入用户昵称" style="width: 260px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formValidate.sex">
|
||||
@@ -119,20 +163,29 @@
|
||||
<el-radio-button :value="0">女</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="修改密码" prop="password">
|
||||
<el-input v-model="formValidate.newPassword" type="password" show-password style="width: 220px" />
|
||||
<el-form-item label="重置密码" prop="password">
|
||||
<el-input
|
||||
v-model="formValidate.newPassword"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="留空则保持原密码"
|
||||
style="width: 260px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="生日" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formValidate.birthday"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 220px"
|
||||
placeholder="请选择生日"
|
||||
style="width: 260px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所在地" prop="mail">
|
||||
{{ formValidate.region || "暂无地址" }}
|
||||
<el-button style="margin-left: 10px" @click="$refs.map.open()">选择</el-button>
|
||||
<div class="region-box">
|
||||
<span class="region-text">{{ formValidate.region || "暂无地址" }}</span>
|
||||
<el-button size="small" @click="$refs.map.open()">选择地区</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -141,7 +194,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px">
|
||||
<el-dialog v-model="picModelFlag" width="1200px" title="选择头像图片">
|
||||
<ossManage
|
||||
ref="ossManageRef"
|
||||
@callback="callbackSelected"
|
||||
@@ -166,19 +219,16 @@ defineOptions({ name: "memberRecycle" });
|
||||
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
// 模板编辑弹窗 el-form 的 ref(原 ref="formValidate",与 data 同名会互相覆盖,改名为 formValidateRef)
|
||||
const formValidateRef = ref(null);
|
||||
// 模板 ossManage 组件的 ref(原 ref="ossManage",与组件 import 同名会冲突,改名为 ossManageRef)
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
const selectedMember = ref(false);
|
||||
const descTitle = ref("");
|
||||
const descFlag = ref(false);
|
||||
const openSearch = ref(true);
|
||||
const loading = ref(true);
|
||||
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
@@ -188,6 +238,7 @@ const searchForm = ref({
|
||||
mobile: "",
|
||||
disabled: "CLOSE",
|
||||
});
|
||||
|
||||
const picModelFlag = ref(false);
|
||||
const formValidate = ref({});
|
||||
const ruleValidate = ref({});
|
||||
@@ -197,26 +248,40 @@ const total = ref(0);
|
||||
function callback(val) {
|
||||
emit("callback", val);
|
||||
}
|
||||
|
||||
function init() {
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePage() {
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.id = "";
|
||||
searchForm.value.username = "";
|
||||
searchForm.value.mobile = "";
|
||||
getData();
|
||||
}
|
||||
|
||||
function editPerm(val) {
|
||||
descTitle.value = `查看用户 ${val.username}`;
|
||||
descTitle.value = `编辑停用客户 - ${val.username || "客户"}`;
|
||||
descFlag.value = true;
|
||||
getMemberInfo(val.id);
|
||||
}
|
||||
|
||||
function getMemberInfo(id) {
|
||||
API_Member.getMemberInfoData(id).then((res) => {
|
||||
if (res.result) {
|
||||
@@ -224,16 +289,22 @@ function getMemberInfo(id) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberListData(searchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
API_Member.getMemberListData(searchForm.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function openPicSelector() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
@@ -242,10 +313,12 @@ function openPicSelector() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
formValidate.value.face = val.url;
|
||||
}
|
||||
|
||||
function selectedRegion(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
@@ -257,23 +330,32 @@ function selectedRegion(val) {
|
||||
formValidate.value.regionId = val.data.addrId;
|
||||
}
|
||||
}
|
||||
|
||||
function detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
}
|
||||
|
||||
function enable(v) {
|
||||
let params = {
|
||||
memberIds: [v.id],
|
||||
disabled: true,
|
||||
};
|
||||
ElMessageBox.confirm("确定启用此会员?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("启用成功");
|
||||
getData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
ElMessageBox.confirm(`确定启用客户 "${v.username || v.nickName || "该客户"}" 吗?`, "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定启用",
|
||||
cancelButtonText: "取消",
|
||||
})
|
||||
.then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("启用成功");
|
||||
getData();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function handleSubmitModal() {
|
||||
const { nickName, sex, username, face, newPassword, id, regionId, region } = formValidate.value;
|
||||
let time = new Date(formValidate.value.birthday);
|
||||
@@ -288,7 +370,6 @@ function handleSubmitModal() {
|
||||
id,
|
||||
};
|
||||
if (newPassword) {
|
||||
// 原 this.md5 为 main.js 注册的全局属性,改为具名导入(同 login.vue / userManage.vue)
|
||||
submit.password = md5(newPassword);
|
||||
}
|
||||
API_Member.updateMember(submit).then((res) => {
|
||||
@@ -304,20 +385,67 @@ onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.face {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
.member-recycle-page {
|
||||
.filter-card {
|
||||
.search-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0;
|
||||
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
.ops a {
|
||||
color: #2d8cf0;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
.avatar-edit-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.dialog-avatar {
|
||||
background-color: #f56c6c;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.ops span {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
color: #dcdee2;
|
||||
|
||||
.region-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.region-text {
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,37 +1,54 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card class="points-statistics-card">
|
||||
<el-row type="flex" justify="space-around" align="middle" class="points-statistics">
|
||||
<div class="member-point-page search">
|
||||
<!-- 顶部数据统计概览看板 -->
|
||||
<el-card shadow="never" class="points-statistics-card">
|
||||
<el-row :gutter="24" class="points-statistics">
|
||||
<el-col :xs="24" :sm="12" class="points-statistics-item">
|
||||
<div class="points-statistics-title">已发放积分数</div>
|
||||
<div class="points-statistics-subtitle">历史累计发放积分数</div>
|
||||
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.totalPoint) }}</div>
|
||||
<div class="stat-inner-box">
|
||||
<div class="points-statistics-title">已发放积分数</div>
|
||||
<div class="points-statistics-subtitle">平台历史累计发放积分总量</div>
|
||||
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.totalPoint) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" class="points-statistics-item">
|
||||
<div class="points-statistics-title">未使用积分数</div>
|
||||
<div class="points-statistics-subtitle">会员账户未使用积分数</div>
|
||||
<div class="points-statistics-value">{{ formatNumber(pointsStatistics.unUsedPoint) }}</div>
|
||||
<div class="stat-inner-box">
|
||||
<div class="points-statistics-title">未使用积分数</div>
|
||||
<div class="points-statistics-subtitle">会员账户当前剩余未使用积分总量</div>
|
||||
<div class="points-statistics-value text-warning">{{ formatNumber(pointsStatistics.unUsedPoint) }}</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- 选项卡与数据主区域 -->
|
||||
<div class="point-tabs-wrap">
|
||||
<el-tabs v-model="activeTab" class="point-tabs">
|
||||
<el-tab-pane label="积分列表" name="pointList">
|
||||
<el-card class="point-content-card">
|
||||
<!-- 选项卡 1:会员积分列表 -->
|
||||
<el-tab-pane label="会员积分列表" name="pointList">
|
||||
<!-- 筛选条件 -->
|
||||
<el-card shadow="never" class="point-filter-card">
|
||||
<el-form
|
||||
ref="memberSearchFormRef"
|
||||
:model="memberSearchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleMemberSearch"
|
||||
>
|
||||
<el-form-item label="客户ID" prop="id">
|
||||
<el-input
|
||||
v-model="memberSearchForm.id"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 190px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="nickName">
|
||||
<el-input
|
||||
v-model="memberSearchForm.nickName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
style="width: 160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户账号" prop="username">
|
||||
@@ -39,82 +56,128 @@
|
||||
v-model="memberSearchForm.username"
|
||||
placeholder="请输入客户账号"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
style="width: 160px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="账号状态" prop="disabled">
|
||||
<el-select
|
||||
v-model="memberSearchForm.disabled"
|
||||
clearable
|
||||
placeholder="请选择账号状态"
|
||||
style="width: 160px"
|
||||
placeholder="全部状态"
|
||||
style="width: 130px"
|
||||
>
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="启用" value="OPEN" />
|
||||
<el-option label="禁用" value="CLOSE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="积分值">
|
||||
<el-form-item label="积分区间">
|
||||
<el-input-number
|
||||
v-model="memberSearchForm.minPoint"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
placeholder="最小积分值"
|
||||
style="width: 140px"
|
||||
placeholder="最小"
|
||||
controls-position="right"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<span class="point-range-separator">-</span>
|
||||
<el-input-number
|
||||
v-model="memberSearchForm.maxPoint"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
placeholder="最大积分值"
|
||||
style="width: 140px"
|
||||
placeholder="最大"
|
||||
controls-position="right"
|
||||
style="width: 120px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="search-btn" type="primary" @click="handleMemberSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button class="search-btn" type="primary" @click="handleMemberSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleMemberReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="point-content-card member-list-card">
|
||||
|
||||
<!-- 会员积分表格 -->
|
||||
<el-card shadow="never" class="point-content-card member-list-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-title">用户列表</div>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>会员积分信息</span>
|
||||
<span class="total-badge">共 {{ memberTotal }} 位会员</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getMemberList">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
v-loading="memberLoading"
|
||||
border
|
||||
stripe
|
||||
:data="memberData"
|
||||
class="member-table"
|
||||
class="custom-point-table"
|
||||
style="width: 100%"
|
||||
>
|
||||
<!-- 客户ID -->
|
||||
<el-table-column label="客户ID" min-width="170">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="font-mono text-dark">{{ row.id || "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 客户名称 -->
|
||||
<el-table-column label="客户名称" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.nickName || "-" }}</span>
|
||||
<span v-if="row" class="font-semibold text-dark">{{ row.nickName || "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户账号" min-width="260" show-overflow-tooltip>
|
||||
|
||||
<!-- 客户账号 -->
|
||||
<el-table-column label="客户账号" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.username || "-" }}</span>
|
||||
<span v-if="row" class="font-mono">{{ row.username || "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="账号状态" min-width="120" align="center">
|
||||
|
||||
<!-- 账号状态 -->
|
||||
<el-table-column label="账号状态" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row" :type="row.disabled === true ? 'success' : 'info'">
|
||||
<el-tag
|
||||
v-if="row"
|
||||
:type="row.disabled === true ? 'success' : 'info'"
|
||||
size="small"
|
||||
>
|
||||
{{ row.disabled === true ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="积分余额" min-width="120" align="center">
|
||||
|
||||
<!-- 积分余额 -->
|
||||
<el-table-column label="积分余额" width="130" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ row.point == null ? 0 : row.point }}</span>
|
||||
<span v-if="row" class="point-num-text">
|
||||
{{ formatNumber(row.point == null ? 0 : row.point) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a v-if="row" class="link-text" @click="detail(row)">详情</a>
|
||||
<template v-if="row">
|
||||
<a class="link-text" @click="detail(row)">客户详情</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="viewMemberHistory(row)">变动明细</a>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="memberSearchForm.pageNumber"
|
||||
v-model:page-size="memberSearchForm.pageSize"
|
||||
@@ -128,63 +191,140 @@
|
||||
</div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 选项卡 2:积分增减记录 -->
|
||||
<el-tab-pane label="积分增减记录" name="pointChangeRecord">
|
||||
<el-card class="point-content-card">
|
||||
<!-- 筛选条件 -->
|
||||
<el-card shadow="never" class="point-filter-card">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
label-width="76px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="会员名称" prop="username">
|
||||
<el-form-item label="客户ID" prop="memberId">
|
||||
<el-input
|
||||
v-model="searchForm.memberId"
|
||||
placeholder="请输入客户ID"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="会员名称" prop="memberName">
|
||||
<el-input
|
||||
v-model="searchForm.memberName"
|
||||
placeholder="请输入会员名称"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="search-btn" type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-form-item class="form-actions">
|
||||
<el-button class="search-btn" type="primary" @click="handleSearch">
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="handleHistoryReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="point-content-card">
|
||||
|
||||
<!-- 积分记录表格 -->
|
||||
<el-card shadow="never" class="point-content-card mt_10">
|
||||
<template #header>
|
||||
<div class="card-header-bar">
|
||||
<div class="header-title">
|
||||
<span>积分流水记录</span>
|
||||
<span class="total-badge">共 {{ total }} 条明细</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<el-button size="small" @click="getData">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
border
|
||||
stripe
|
||||
:data="data"
|
||||
ref="table"
|
||||
class="mt_10 point-table"
|
||||
class="custom-point-table"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="memberName" label="会员名称" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="content" label="操作内容" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="beforePoint" label="之前积分" width="200" />
|
||||
<el-table-column label="变动积分" width="200">
|
||||
<!-- 客户ID -->
|
||||
<el-table-column label="客户ID" min-width="170">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="font-mono text-dark">{{ row.memberId || "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 会员名称 -->
|
||||
<el-table-column prop="memberName" label="会员名称" min-width="140" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="font-semibold text-dark">{{ row.memberName || "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作内容/原因 -->
|
||||
<el-table-column prop="content" label="操作内容" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span>{{ row ? row.content || "-" : "-" }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 变动前积分 -->
|
||||
<el-table-column label="变动前积分" width="130" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ formatNumber(row.beforePoint) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 变动积分 -->
|
||||
<el-table-column label="变动积分" width="130" align="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row">
|
||||
<priceColorScheme
|
||||
<el-tag
|
||||
v-if="row.pointType === 'INCREASE'"
|
||||
:value="row.variablePoint"
|
||||
color="green"
|
||||
unit="+"
|
||||
/>
|
||||
<priceColorScheme
|
||||
type="success"
|
||||
size="small"
|
||||
effect="light"
|
||||
class="point-change-tag"
|
||||
>
|
||||
+{{ formatNumber(row.variablePoint) }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else
|
||||
:value="row.variablePoint"
|
||||
:color="$mainColor"
|
||||
unit="-"
|
||||
/>
|
||||
type="danger"
|
||||
size="small"
|
||||
effect="light"
|
||||
class="point-change-tag"
|
||||
>
|
||||
-{{ formatNumber(row.variablePoint) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="point" label="当前积分" width="200" />
|
||||
<el-table-column prop="createTime" label="操作时间" width="200" />
|
||||
|
||||
<!-- 变动后积分 -->
|
||||
<el-table-column label="变动后积分" width="130" align="right">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="point-num-text">
|
||||
{{ formatNumber(row.point) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 操作时间 -->
|
||||
<el-table-column prop="createTime" label="操作时间" width="170" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row" class="time-text">{{ row.createTime }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrap mt_10">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
@@ -207,12 +347,12 @@
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "point" });
|
||||
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
// 模板 ref,script 内未使用
|
||||
const memberSearchFormRef = ref(null);
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
@@ -224,35 +364,60 @@ const pointsStatistics = ref({
|
||||
totalPoint: 0,
|
||||
unUsedPoint: 0,
|
||||
});
|
||||
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberId: "",
|
||||
memberName: "",
|
||||
});
|
||||
|
||||
const memberSearchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
id: "",
|
||||
nickName: "",
|
||||
username: "",
|
||||
disabled: "",
|
||||
minPoint: null,
|
||||
maxPoint: null,
|
||||
});
|
||||
|
||||
const data = ref([]);
|
||||
const memberData = ref([]);
|
||||
const memberTotal = ref(0);
|
||||
const total = ref(0);
|
||||
|
||||
function callback(val) {
|
||||
emit("callback", val);
|
||||
function copyText(text, label = "客户ID") {
|
||||
if (!text) return;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard
|
||||
.writeText(String(text))
|
||||
.then(() => ElMessage.success(`已复制${label}到剪贴板`))
|
||||
.catch(() => ElMessage.info(String(text)));
|
||||
} else {
|
||||
ElMessage.info(String(text));
|
||||
}
|
||||
}
|
||||
|
||||
function detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
}
|
||||
|
||||
function viewMemberHistory(row) {
|
||||
activeTab.value = "pointChangeRecord";
|
||||
searchForm.memberId = row.id;
|
||||
searchForm.memberName = row.nickName || row.username || "";
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
|
||||
function init() {
|
||||
getStatistics();
|
||||
getMemberList();
|
||||
getData();
|
||||
}
|
||||
|
||||
function getStatistics() {
|
||||
API_Member.queryMemberPointsStatistics().then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
@@ -263,45 +428,71 @@ function getStatistics() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function formatNumber(value) {
|
||||
const numericValue = Number(value || 0);
|
||||
if (!Number.isFinite(numericValue)) return "0";
|
||||
return numericValue.toLocaleString();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleHistoryReset() {
|
||||
searchForm.memberId = "";
|
||||
searchForm.memberName = "";
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
|
||||
function handleMemberSearch() {
|
||||
memberSearchForm.pageNumber = 1;
|
||||
getMemberList();
|
||||
}
|
||||
|
||||
function handleMemberReset() {
|
||||
memberSearchForm.id = "";
|
||||
memberSearchForm.nickName = "";
|
||||
memberSearchForm.username = "";
|
||||
memberSearchForm.disabled = "";
|
||||
memberSearchForm.minPoint = null;
|
||||
memberSearchForm.maxPoint = null;
|
||||
memberSearchForm.pageNumber = 1;
|
||||
getMemberList();
|
||||
}
|
||||
|
||||
function changeMemberPage(v) {
|
||||
memberSearchForm.pageNumber = v;
|
||||
getMemberList();
|
||||
}
|
||||
|
||||
function changeMemberPageSize(v) {
|
||||
memberSearchForm.pageNumber = 1;
|
||||
memberSearchForm.pageSize = v;
|
||||
getMemberList();
|
||||
}
|
||||
|
||||
function getMemberList() {
|
||||
memberLoading.value = true;
|
||||
const params = {
|
||||
pageNumber: memberSearchForm.pageNumber,
|
||||
pageSize: memberSearchForm.pageSize,
|
||||
nickName: memberSearchForm.nickName || undefined,
|
||||
username: memberSearchForm.username || undefined,
|
||||
id: memberSearchForm.id ? memberSearchForm.id.trim() : undefined,
|
||||
nickName: memberSearchForm.nickName ? memberSearchForm.nickName.trim() : undefined,
|
||||
username: memberSearchForm.username ? memberSearchForm.username.trim() : undefined,
|
||||
disabled: memberSearchForm.disabled || undefined,
|
||||
minPoint: memberSearchForm.minPoint,
|
||||
maxPoint: memberSearchForm.maxPoint,
|
||||
@@ -318,31 +509,111 @@ function getMemberList() {
|
||||
memberLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getHistoryPointData(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
const params = {
|
||||
pageNumber: searchForm.pageNumber,
|
||||
pageSize: searchForm.pageSize,
|
||||
memberId: searchForm.memberId ? searchForm.memberId.trim() : undefined,
|
||||
memberName: searchForm.memberName ? searchForm.memberName.trim() : undefined,
|
||||
};
|
||||
API_Member.getHistoryPointData(params)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.points-statistics-card {
|
||||
margin-bottom: 10px;
|
||||
.member-point-page {
|
||||
padding: 16px 20px;
|
||||
background-color: #f6f8fa;
|
||||
min-height: calc(100vh - 110px);
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
/* 顶部统计卡片 */
|
||||
.points-statistics-card {
|
||||
margin-bottom: 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.points-statistics {
|
||||
width: 100%;
|
||||
|
||||
.points-statistics-item {
|
||||
padding: 6px 0;
|
||||
|
||||
.stat-inner-box {
|
||||
text-align: center;
|
||||
padding: 12px 16px;
|
||||
background: #fbfbfc;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #f2f3f5;
|
||||
|
||||
.points-statistics-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.points-statistics-subtitle {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 18px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.points-statistics-value {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #f56c6c;
|
||||
line-height: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 选项卡容器 */
|
||||
.point-tabs-wrap {
|
||||
padding: 12px 16px 16px;
|
||||
padding: 14px 18px 18px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(23, 35, 61, 0.04);
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.point-tabs {
|
||||
@@ -352,107 +623,108 @@ onMounted(() => {
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
padding: 8px 18px;
|
||||
color: #515a6e;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item.is-active) {
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__active-bar) {
|
||||
height: 2px;
|
||||
border-radius: 2px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.points-statistics {
|
||||
width: 100%;
|
||||
}
|
||||
/* 筛选表单 */
|
||||
.point-filter-card {
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ebeef5;
|
||||
background-color: #fafbfe;
|
||||
|
||||
.points-statistics-item {
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.points-statistics-title {
|
||||
font-size: 14px;
|
||||
color: #17233d;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.points-statistics-subtitle {
|
||||
font-size: 12px;
|
||||
color: #808695;
|
||||
line-height: 18px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.points-statistics-value {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #fa6419;
|
||||
line-height: 26px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.point-content-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.member-list-card {
|
||||
:deep(.el-card__header) {
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #17233d;
|
||||
}
|
||||
|
||||
.member-table {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-table__header th.el-table__cell) {
|
||||
background: #fafafa;
|
||||
padding: 14px 0;
|
||||
font-size: 14px;
|
||||
color: #17233d;
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 16px 2px;
|
||||
}
|
||||
|
||||
:deep(.el-table__header .cell) {
|
||||
padding: 0 16px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
:deep(.el-table__body td.el-table__cell) {
|
||||
padding: 12px 0;
|
||||
}
|
||||
.el-form-item {
|
||||
margin-bottom: 14px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
:deep(.el-table__body .cell) {
|
||||
padding: 0 16px;
|
||||
line-height: 22px;
|
||||
.form-actions {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.point-range-separator {
|
||||
display: inline-block;
|
||||
margin: 0 8px;
|
||||
color: #808695;
|
||||
margin: 0 6px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.link-text {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
/* 表格区域卡片 */
|
||||
.point-content-card {
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ebeef5;
|
||||
|
||||
.card-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
.total-badge {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.face {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
/* 表格单元格 */
|
||||
.custom-point-table {
|
||||
.id-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.copy-btn {
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.point-num-text {
|
||||
font-weight: 600;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.point-change-tag {
|
||||
font-weight: 600;
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-wrap {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user