|
@@ -5,7 +5,7 @@
|
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
|
<div class="left-panel">
|
|
<div class="left-panel">
|
|
|
<span class="title">履约者池</span>
|
|
<span class="title">履约者池</span>
|
|
|
- <el-tag type="info" effect="plain" style="margin-left: 10px;">共 1205 人</el-tag>
|
|
|
|
|
|
|
+ <el-tag type="info" effect="plain" style="margin-left: 10px;">共 {{ total }} 人</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="right-panel">
|
|
<div class="right-panel">
|
|
|
<el-button type="primary" icon="Plus" style="margin-right: 15px" @click="handleCreate">新增履约者</el-button>
|
|
<el-button type="primary" icon="Plus" style="margin-right: 15px" @click="handleCreate">新增履约者</el-button>
|
|
@@ -15,17 +15,20 @@
|
|
|
class="search-input"
|
|
class="search-input"
|
|
|
prefix-icon="Search"
|
|
prefix-icon="Search"
|
|
|
clearable
|
|
clearable
|
|
|
|
|
+ @keyup.enter="handleSearch"
|
|
|
|
|
+ @clear="handleSearch"
|
|
|
/>
|
|
/>
|
|
|
- <el-select v-model="filterCity" placeholder="所属城市" style="width: 120px; margin-left: 10px;">
|
|
|
|
|
- <el-option label="所有城市" value="" />
|
|
|
|
|
- <el-option label="北京市" value="beijing" />
|
|
|
|
|
- <el-option label="上海市" value="shanghai" />
|
|
|
|
|
- <el-option label="深圳市" value="shenzhen" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-select v-model="filterStation" placeholder="所属站点" style="width: 150px; margin-left: 10px;">
|
|
|
|
|
- <el-option label="所有站点" value="" />
|
|
|
|
|
- <el-option label="北京朝阳一站" value="bj-cy-01" />
|
|
|
|
|
- <el-option label="上海浦东一站" value="sh-pd-01" />
|
|
|
|
|
|
|
+ <el-cascader
|
|
|
|
|
+ v-model="filterCascaderValue"
|
|
|
|
|
+ :options="cityCascaderOptions"
|
|
|
|
|
+ :props="{ checkStrictly: true }"
|
|
|
|
|
+ placeholder="所属城市/区域"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 200px; margin-left: 10px;"
|
|
|
|
|
+ @change="handleFilterCascaderChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-select v-model="queryParams.stationId" placeholder="所属站点" style="width: 150px; margin-left: 10px;" clearable @change="getList">
|
|
|
|
|
+ <el-option v-for="station in stationOptions" :key="station.id" :label="station.name" :value="station.id" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -39,16 +42,16 @@
|
|
|
</el-tabs>
|
|
</el-tabs>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
- <el-table :data="filteredTableData" style="width: 100%" :header-cell-style="{ background: '#f5f7fa' }">
|
|
|
|
|
|
|
+ <el-table v-loading="loading" :data="tableData" style="width: 100%" :header-cell-style="{ background: '#f5f7fa' }">
|
|
|
<el-table-column label="基本信息" width="280">
|
|
<el-table-column label="基本信息" width="280">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<div class="user-info">
|
|
<div class="user-info">
|
|
|
- <el-avatar :size="45" :src="scope.row.avatar">{{ scope.row.name.charAt(0) }}</el-avatar>
|
|
|
|
|
|
|
+ <el-avatar :size="45" :src="scope.row.avatarUrl">{{ scope.row.name.charAt(0) }}</el-avatar>
|
|
|
<div class="text-col">
|
|
<div class="text-col">
|
|
|
<div class="name-row">
|
|
<div class="name-row">
|
|
|
<span class="name">{{ scope.row.name }}</span>
|
|
<span class="name">{{ scope.row.name }}</span>
|
|
|
<span class="gender-tag">
|
|
<span class="gender-tag">
|
|
|
- <el-icon v-if="scope.row.gender === 'male'" color="#409eff"><Male /></el-icon>
|
|
|
|
|
|
|
+ <el-icon v-if="scope.row.gender === '0'" color="#409eff"><Male /></el-icon>
|
|
|
<el-icon v-else color="#f56c6c"><Female /></el-icon>
|
|
<el-icon v-else color="#f56c6c"><Female /></el-icon>
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
@@ -58,8 +61,8 @@
|
|
|
{{ scope.row.workType === 'full_time' ? '全职专送' : '兼职众包' }}
|
|
{{ scope.row.workType === 'full_time' ? '全职专送' : '兼职众包' }}
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
<!-- 等级展示 -->
|
|
<!-- 等级展示 -->
|
|
|
- <el-tag size="small" :type="getLevelType(scope.row.level)" effect="plain" class="level-tag">
|
|
|
|
|
- {{ getLevelText(scope.row.level) }}
|
|
|
|
|
|
|
+ <el-tag size="small" :type="getLevelType(scope.row.levelName)" effect="plain" class="level-tag">
|
|
|
|
|
+ {{ getLevelText(scope.row.levelName) }}
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="sub-text">{{ scope.row.age }}岁 | {{ scope.row.phone }}</div>
|
|
<div class="sub-text">{{ scope.row.age }}岁 | {{ scope.row.phone }}</div>
|
|
@@ -78,15 +81,15 @@
|
|
|
<el-icon><Medal /></el-icon> 资质证
|
|
<el-icon><Medal /></el-icon> 资质证
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="sub-text" style="margin-top:5px;">ID: {{ scope.row.idNo }}</div>
|
|
|
|
|
|
|
+ <div class="sub-text" style="margin-top:5px;">ID: {{ scope.row.idCard }}</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column label="服务区域" width="180">
|
|
<el-table-column label="服务区域" width="180">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<div class="text-col">
|
|
<div class="text-col">
|
|
|
- <span style="font-size: 13px; color: #333;">{{ scope.row.city }}</span>
|
|
|
|
|
- <span style="font-size: 12px; color: #999;">{{ scope.row.station }}</span>
|
|
|
|
|
|
|
+ <span style="font-size: 13px; color: #333;">{{ scope.row.cityName }}</span>
|
|
|
|
|
+ <span style="font-size: 12px; color: #999;">{{ scope.row.stationName }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
@@ -95,8 +98,8 @@
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<el-tag
|
|
<el-tag
|
|
|
v-for="tag in scope.row.tags"
|
|
v-for="tag in scope.row.tags"
|
|
|
- :key="tag.name"
|
|
|
|
|
- :type="tag.type"
|
|
|
|
|
|
|
+ :key="tag.id"
|
|
|
|
|
+ :type="tag.colorType"
|
|
|
size="small"
|
|
size="small"
|
|
|
class="skill-tag"
|
|
class="skill-tag"
|
|
|
effect="plain"
|
|
effect="plain"
|
|
@@ -154,8 +157,8 @@
|
|
|
|
|
|
|
|
<div class="pagination-container">
|
|
<div class="pagination-container">
|
|
|
<el-pagination
|
|
<el-pagination
|
|
|
- v-model:current-page="currentPage"
|
|
|
|
|
- v-model:page-size="pageSize"
|
|
|
|
|
|
|
+ v-model:current-page="queryParams.pageNum"
|
|
|
|
|
+ v-model:page-size="queryParams.pageSize"
|
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
:total="total"
|
|
:total="total"
|
|
@@ -176,22 +179,22 @@
|
|
|
<div class="drawer-content" v-if="currentItem">
|
|
<div class="drawer-content" v-if="currentItem">
|
|
|
<!-- 头部概览 -->
|
|
<!-- 头部概览 -->
|
|
|
<div class="user-header-card">
|
|
<div class="user-header-card">
|
|
|
- <el-avatar :size="70" :src="currentItem.avatar" class="header-avatar">{{ currentItem.name?.charAt(0) }}</el-avatar>
|
|
|
|
|
|
|
+ <el-avatar :size="70" :src="currentItem.avatarUrl" class="header-avatar">{{ currentItem.name?.charAt(0) }}</el-avatar>
|
|
|
<div class="header-info">
|
|
<div class="header-info">
|
|
|
<div class="top-row">
|
|
<div class="top-row">
|
|
|
<span class="user-name">{{ currentItem.name }}</span>
|
|
<span class="user-name">{{ currentItem.name }}</span>
|
|
|
- <el-tag size="small" :type="currentItem.gender === 'male' ? '' : 'danger'" effect="plain" round style="margin-left: 8px;">
|
|
|
|
|
- {{ currentItem.gender === 'male' ? '男' : '女' }} {{ currentItem.age }}岁
|
|
|
|
|
|
|
+ <el-tag size="small" :type="currentItem.gender === '0' ? '' : 'danger'" effect="plain" round style="margin-left: 8px;">
|
|
|
|
|
+ {{ currentItem.gender === '0' ? '男' : '女' }} {{ currentItem.age }}岁
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
<span class="status-badge" :class="currentItem.status">{{ getStatusText(currentItem.status) }}</span>
|
|
<span class="status-badge" :class="currentItem.status">{{ getStatusText(currentItem.status) }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="sub-row">
|
|
<div class="sub-row">
|
|
|
<span class="info-item"><el-icon><Iphone /></el-icon> {{ currentItem.phone }}</span>
|
|
<span class="info-item"><el-icon><Iphone /></el-icon> {{ currentItem.phone }}</span>
|
|
|
<span class="divider">|</span>
|
|
<span class="divider">|</span>
|
|
|
- <span class="info-item"><el-icon><Location /></el-icon> {{ currentItem.city }}</span>
|
|
|
|
|
|
|
+ <span class="info-item"><el-icon><Location /></el-icon> {{ currentItem.cityName }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="tags-row">
|
|
<div class="tags-row">
|
|
|
- <el-tag size="small" :type="getLevelType(currentItem.level)" effect="dark">{{ getLevelText(currentItem.level) }}</el-tag>
|
|
|
|
|
|
|
+ <el-tag size="small" :type="getLevelType(currentItem.levelName)" effect="dark">{{ getLevelText(currentItem.levelName) }}</el-tag>
|
|
|
<el-tag size="small" type="warning" effect="plain" v-if="currentItem.workType === 'full_time'" style="margin-left:5px">全职专送</el-tag>
|
|
<el-tag size="small" type="warning" effect="plain" v-if="currentItem.workType === 'full_time'" style="margin-left:5px">全职专送</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -226,11 +229,11 @@
|
|
|
<div class="section-block">
|
|
<div class="section-block">
|
|
|
<div class="section-title">基础信息</div>
|
|
<div class="section-title">基础信息</div>
|
|
|
<el-descriptions :column="2" border>
|
|
<el-descriptions :column="2" border>
|
|
|
- <el-descriptions-item label="身份证号">{{ currentItem.idNo }}</el-descriptions-item>
|
|
|
|
|
|
|
+ <el-descriptions-item label="身份证号">{{ currentItem.idCard }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="真实姓名">{{ currentItem.realName || currentItem.name }}</el-descriptions-item>
|
|
<el-descriptions-item label="真实姓名">{{ currentItem.realName || currentItem.name }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="归属站点">{{ currentItem.station }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="证件有效期">{{ currentItem.idExpiry || '2030-01-01' }}</el-descriptions-item>
|
|
|
|
|
- <el-descriptions-item label="入驻时间">2024-05-12</el-descriptions-item>
|
|
|
|
|
|
|
+ <el-descriptions-item label="归属站点">{{ currentItem.stationName }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="证件有效期">{{ currentItem.idCardExpiry || '-' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="入驻时间">{{ currentItem.createTime }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="工作性质">{{ currentItem.workType === 'full_time' ? '全职' : '兼职' }}</el-descriptions-item>
|
|
<el-descriptions-item label="工作性质">{{ currentItem.workType === 'full_time' ? '全职' : '兼职' }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
</el-descriptions>
|
|
|
</div>
|
|
</div>
|
|
@@ -238,14 +241,14 @@
|
|
|
<div class="section-block">
|
|
<div class="section-block">
|
|
|
<div class="section-title">实名认证</div>
|
|
<div class="section-title">实名认证</div>
|
|
|
<div class="cert-row">
|
|
<div class="cert-row">
|
|
|
- <div class="cert-item" @click="handleViewImage(currentItem.idCardFront)">
|
|
|
|
|
- <el-image :src="currentItem.idCardFront || ''" fit="cover" class="cert-img">
|
|
|
|
|
|
|
+ <div class="cert-item" @click="handleViewImage(currentItem.idCardFrontUrl)">
|
|
|
|
|
+ <el-image :src="currentItem.idCardFrontUrl || ''" fit="cover" class="cert-img">
|
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
|
</el-image>
|
|
</el-image>
|
|
|
<div class="cert-name">身份证人像面</div>
|
|
<div class="cert-name">身份证人像面</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="cert-item" @click="handleViewImage(currentItem.idCardBack)">
|
|
|
|
|
- <el-image :src="currentItem.idCardBack || ''" fit="cover" class="cert-img">
|
|
|
|
|
|
|
+ <div class="cert-item" @click="handleViewImage(currentItem.idCardBackUrl)">
|
|
|
|
|
+ <el-image :src="currentItem.idCardBackUrl || ''" fit="cover" class="cert-img">
|
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
|
</el-image>
|
|
</el-image>
|
|
|
<div class="cert-name">身份证国徽面</div>
|
|
<div class="cert-name">身份证国徽面</div>
|
|
@@ -255,8 +258,8 @@
|
|
|
|
|
|
|
|
<div class="section-block">
|
|
<div class="section-block">
|
|
|
<div class="section-title">资质认证</div>
|
|
<div class="section-title">资质认证</div>
|
|
|
- <div class="cert-row" v-if="currentItem.qualImages && currentItem.qualImages.length">
|
|
|
|
|
- <div class="cert-item" v-for="(img, index) in currentItem.qualImages" :key="index" @click="handleViewImage(img)">
|
|
|
|
|
|
|
+ <div class="cert-row" v-if="currentItem.qualImageUrls && currentItem.qualImageUrls.length">
|
|
|
|
|
+ <div class="cert-item" v-for="(img, index) in currentItem.qualImageUrls" :key="index" @click="handleViewImage(img)">
|
|
|
<el-image :src="img" fit="cover" class="cert-img">
|
|
<el-image :src="img" fit="cover" class="cert-img">
|
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
<template #error><div class="img-slot"><el-icon><Picture /></el-icon></div></template>
|
|
|
</el-image>
|
|
</el-image>
|
|
@@ -267,7 +270,7 @@
|
|
|
<div class="section-block">
|
|
<div class="section-block">
|
|
|
<div class="section-title">技能标签</div>
|
|
<div class="section-title">技能标签</div>
|
|
|
<div class="tag-list">
|
|
<div class="tag-list">
|
|
|
- <el-tag v-for="tag in currentItem.tags" :key="tag.name" :type="tag.type" size="large" style="margin-right: 12px; margin-bottom: 8px;">{{ tag.name }}</el-tag>
|
|
|
|
|
|
|
+ <el-tag v-for="tag in currentItem.tags" :key="tag.id" :type="tag.colorType" size="large" style="margin-right: 12px; margin-bottom: 8px;">{{ tag.name }}</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -275,7 +278,7 @@
|
|
|
|
|
|
|
|
<el-tab-pane label="服务订单" name="orders">
|
|
<el-tab-pane label="服务订单" name="orders">
|
|
|
<div class="tab-content-wrapper">
|
|
<div class="tab-content-wrapper">
|
|
|
- <el-table :data="mockOrders" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
|
|
+ <el-table :data="[]" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
<el-table-column prop="orderNo" label="订单号" width="160" show-overflow-tooltip />
|
|
<el-table-column prop="orderNo" label="订单号" width="160" show-overflow-tooltip />
|
|
|
<el-table-column prop="serviceName" label="服务项目" show-overflow-tooltip />
|
|
<el-table-column prop="serviceName" label="服务项目" show-overflow-tooltip />
|
|
|
<el-table-column prop="serviceFee" label="收入" width="100">
|
|
<el-table-column prop="serviceFee" label="收入" width="100">
|
|
@@ -297,11 +300,11 @@
|
|
|
|
|
|
|
|
<el-tab-pane label="积分记录" name="pointLogs">
|
|
<el-tab-pane label="积分记录" name="pointLogs">
|
|
|
<div class="tab-content-wrapper">
|
|
<div class="tab-content-wrapper">
|
|
|
- <el-table :data="mockPointLogs" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
- <el-table-column prop="time" label="变动时间" width="180" />
|
|
|
|
|
|
|
+ <el-table v-loading="logLoading" :data="pointsLogData" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
+ <el-table-column prop="createTime" label="变动时间" width="180" />
|
|
|
<el-table-column prop="bizType" label="业务类型" width="120">
|
|
<el-table-column prop="bizType" label="业务类型" width="120">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="getBizTypeTag(row.bizType)" size="small" effect="plain">{{ row.bizTypeName }}</el-tag>
|
|
|
|
|
|
|
+ <el-tag :type="getBizTypeTag(row.bizType)" size="small" effect="plain">{{ getBizTypeName(row.bizType) }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="amount" label="变动数值" width="120">
|
|
<el-table-column prop="amount" label="变动数值" width="120">
|
|
@@ -312,18 +315,18 @@
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="reason" label="变动原因" show-overflow-tooltip />
|
|
<el-table-column prop="reason" label="变动原因" show-overflow-tooltip />
|
|
|
- <el-table-column prop="operator" label="操作人" width="120" />
|
|
|
|
|
|
|
+ <el-table-column prop="operatorId" label="操作人" width="120" />
|
|
|
</el-table>
|
|
</el-table>
|
|
|
</div>
|
|
</div>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
|
|
|
|
|
<el-tab-pane label="余额变动" name="balanceLogs">
|
|
<el-tab-pane label="余额变动" name="balanceLogs">
|
|
|
<div class="tab-content-wrapper">
|
|
<div class="tab-content-wrapper">
|
|
|
- <el-table :data="mockBalanceLogs" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
- <el-table-column prop="time" label="变动时间" width="180" show-overflow-tooltip />
|
|
|
|
|
|
|
+ <el-table v-loading="logLoading" :data="balanceLogData" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
+ <el-table-column prop="createTime" label="变动时间" width="180" show-overflow-tooltip />
|
|
|
<el-table-column prop="subType" label="资金类型" width="120">
|
|
<el-table-column prop="subType" label="资金类型" width="120">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.amount > 0 ? 'success' : 'danger'" size="small" effect="plain">{{ row.subTypeName }}</el-tag>
|
|
|
|
|
|
|
+ <el-tag :type="getBizTypeTag(row.subType)" size="small" effect="plain">{{ getSubTypeName(row.subType) }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="amount" label="变动金额" width="120">
|
|
<el-table-column prop="amount" label="变动金额" width="120">
|
|
@@ -339,15 +342,15 @@
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="reason" label="备注说明" show-overflow-tooltip />
|
|
<el-table-column prop="reason" label="备注说明" show-overflow-tooltip />
|
|
|
- <el-table-column prop="operator" label="操作人" width="100" />
|
|
|
|
|
|
|
+ <el-table-column prop="operatorId" label="操作人" width="100" />
|
|
|
</el-table>
|
|
</el-table>
|
|
|
</div>
|
|
</div>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
|
|
|
|
|
|
<el-tab-pane label="奖惩记录" name="rewards">
|
|
<el-tab-pane label="奖惩记录" name="rewards">
|
|
|
<div class="tab-content-wrapper">
|
|
<div class="tab-content-wrapper">
|
|
|
- <el-table :data="mockRewards" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
- <el-table-column prop="time" label="操作时间" width="180" />
|
|
|
|
|
|
|
+ <el-table v-loading="logLoading" :data="rewardLogData" stripe style="width: 100%" :header-cell-style="{background:'#f5f7fa', color:'#606266'}">
|
|
|
|
|
+ <el-table-column prop="createTime" label="操作时间" width="180" />
|
|
|
<el-table-column prop="type" label="奖惩类型" width="100">
|
|
<el-table-column prop="type" label="奖惩类型" width="100">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
<el-tag :type="row.type==='reward' ? 'success' : 'danger'" size="small">{{ row.type === 'reward' ? '奖励' : '惩罚' }}</el-tag>
|
|
<el-tag :type="row.type==='reward' ? 'success' : 'danger'" size="small">{{ row.type === 'reward' ? '奖励' : '惩罚' }}</el-tag>
|
|
@@ -358,15 +361,15 @@
|
|
|
<el-tag type="info" size="small" effect="plain">{{ row.target === 'points' ? '积分' : '余额' }}</el-tag>
|
|
<el-tag type="info" size="small" effect="plain">{{ row.target === 'points' ? '积分' : '余额' }}</el-tag>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column prop="val" label="涉及数值" width="120">
|
|
|
|
|
|
|
+ <el-table-column prop="amount" label="涉及数值" width="120">
|
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
|
<span :style="{ color: row.type==='reward' ? '#67c23a' : '#f56c6c', fontWeight: 'bold' }">
|
|
<span :style="{ color: row.type==='reward' ? '#67c23a' : '#f56c6c', fontWeight: 'bold' }">
|
|
|
- {{ row.type === 'reward' ? '+' : '-' }}{{ row.val }} {{ row.target === 'points' ? '分' : '元' }}
|
|
|
|
|
|
|
+ {{ row.type === 'reward' ? '+' : '-' }}{{ row.amount }} {{ row.target === 'points' ? '分' : '元' }}
|
|
|
</span>
|
|
</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column prop="reason" label="奖惩原因" show-overflow-tooltip />
|
|
<el-table-column prop="reason" label="奖惩原因" show-overflow-tooltip />
|
|
|
- <el-table-column prop="operator" label="操作人" width="100" />
|
|
|
|
|
|
|
+ <el-table-column prop="operatorName" label="操作人" width="100" />
|
|
|
</el-table>
|
|
</el-table>
|
|
|
</div>
|
|
</div>
|
|
|
</el-tab-pane>
|
|
</el-tab-pane>
|
|
@@ -396,14 +399,14 @@
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="性别">
|
|
<el-form-item label="性别">
|
|
|
<el-radio-group v-model="editDialog.form.gender">
|
|
<el-radio-group v-model="editDialog.form.gender">
|
|
|
- <el-radio label="male">男</el-radio>
|
|
|
|
|
- <el-radio label="female">女</el-radio>
|
|
|
|
|
|
|
+ <el-radio label="0">男</el-radio>
|
|
|
|
|
+ <el-radio label="1">女</el-radio>
|
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="身份证号">
|
|
<el-form-item label="身份证号">
|
|
|
- <el-input v-model="editDialog.form.idNo" />
|
|
|
|
|
|
|
+ <el-input v-model="editDialog.form.idCard" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
@@ -411,19 +414,13 @@
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="服务城市">
|
|
<el-form-item label="服务城市">
|
|
|
- <el-select v-model="editDialog.form.city" style="width: 100%">
|
|
|
|
|
- <el-option label="北京市" value="北京市" />
|
|
|
|
|
- <el-option label="上海市" value="上海市" />
|
|
|
|
|
- <el-option label="深圳市" value="深圳市" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-cascader v-model="editDialog.cascaderValue" :options="cityCascaderOptions" :props="{ checkStrictly: true }" placeholder="请选择城市/区域" clearable style="width: 100%" @change="handleEditCascaderChange" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="归属站点">
|
|
<el-form-item label="归属站点">
|
|
|
- <el-select v-model="editDialog.form.station" style="width: 100%">
|
|
|
|
|
- <el-option label="北京朝阳一站" value="北京朝阳一站" />
|
|
|
|
|
- <el-option label="上海浦东一站" value="上海浦东一站" />
|
|
|
|
|
- <el-option label="北京海淀二站" value="北京海淀二站" />
|
|
|
|
|
|
|
+ <el-select v-model="editDialog.form.stationId" placeholder="请选择站点" style="width: 100%">
|
|
|
|
|
+ <el-option v-for="station in editDialog.stationOptions" :key="station.id" :label="station.name" :value="station.id" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
@@ -432,11 +429,7 @@
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
<el-form-item label="等级">
|
|
<el-form-item label="等级">
|
|
|
- <el-select v-model="editDialog.form.level" style="width: 100%">
|
|
|
|
|
- <el-option label="金牌" value="gold" />
|
|
|
|
|
- <el-option label="银牌" value="silver" />
|
|
|
|
|
- <el-option label="铜牌" value="bronze" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-input v-model="editDialog.form.levelId" placeholder="等级ID" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
@@ -456,13 +449,8 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="技能标签">
|
|
<el-form-item label="技能标签">
|
|
|
- <el-checkbox-group v-model="editDialog.form.tags">
|
|
|
|
|
- <el-checkbox label="行为矫正" value="行为矫正" />
|
|
|
|
|
- <el-checkbox label="驾驶" value="驾驶" />
|
|
|
|
|
- <el-checkbox label="摄影" value="摄影" />
|
|
|
|
|
- <el-checkbox label="洗护护理" value="洗护护理" />
|
|
|
|
|
- <el-checkbox label="精细美容" value="精细美容" />
|
|
|
|
|
- <el-checkbox label="基础喂遛" value="基础喂遛" />
|
|
|
|
|
|
|
+ <el-checkbox-group v-model="editDialog.form.tagIds">
|
|
|
|
|
+ <el-checkbox v-for="t in allTags" :key="t.id" :label="t.id" :value="t.id">{{ t.name }}</el-checkbox>
|
|
|
</el-checkbox-group>
|
|
</el-checkbox-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
@@ -521,21 +509,16 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="性别">
|
|
<el-form-item label="性别">
|
|
|
<el-radio-group v-model="createDialog.form.gender">
|
|
<el-radio-group v-model="createDialog.form.gender">
|
|
|
- <el-radio label="male">男</el-radio>
|
|
|
|
|
- <el-radio label="female">女</el-radio>
|
|
|
|
|
|
|
+ <el-radio label="0">男</el-radio>
|
|
|
|
|
+ <el-radio label="1">女</el-radio>
|
|
|
</el-radio-group>
|
|
</el-radio-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="服务城市">
|
|
<el-form-item label="服务城市">
|
|
|
- <el-select v-model="createDialog.form.city" style="width: 100%">
|
|
|
|
|
- <el-option label="北京市" value="北京市" />
|
|
|
|
|
- <el-option label="上海市" value="上海市" />
|
|
|
|
|
- <el-option label="深圳市" value="深圳市" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-cascader v-model="createDialog.cascaderValue" :options="cityCascaderOptions" :props="{ checkStrictly: true }" placeholder="请选择城市/区域" clearable style="width: 100%" @change="handleCreateCascaderChange" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="归属站点">
|
|
<el-form-item label="归属站点">
|
|
|
- <el-select v-model="createDialog.form.station" style="width: 100%">
|
|
|
|
|
- <el-option label="北京朝阳一站" value="北京朝阳一站" />
|
|
|
|
|
- <el-option label="上海浦东一站" value="上海浦东一站" />
|
|
|
|
|
|
|
+ <el-select v-model="createDialog.form.stationId" placeholder="请选择站点" style="width: 100%">
|
|
|
|
|
+ <el-option v-for="station in createDialog.stationOptions" :key="station.id" :label="station.name" :value="station.id" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
@@ -610,397 +593,454 @@
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-<script setup>
|
|
|
|
|
-import { ref, reactive, computed } from 'vue'
|
|
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { ref, reactive, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ listFulfiller, getFulfiller, addFulfiller, updateFulfiller,
|
|
|
|
|
+ changeStatus, resetPwd, reward, adjustPoints, adjustBalance,
|
|
|
|
|
+ listPointsLog, listBalanceLog, listRewardLog
|
|
|
|
|
+} from '@/api/fulfiller/pool'
|
|
|
|
|
+import type {
|
|
|
|
|
+ FlfFulfillerVO, FlfFulfillerForm, FlfFulfillerQuery,
|
|
|
|
|
+ FlfRewardForm, FlfAdjustPointsForm, FlfAdjustBalanceForm,
|
|
|
|
|
+ FlfPointsLogVO, FlfBalanceLogVO, FlfRewardLogVO
|
|
|
|
|
+} from '@/api/fulfiller/pool/types'
|
|
|
|
|
+import { listAllTag } from '@/api/fulfiller/tag'
|
|
|
|
|
+import type { FlfTagVO } from '@/api/fulfiller/tag/types'
|
|
|
|
|
+import { listOnStore } from '@/api/system/areaStation'
|
|
|
|
|
+import type { SysAreaStationOnStoreVo } from '@/api/system/areaStation/types'
|
|
|
|
|
+
|
|
|
|
|
+const loading = ref(false)
|
|
|
const searchKey = ref('')
|
|
const searchKey = ref('')
|
|
|
-const filterCity = ref('')
|
|
|
|
|
-const filterStation = ref('')
|
|
|
|
|
const activeTab = ref('all')
|
|
const activeTab = ref('all')
|
|
|
|
|
+const total = ref(0)
|
|
|
|
|
+const tableData = ref<FlfFulfillerVO[]>([])
|
|
|
|
|
+const allTags = ref<FlfTagVO[]>([])
|
|
|
|
|
+const areaStationList = ref<SysAreaStationOnStoreVo[]>([])
|
|
|
|
|
+const cityCascaderOptions = ref<any[]>([])
|
|
|
|
|
+const stationOptions = ref<SysAreaStationOnStoreVo[]>([])
|
|
|
|
|
+const filterCascaderValue = ref<any[]>([])
|
|
|
|
|
+
|
|
|
|
|
+const queryParams = reactive<FlfFulfillerQuery>({
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
-const currentPage = ref(1)
|
|
|
|
|
-const pageSize = ref(10)
|
|
|
|
|
-const total = ref(1205)
|
|
|
|
|
-
|
|
|
|
|
-const handleSizeChange = (val) => { console.log(`每页 ${val} 条`) }
|
|
|
|
|
-const handleCurrentChange = (val) => { console.log(`当前页: ${val}`) }
|
|
|
|
|
-
|
|
|
|
|
-// Drawer State instead of Dialog
|
|
|
|
|
|
|
+// Drawer State
|
|
|
const detailVisible = ref(false)
|
|
const detailVisible = ref(false)
|
|
|
const activeDetailTab = ref('info')
|
|
const activeDetailTab = ref('info')
|
|
|
-const currentItem = ref(null)
|
|
|
|
|
-
|
|
|
|
|
-const tableData = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 101,
|
|
|
|
|
- name: '王大力',
|
|
|
|
|
- gender: 'male',
|
|
|
|
|
- age: 28,
|
|
|
|
|
- workType: 'full_time',
|
|
|
|
|
- phone: '13566668888',
|
|
|
|
|
- level: 'gold',
|
|
|
|
|
- authId: true,
|
|
|
|
|
- authQual: true,
|
|
|
|
|
- idNo: '1101************12',
|
|
|
|
|
- city: '北京市',
|
|
|
|
|
- station: '北京朝阳一站',
|
|
|
|
|
- idExpiry: '2028-12-31',
|
|
|
|
|
- realName: '王大力',
|
|
|
|
|
- tags: [
|
|
|
|
|
- { name: '行为矫正', type: 'warning' },
|
|
|
|
|
- { name: '驾驶', type: 'info' },
|
|
|
|
|
- { name: '摄影', type: 'success' }
|
|
|
|
|
- ],
|
|
|
|
|
- points: 2450,
|
|
|
|
|
- balance: 1280.50,
|
|
|
|
|
- orderCount: 1240,
|
|
|
|
|
- rejectCount: 2,
|
|
|
|
|
- status: 'busy',
|
|
|
|
|
- avatar: '',
|
|
|
|
|
- idCardFront: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg',
|
|
|
|
|
- idCardBack: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg',
|
|
|
|
|
- qualImages: [
|
|
|
|
|
- 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg',
|
|
|
|
|
- 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
|
|
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 102,
|
|
|
|
|
- name: '张小美',
|
|
|
|
|
- gender: 'female',
|
|
|
|
|
- age: 24,
|
|
|
|
|
- workType: 'part_time',
|
|
|
|
|
- phone: '13612345678',
|
|
|
|
|
- level: 'silver',
|
|
|
|
|
- authId: true,
|
|
|
|
|
- authQual: false,
|
|
|
|
|
- idNo: '3101************34',
|
|
|
|
|
- city: '上海市',
|
|
|
|
|
- station: '上海浦东一站',
|
|
|
|
|
- tags: [
|
|
|
|
|
- { name: '洗护护理', type: 'primary' },
|
|
|
|
|
- { name: '精细美容', type: 'danger' }
|
|
|
|
|
- ],
|
|
|
|
|
- points: 890,
|
|
|
|
|
- balance: 320.00,
|
|
|
|
|
- orderCount: 450,
|
|
|
|
|
- rejectCount: 0,
|
|
|
|
|
- status: 'resting',
|
|
|
|
|
- avatar: '',
|
|
|
|
|
- idCardFront: 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg'
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 103,
|
|
|
|
|
- name: '李建国',
|
|
|
|
|
- gender: 'male',
|
|
|
|
|
- age: 35,
|
|
|
|
|
- workType: 'full_time',
|
|
|
|
|
- phone: '13987654321',
|
|
|
|
|
- level: 'bronze',
|
|
|
|
|
- authId: true,
|
|
|
|
|
- authQual: true,
|
|
|
|
|
- idNo: '1101************99',
|
|
|
|
|
- city: '北京市',
|
|
|
|
|
- station: '北京海淀二站',
|
|
|
|
|
- tags: [
|
|
|
|
|
- { name: '基础喂遛', type: '' }
|
|
|
|
|
- ],
|
|
|
|
|
- points: 120,
|
|
|
|
|
- balance: 50.00,
|
|
|
|
|
- orderCount: 56,
|
|
|
|
|
- rejectCount: 5,
|
|
|
|
|
- status: 'disabled',
|
|
|
|
|
- avatar: ''
|
|
|
|
|
|
|
+const currentItem = ref<FlfFulfillerVO | null>(null)
|
|
|
|
|
+
|
|
|
|
|
+// Log data for detail tabs
|
|
|
|
|
+const pointsLogData = ref<FlfPointsLogVO[]>([])
|
|
|
|
|
+const balanceLogData = ref<FlfBalanceLogVO[]>([])
|
|
|
|
|
+const rewardLogData = ref<FlfRewardLogVO[]>([])
|
|
|
|
|
+const logLoading = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+/** 查询列表 */
|
|
|
|
|
+const getList = async () => {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const params: FlfFulfillerQuery = {
|
|
|
|
|
+ pageNum: queryParams.pageNum,
|
|
|
|
|
+ pageSize: queryParams.pageSize,
|
|
|
|
|
+ status: activeTab.value === 'all' ? undefined : activeTab.value,
|
|
|
|
|
+ keyword: searchKey.value || undefined,
|
|
|
|
|
+ cityCode: queryParams.cityCode || undefined,
|
|
|
|
|
+ stationId: queryParams.stationId || undefined
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await listFulfiller(params)
|
|
|
|
|
+ tableData.value = res.rows
|
|
|
|
|
+ total.value = res.total
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 加载全部标签(选择器用) */
|
|
|
|
|
+const loadAllTags = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listAllTag({ category: 'fulfiller' })
|
|
|
|
|
+ allTags.value = res.data || []
|
|
|
|
|
+ } catch { /* ignore */ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 加载区域站点数据并构建级联树 */
|
|
|
|
|
+const loadAreaStations = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listOnStore()
|
|
|
|
|
+ const list = res.data || []
|
|
|
|
|
+ areaStationList.value = list
|
|
|
|
|
+ // 构建城市→区域的级联树(不含站点type=2)
|
|
|
|
|
+ const cities = list.filter(item => item.type === 0)
|
|
|
|
|
+ cityCascaderOptions.value = cities.map(city => {
|
|
|
|
|
+ const districts = list.filter(d => d.parentId == city.id && d.type === 1)
|
|
|
|
|
+ return {
|
|
|
|
|
+ value: city.id,
|
|
|
|
|
+ label: city.name,
|
|
|
|
|
+ children: districts.length > 0 ? districts.map(d => ({ value: d.id, label: d.name })) : undefined
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } catch { /* ignore */ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 根据级联选择的最后一级ID加载站点列表 */
|
|
|
|
|
+const loadStationsByAreaId = (areaId: string | number) => {
|
|
|
|
|
+ stationOptions.value = areaStationList.value.filter(item => item.parentId == areaId && item.type === 2)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 根据级联值获取cityCode和cityName */
|
|
|
|
|
+const getCityInfoFromCascader = (cascaderValue: any[]) => {
|
|
|
|
|
+ if (!cascaderValue || cascaderValue.length === 0) return { cityCode: '', cityName: '' }
|
|
|
|
|
+ const lastId = cascaderValue[cascaderValue.length - 1]
|
|
|
|
|
+ const names: string[] = []
|
|
|
|
|
+ for (const id of cascaderValue) {
|
|
|
|
|
+ const item = areaStationList.value.find(i => i.id == id)
|
|
|
|
|
+ if (item) names.push(item.name || '')
|
|
|
}
|
|
}
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-// Mock Data for Detail Tabs
|
|
|
|
|
-const mockOrders = ref([
|
|
|
|
|
- { orderNo: 'ORD20240204001', serviceName: '上门洗澡-中型犬', amount: 88, serviceFee: 66, time: '2024-02-04 10:00', status: 'completed' },
|
|
|
|
|
- { orderNo: 'ORD20240204002', serviceName: '家庭寄养-3天', amount: 240, serviceFee: 190, time: '2024-02-03 14:30', status: 'completed' },
|
|
|
|
|
- { orderNo: 'ORD20240203009', serviceName: '遛狗服务-1小时', amount: 45, serviceFee: 0, time: '2024-02-03 09:00', status: 'cancelled' },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const mockRewards = ref([
|
|
|
|
|
- { time: '2024-02-01 10:00:00', type: 'reward', target: 'points', val: 20, reason: '月度全勤奖励', operator: '系统' },
|
|
|
|
|
- { time: '2024-01-25 15:30:12', type: 'punish', target: 'balance', val: 50.00, reason: '用户投诉服务态度差', operator: 'admin' },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const mockBalanceLogs = ref([
|
|
|
|
|
- { time: '2024-02-04 11:30:00', subType: 'settle', subTypeName: '服务结算', amount: 66.00, balanceAfter: 1280.50, reason: '订单ORD20240204001结算', operator: '系统' },
|
|
|
|
|
- { time: '2024-02-04 10:00:00', subType: 'withdraw', subTypeName: '提现', amount: -200.00, balanceAfter: 1214.50, reason: '用户提现申请', operator: '系统' },
|
|
|
|
|
- { time: '2024-02-03 15:00:00', subType: 'salary', subTypeName: '工资发放', amount: 3500.00, balanceAfter: 4714.50, reason: '2024年1月工资发放', operator: '财务' },
|
|
|
|
|
- { time: '2024-01-25 15:30:12', subType: 'punish', subTypeName: '惩罚', amount: -50.00, balanceAfter: 1224.50, reason: '用户投诉服务态度差', operator: 'admin' },
|
|
|
|
|
- { time: '2024-01-20 10:00:00', subType: 'reward', subTypeName: '奖励', amount: 100.00, balanceAfter: 1274.50, reason: '季度优秀员工奖励', operator: 'admin' },
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const mockPointLogs = ref([
|
|
|
|
|
- { time: '2024-02-04 10:00:00', bizType: 'order', bizTypeName: '订单完成', amount: 50, reason: '完成订单 ORD20240204001', operator: '系统' },
|
|
|
|
|
- { time: '2024-02-03 12:00:00', bizType: 'reward', bizTypeName: '奖励', amount: 20, reason: '获得用户5星好评', operator: '系统' },
|
|
|
|
|
- { time: '2024-02-01 10:00:00', bizType: 'other', bizTypeName: '其他', amount: 100, reason: '系统补偿', operator: '系统' },
|
|
|
|
|
- { time: '2024-01-20 15:30:00', bizType: 'punish', bizTypeName: '惩罚', amount: -10, reason: '接单后无故取消', operator: 'admin' }
|
|
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-const getBizTypeTag = (type) => {
|
|
|
|
|
- // Points: order, reward, punish, other
|
|
|
|
|
- // Balance: settle, withdraw, salary, reward, punish, other
|
|
|
|
|
- const map = {
|
|
|
|
|
- order: '',
|
|
|
|
|
- reward: 'success',
|
|
|
|
|
- salary: 'success',
|
|
|
|
|
- settle: 'success',
|
|
|
|
|
- punish: 'danger',
|
|
|
|
|
- withdraw: 'warning',
|
|
|
|
|
- other: 'info'
|
|
|
|
|
|
|
+ return { cityCode: String(lastId), cityName: names.join(' ') }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 搜索框回车/清除触发查询 */
|
|
|
|
|
+const handleSearch = () => {
|
|
|
|
|
+ queryParams.pageNum = 1
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleSizeChange = (val: number) => { queryParams.pageSize = val; getList() }
|
|
|
|
|
+const handleCurrentChange = (val: number) => { queryParams.pageNum = val; getList() }
|
|
|
|
|
+
|
|
|
|
|
+/** 积分业务类型标签颜色 */
|
|
|
|
|
+const getBizTypeTag = (type: string) => {
|
|
|
|
|
+ const map: Record<string, string> = {
|
|
|
|
|
+ order: '', reward: 'success', admin_reward: 'success',
|
|
|
|
|
+ punish: 'danger', admin_punish: 'danger',
|
|
|
|
|
+ adjust: 'info', admin_adjust: 'info'
|
|
|
}
|
|
}
|
|
|
return map[type] || 'info'
|
|
return map[type] || 'info'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/** 积分业务类型中文名称 */
|
|
|
|
|
+const getBizTypeName = (type: string) => {
|
|
|
|
|
+ const map: Record<string, string> = {
|
|
|
|
|
+ order: '订单',
|
|
|
|
|
+ reward: '奖励',
|
|
|
|
|
+ punish: '惩罚',
|
|
|
|
|
+ adjust: '手动调整',
|
|
|
|
|
+ admin_adjust: '后台调整',
|
|
|
|
|
+ admin_reward: '后台奖励',
|
|
|
|
|
+ admin_punish: '后台惩罚'
|
|
|
|
|
+ }
|
|
|
|
|
+ return map[type] || type
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 余额资金类型中文名称 */
|
|
|
|
|
+const getSubTypeName = (type: string) => {
|
|
|
|
|
+ const map: Record<string, string> = {
|
|
|
|
|
+ reward: '奖励',
|
|
|
|
|
+ punish: '惩罚',
|
|
|
|
|
+ salary: '工资发放',
|
|
|
|
|
+ withdraw: '提现',
|
|
|
|
|
+ settle: '结算',
|
|
|
|
|
+ other: '其他',
|
|
|
|
|
+ admin_adjust: '后台调整',
|
|
|
|
|
+ admin_reward: '后台奖励',
|
|
|
|
|
+ admin_punish: '后台惩罚'
|
|
|
|
|
+ }
|
|
|
|
|
+ return map[type] || type
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const rewardDialog = reactive({
|
|
const rewardDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
userName: '',
|
|
userName: '',
|
|
|
- form: {
|
|
|
|
|
- type: 'punish',
|
|
|
|
|
- target: 'points',
|
|
|
|
|
- amount: 10,
|
|
|
|
|
- reason: ''
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ fulfillerId: 0 as number | string,
|
|
|
|
|
+ form: { type: 'reward', target: 'points', amount: 10, reason: '' }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const editDialog = reactive({
|
|
const editDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
- form: {
|
|
|
|
|
- id: null,
|
|
|
|
|
- name: '',
|
|
|
|
|
- phone: '',
|
|
|
|
|
- gender: '',
|
|
|
|
|
- idNo: '',
|
|
|
|
|
- city: '',
|
|
|
|
|
- station: '',
|
|
|
|
|
- level: '',
|
|
|
|
|
- status: '',
|
|
|
|
|
- authId: false,
|
|
|
|
|
- authQual: false,
|
|
|
|
|
- tags: []
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ form: {} as FlfFulfillerForm,
|
|
|
|
|
+ cascaderValue: [] as any[],
|
|
|
|
|
+ stationOptions: [] as SysAreaStationOnStoreVo[]
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const createDialog = reactive({
|
|
const createDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
form: {
|
|
form: {
|
|
|
- name: '',
|
|
|
|
|
- phone: '',
|
|
|
|
|
- password: '',
|
|
|
|
|
- city: '北京市',
|
|
|
|
|
- station: '',
|
|
|
|
|
- gender: 'male'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ name: '', phone: '', password: '', cityCode: '', cityName: '', stationId: undefined as any, gender: '0', workType: 'full_time'
|
|
|
|
|
+ },
|
|
|
|
|
+ cascaderValue: [] as any[],
|
|
|
|
|
+ stationOptions: [] as SysAreaStationOnStoreVo[]
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const pointsDialog = reactive({
|
|
const pointsDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
- currentRow: null,
|
|
|
|
|
|
|
+ currentRow: null as FlfFulfillerVO | null,
|
|
|
form: { type: 'add', amount: 0, reason: '' }
|
|
form: { type: 'add', amount: 0, reason: '' }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const balanceDialog = reactive({
|
|
const balanceDialog = reactive({
|
|
|
visible: false,
|
|
visible: false,
|
|
|
- currentRow: null,
|
|
|
|
|
|
|
+ currentRow: null as FlfFulfillerVO | null,
|
|
|
form: { type: 'add', subType: 'reward', amount: 0, reason: '' }
|
|
form: { type: 'add', subType: 'reward', amount: 0, reason: '' }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const getStatusText = (status) => {
|
|
|
|
|
- const map = { busy: '接单中', resting: '休息', disabled: '禁用', frozen: '冻结' }
|
|
|
|
|
|
|
+const getStatusText = (status: string) => {
|
|
|
|
|
+ const map: Record<string, string> = { busy: '接单中', resting: '休息', disabled: '禁用', frozen: '冻结' }
|
|
|
return map[status] || '未知'
|
|
return map[status] || '未知'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getLevelText = (level) => {
|
|
|
|
|
- const map = { gold: 'Lv.3 金牌', silver: 'Lv.2 银牌', bronze: 'Lv.1 铜牌' }
|
|
|
|
|
- return map[level] || 'Lv.0 普通'
|
|
|
|
|
|
|
+const getLevelText = (levelName: string) => {
|
|
|
|
|
+ return levelName || '普通'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getLevelType = (level) => {
|
|
|
|
|
- const map = { gold: 'warning', silver: 'info', bronze: 'danger' }
|
|
|
|
|
- return map[level] || 'info'
|
|
|
|
|
|
|
+const getLevelType = (levelName: string) => {
|
|
|
|
|
+ if (!levelName) return 'info'
|
|
|
|
|
+ if (levelName.includes('金')) return 'warning'
|
|
|
|
|
+ if (levelName.includes('银')) return 'info'
|
|
|
|
|
+ if (levelName.includes('铜')) return 'danger'
|
|
|
|
|
+ return 'info'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleTabClick = (tab) => {
|
|
|
|
|
- console.log('Tab switch:', tab.props.name)
|
|
|
|
|
|
|
+const handleTabClick = (tab: any) => {
|
|
|
|
|
+ activeTab.value = tab.paneName
|
|
|
|
|
+ queryParams.pageNum = 1
|
|
|
|
|
+ getList()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const filteredTableData = computed(() => {
|
|
|
|
|
- let data = tableData.value
|
|
|
|
|
- // Status Filter
|
|
|
|
|
- if (activeTab.value !== 'all') {
|
|
|
|
|
- data = data.filter(item => item.status === activeTab.value)
|
|
|
|
|
|
|
+/** 加载日志数据 */
|
|
|
|
|
+const loadLogs = async (fulfillerId: string | number) => {
|
|
|
|
|
+ logLoading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const [pRes, bRes, rRes] = await Promise.all([
|
|
|
|
|
+ listPointsLog(fulfillerId, { pageNum: 1, pageSize: 20 }),
|
|
|
|
|
+ listBalanceLog(fulfillerId, { pageNum: 1, pageSize: 20 }),
|
|
|
|
|
+ listRewardLog(fulfillerId, { pageNum: 1, pageSize: 20 })
|
|
|
|
|
+ ])
|
|
|
|
|
+ pointsLogData.value = pRes.rows || []
|
|
|
|
|
+ balanceLogData.value = bRes.rows || []
|
|
|
|
|
+ rewardLogData.value = rRes.rows || []
|
|
|
|
|
+ } catch { /* ignore */ } finally {
|
|
|
|
|
+ logLoading.value = false
|
|
|
}
|
|
}
|
|
|
- // Search Filter
|
|
|
|
|
- if (searchKey.value) {
|
|
|
|
|
- const key = searchKey.value.toLowerCase()
|
|
|
|
|
- data = data.filter(item =>
|
|
|
|
|
- item.name.toLowerCase().includes(key) ||
|
|
|
|
|
- item.phone.includes(key) ||
|
|
|
|
|
- (item.idNo && item.idNo.includes(key))
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- // City & Station Filter
|
|
|
|
|
- if (filterCity.value) {
|
|
|
|
|
- // Simple mapping for demo, usually would match exact city code/name
|
|
|
|
|
- if(filterCity.value === 'beijing') data = data.filter(item => item.city.includes('北京'))
|
|
|
|
|
- if(filterCity.value === 'shanghai') data = data.filter(item => item.city.includes('上海'))
|
|
|
|
|
- if(filterCity.value === 'shenzhen') data = data.filter(item => item.city.includes('深圳'))
|
|
|
|
|
- }
|
|
|
|
|
- if (filterStation.value) {
|
|
|
|
|
- if(filterStation.value === 'bj-cy-01') data = data.filter(item => item.station.includes('北京朝阳一站'))
|
|
|
|
|
- if(filterStation.value === 'sh-pd-01') data = data.filter(item => item.station.includes('上海浦东一站'))
|
|
|
|
|
- }
|
|
|
|
|
- return data
|
|
|
|
|
-})
|
|
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const handleDetail = (row) => {
|
|
|
|
|
- currentItem.value = row
|
|
|
|
|
|
|
+const handleDetail = async (row: FlfFulfillerVO) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getFulfiller(row.id)
|
|
|
|
|
+ currentItem.value = res.data
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ currentItem.value = { ...row }
|
|
|
|
|
+ }
|
|
|
activeDetailTab.value = 'info'
|
|
activeDetailTab.value = 'info'
|
|
|
detailVisible.value = true
|
|
detailVisible.value = true
|
|
|
|
|
+ loadLogs(row.id)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleEdit = (row) => {
|
|
|
|
|
|
|
+const handleEdit = (row: FlfFulfillerVO) => {
|
|
|
editDialog.form = {
|
|
editDialog.form = {
|
|
|
id: row.id,
|
|
id: row.id,
|
|
|
name: row.name,
|
|
name: row.name,
|
|
|
phone: row.phone,
|
|
phone: row.phone,
|
|
|
- password: '', // Reset password field
|
|
|
|
|
gender: row.gender,
|
|
gender: row.gender,
|
|
|
- idNo: row.idNo,
|
|
|
|
|
- city: row.city,
|
|
|
|
|
- station: row.station,
|
|
|
|
|
- level: row.level,
|
|
|
|
|
|
|
+ idCard: row.idCard,
|
|
|
|
|
+ cityCode: row.cityCode,
|
|
|
|
|
+ cityName: row.cityName,
|
|
|
|
|
+ stationId: row.stationId,
|
|
|
|
|
+ levelId: row.levelId,
|
|
|
status: row.status,
|
|
status: row.status,
|
|
|
authId: row.authId,
|
|
authId: row.authId,
|
|
|
authQual: row.authQual,
|
|
authQual: row.authQual,
|
|
|
- tags: row.tags ? row.tags.map(t => t.name) : []
|
|
|
|
|
|
|
+ tagIds: row.tags ? row.tags.map(t => t.id) : []
|
|
|
|
|
+ }
|
|
|
|
|
+ // 根据cityCode构建级联选择器的值
|
|
|
|
|
+ editDialog.cascaderValue = []
|
|
|
|
|
+ editDialog.stationOptions = []
|
|
|
|
|
+ if (row.cityCode) {
|
|
|
|
|
+ const item = areaStationList.value.find(i => String(i.id) === row.cityCode)
|
|
|
|
|
+ if (item) {
|
|
|
|
|
+ if (item.type === 1 && item.parentId) {
|
|
|
|
|
+ // 区域级:cascaderValue = [城市ID, 区域ID]
|
|
|
|
|
+ editDialog.cascaderValue = [item.parentId, item.id]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 城市级:cascaderValue = [城市ID]
|
|
|
|
|
+ editDialog.cascaderValue = [item.id]
|
|
|
|
|
+ }
|
|
|
|
|
+ loadStationsByAreaId(item.id)
|
|
|
|
|
+ editDialog.stationOptions = stationOptions.value
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
editDialog.visible = true
|
|
editDialog.visible = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleCreate = () => {
|
|
const handleCreate = () => {
|
|
|
- createDialog.form = { name: '', phone: '', password: '', city: '北京市', station: '', gender: 'male' }
|
|
|
|
|
|
|
+ createDialog.form = { name: '', phone: '', password: '', cityCode: '', cityName: '', stationId: undefined, gender: '0', workType: 'full_time' }
|
|
|
|
|
+ createDialog.cascaderValue = []
|
|
|
|
|
+ createDialog.stationOptions = []
|
|
|
createDialog.visible = true
|
|
createDialog.visible = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const submitCreate = () => {
|
|
|
|
|
- if(!createDialog.form.name || !createDialog.form.phone || !createDialog.form.password) {
|
|
|
|
|
|
|
+const submitCreate = async () => {
|
|
|
|
|
+ if (!createDialog.form.name || !createDialog.form.phone || !createDialog.form.password) {
|
|
|
ElMessage.warning('请填写完整信息')
|
|
ElMessage.warning('请填写完整信息')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- // Simulate creation
|
|
|
|
|
- tableData.value.unshift({
|
|
|
|
|
- id: Date.now(),
|
|
|
|
|
- ...createDialog.form,
|
|
|
|
|
- level: 'bronze',
|
|
|
|
|
- status: 'online',
|
|
|
|
|
- points: 0,
|
|
|
|
|
- balance: 0,
|
|
|
|
|
- orderCount: 0,
|
|
|
|
|
- rejectCount: 0,
|
|
|
|
|
- status: 'online',
|
|
|
|
|
- avatar: '',
|
|
|
|
|
- authId: false,
|
|
|
|
|
- authQual: false,
|
|
|
|
|
- tags: []
|
|
|
|
|
- })
|
|
|
|
|
- createDialog.visible = false
|
|
|
|
|
- ElMessage.success('创建成功')
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ await addFulfiller(createDialog.form as FlfFulfillerForm)
|
|
|
|
|
+ createDialog.visible = false
|
|
|
|
|
+ ElMessage.success('创建成功')
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const saveEdit = () => {
|
|
|
|
|
- const idx = tableData.value.findIndex(item => item.id === editDialog.form.id)
|
|
|
|
|
- if(idx !== -1) {
|
|
|
|
|
- // update basic info
|
|
|
|
|
- const target = tableData.value[idx]
|
|
|
|
|
- target.name = editDialog.form.name
|
|
|
|
|
- target.phone = editDialog.form.phone
|
|
|
|
|
- target.gender = editDialog.form.gender
|
|
|
|
|
- target.idNo = editDialog.form.idNo
|
|
|
|
|
- target.city = editDialog.form.city
|
|
|
|
|
- target.station = editDialog.form.station
|
|
|
|
|
- target.level = editDialog.form.level
|
|
|
|
|
- target.status = editDialog.form.status
|
|
|
|
|
- target.authId = editDialog.form.authId
|
|
|
|
|
- target.authQual = editDialog.form.authQual
|
|
|
|
|
-
|
|
|
|
|
- // update tags
|
|
|
|
|
- // simplistic approach: find predefined colors or default 'info'
|
|
|
|
|
- const colorMap = { '行为矫正': 'warning', '驾驶': 'info', '摄影': 'success', '洗护护理': 'primary', '精细美容': 'danger' }
|
|
|
|
|
- target.tags = editDialog.form.tags.map(t => ({ name: t, type: colorMap[t] || 'info' }))
|
|
|
|
|
-
|
|
|
|
|
|
|
+const saveEdit = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await updateFulfiller(editDialog.form)
|
|
|
ElMessage.success('更新成功')
|
|
ElMessage.success('更新成功')
|
|
|
- }
|
|
|
|
|
- editDialog.visible = false
|
|
|
|
|
|
|
+ editDialog.visible = false
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleReward = (row) => {
|
|
|
|
|
|
|
+const handleReward = (row: FlfFulfillerVO) => {
|
|
|
rewardDialog.userName = row.name
|
|
rewardDialog.userName = row.name
|
|
|
|
|
+ rewardDialog.fulfillerId = row.id
|
|
|
rewardDialog.form = { type: 'reward', target: 'points', amount: 10, reason: '' }
|
|
rewardDialog.form = { type: 'reward', target: 'points', amount: 10, reason: '' }
|
|
|
rewardDialog.visible = true
|
|
rewardDialog.visible = true
|
|
|
}
|
|
}
|
|
|
-const submitReward = () => {
|
|
|
|
|
- ElMessage.success('操作成功')
|
|
|
|
|
- rewardDialog.visible = false
|
|
|
|
|
|
|
+
|
|
|
|
|
+const submitReward = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await reward({
|
|
|
|
|
+ fulfillerId: rewardDialog.fulfillerId,
|
|
|
|
|
+ type: rewardDialog.form.type,
|
|
|
|
|
+ target: rewardDialog.form.target,
|
|
|
|
|
+ amount: rewardDialog.form.amount,
|
|
|
|
|
+ reason: rewardDialog.form.reason
|
|
|
|
|
+ })
|
|
|
|
|
+ ElMessage.success('操作成功')
|
|
|
|
|
+ rewardDialog.visible = false
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleCommand = (cmd, row) => {
|
|
|
|
|
- if(cmd === 'adjustPoints') {
|
|
|
|
|
|
|
+const handleCommand = async (cmd: string, row: FlfFulfillerVO) => {
|
|
|
|
|
+ if (cmd === 'adjustPoints') {
|
|
|
pointsDialog.currentRow = row
|
|
pointsDialog.currentRow = row
|
|
|
pointsDialog.form = { type: 'add', amount: 0, reason: '' }
|
|
pointsDialog.form = { type: 'add', amount: 0, reason: '' }
|
|
|
pointsDialog.visible = true
|
|
pointsDialog.visible = true
|
|
|
- } else if(cmd === 'adjustBalance') {
|
|
|
|
|
|
|
+ } else if (cmd === 'adjustBalance') {
|
|
|
balanceDialog.currentRow = row
|
|
balanceDialog.currentRow = row
|
|
|
balanceDialog.form = { type: 'add', subType: 'reward', amount: 0, reason: '' }
|
|
balanceDialog.form = { type: 'add', subType: 'reward', amount: 0, reason: '' }
|
|
|
balanceDialog.visible = true
|
|
balanceDialog.visible = true
|
|
|
- } else if(cmd === 'disable') {
|
|
|
|
|
- ElMessageBox.confirm(`确定禁用履约者【${row.name}】吗?禁用后将无法接单。`, '提示', { type: 'warning' })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- row.status = 'disabled'
|
|
|
|
|
- ElMessage.success('账号已禁用')
|
|
|
|
|
- })
|
|
|
|
|
- } else if(cmd === 'enable') {
|
|
|
|
|
- ElMessageBox.confirm(`确定启用履约者【${row.name}】吗?`, '提示', { type: 'success' })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- row.status = 'resting' // Default to resting when enabled
|
|
|
|
|
- ElMessage.success('账号已启用')
|
|
|
|
|
- })
|
|
|
|
|
- } else if(cmd === 'resetPwd') {
|
|
|
|
|
- ElMessageBox.confirm('确定重置密码为默认密码 [123456] 吗?', '提示', { type: 'info' })
|
|
|
|
|
- .then(() => { ElMessage.success('密码重置成功') })
|
|
|
|
|
|
|
+ } else if (cmd === 'disable') {
|
|
|
|
|
+ await ElMessageBox.confirm(`确定禁用履约者【${row.name}】吗?禁用后将无法接单。`, '提示', { type: 'warning' })
|
|
|
|
|
+ try {
|
|
|
|
|
+ await changeStatus(row.id, 'disabled')
|
|
|
|
|
+ ElMessage.success('账号已禁用')
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
|
|
+ } else if (cmd === 'enable') {
|
|
|
|
|
+ await ElMessageBox.confirm(`确定启用履约者【${row.name}】吗?`, '提示', { type: 'success' })
|
|
|
|
|
+ try {
|
|
|
|
|
+ await changeStatus(row.id, 'resting')
|
|
|
|
|
+ ElMessage.success('账号已启用')
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
|
|
+ } else if (cmd === 'resetPwd') {
|
|
|
|
|
+ await ElMessageBox.confirm('确定重置密码为默认密码 [123456] 吗?', '提示', { type: 'info' })
|
|
|
|
|
+ try {
|
|
|
|
|
+ await resetPwd(row.id, '123456')
|
|
|
|
|
+ ElMessage.success('密码重置成功')
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const submitPointsAdjust = () => {
|
|
|
|
|
- if(pointsDialog.currentRow) {
|
|
|
|
|
- if(pointsDialog.form.type === 'add') pointsDialog.currentRow.points += pointsDialog.form.amount
|
|
|
|
|
- else pointsDialog.currentRow.points -= pointsDialog.form.amount
|
|
|
|
|
|
|
+const submitPointsAdjust = async () => {
|
|
|
|
|
+ if (!pointsDialog.currentRow) return
|
|
|
|
|
+ try {
|
|
|
|
|
+ await adjustPoints({
|
|
|
|
|
+ fulfillerId: pointsDialog.currentRow.id,
|
|
|
|
|
+ type: pointsDialog.form.type,
|
|
|
|
|
+ amount: pointsDialog.form.amount,
|
|
|
|
|
+ reason: pointsDialog.form.reason
|
|
|
|
|
+ })
|
|
|
ElMessage.success('积分调整成功')
|
|
ElMessage.success('积分调整成功')
|
|
|
- }
|
|
|
|
|
- pointsDialog.visible = false
|
|
|
|
|
|
|
+ pointsDialog.visible = false
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const submitBalanceAdjust = () => {
|
|
|
|
|
- if(balanceDialog.currentRow) {
|
|
|
|
|
- let amt = balanceDialog.form.amount
|
|
|
|
|
- if(balanceDialog.form.type === 'reduce') amt = -amt
|
|
|
|
|
- balanceDialog.currentRow.balance += amt
|
|
|
|
|
- // keep 2 decimals
|
|
|
|
|
- balanceDialog.currentRow.balance = Math.round(balanceDialog.currentRow.balance * 100) / 100
|
|
|
|
|
|
|
+const submitBalanceAdjust = async () => {
|
|
|
|
|
+ if (!balanceDialog.currentRow) return
|
|
|
|
|
+ try {
|
|
|
|
|
+ await adjustBalance({
|
|
|
|
|
+ fulfillerId: balanceDialog.currentRow.id,
|
|
|
|
|
+ type: balanceDialog.form.type,
|
|
|
|
|
+ subType: balanceDialog.form.subType,
|
|
|
|
|
+ amount: balanceDialog.form.amount,
|
|
|
|
|
+ reason: balanceDialog.form.reason
|
|
|
|
|
+ })
|
|
|
ElMessage.success('余额调整成功')
|
|
ElMessage.success('余额调整成功')
|
|
|
- }
|
|
|
|
|
- balanceDialog.visible = false
|
|
|
|
|
|
|
+ balanceDialog.visible = false
|
|
|
|
|
+ getList()
|
|
|
|
|
+ } catch { /* handled by interceptor */ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleViewImage = (url) => {
|
|
|
|
|
|
|
+const handleViewImage = (url: string) => {
|
|
|
// Already handled by el-image preview
|
|
// Already handled by el-image preview
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/** 顶部筛选:级联选择变化时 */
|
|
|
|
|
+const handleFilterCascaderChange = (val: any[]) => {
|
|
|
|
|
+ if (val && val.length > 0) {
|
|
|
|
|
+ const lastId = val[val.length - 1]
|
|
|
|
|
+ queryParams.cityCode = String(lastId)
|
|
|
|
|
+ loadStationsByAreaId(lastId)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ queryParams.cityCode = undefined
|
|
|
|
|
+ stationOptions.value = []
|
|
|
|
|
+ }
|
|
|
|
|
+ queryParams.stationId = undefined
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 编辑对话框:级联选择变化时 */
|
|
|
|
|
+const handleEditCascaderChange = (val: any[]) => {
|
|
|
|
|
+ const { cityCode, cityName } = getCityInfoFromCascader(val)
|
|
|
|
|
+ editDialog.form.cityCode = cityCode
|
|
|
|
|
+ editDialog.form.cityName = cityName
|
|
|
|
|
+ if (val && val.length > 0) {
|
|
|
|
|
+ const lastId = val[val.length - 1]
|
|
|
|
|
+ loadStationsByAreaId(lastId)
|
|
|
|
|
+ editDialog.stationOptions = stationOptions.value
|
|
|
|
|
+ } else {
|
|
|
|
|
+ editDialog.stationOptions = []
|
|
|
|
|
+ }
|
|
|
|
|
+ editDialog.form.stationId = undefined
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 新增对话框:级联选择变化时 */
|
|
|
|
|
+const handleCreateCascaderChange = (val: any[]) => {
|
|
|
|
|
+ const { cityCode, cityName } = getCityInfoFromCascader(val)
|
|
|
|
|
+ createDialog.form.cityCode = cityCode
|
|
|
|
|
+ createDialog.form.cityName = cityName
|
|
|
|
|
+ if (val && val.length > 0) {
|
|
|
|
|
+ const lastId = val[val.length - 1]
|
|
|
|
|
+ loadStationsByAreaId(lastId)
|
|
|
|
|
+ createDialog.stationOptions = stationOptions.value
|
|
|
|
|
+ } else {
|
|
|
|
|
+ createDialog.stationOptions = []
|
|
|
|
|
+ }
|
|
|
|
|
+ createDialog.form.stationId = undefined
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getList()
|
|
|
|
|
+ loadAllTags()
|
|
|
|
|
+ loadAreaStations()
|
|
|
|
|
+})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|