feat: 专员列表管理

This commit is contained in:
lzhizhao 2025-10-29 17:05:31 +08:00
parent 1be3aac263
commit c800b97d90
2 changed files with 797 additions and 1 deletions

View File

@ -558,5 +558,174 @@ export const mer_admin = {
params: query,
data: data
});
},
// ==================== 专员管理相关接口 ====================
/**
* 获取专员列表
* @param {object} data 查询参数
* @param {number} data.marketId 市场ID
* @returns {Promise}
*/
listAssistant: data => {
return $http.request({
method: "get",
url: `/merchant-api/assistant/list`,
params: data
});
},
/**
* 获取专员调度费模式
* @param {object} data 查询参数
* @param {number} data.marketId 市场ID
* @returns {Promise}
*/
getAssistantMode: data => {
return $http.request({
method: "get",
url: `/merchant-api/assistant/dispatch_mode`,
params: data
});
},
/**
* 专员调度费模式配置
* @param {object} data 配置参数
* @param {number} data.marketId 市场ID
* @param {number} data.dispatchMode 调度模式1:顾客支付 2:经营者支付
* @returns {Promise}
*/
setAssistantMode: data => {
return $http.request({
method: "put",
url: `/merchant-api/assistant/edit_dispatch_mode`,
params: data
});
},
/**
* 设置市场默认专员
* @param {object} data 设置参数
* @param {string} data.assistantId 专员ID
* @param {number} data.marketId 市场ID
* @param {number} data.status 状态0:取消 1:设置
* @returns {Promise}
*/
setAssistantDefault: data => {
return $http.request({
method: "put",
url: `/merchant-api/assistant/set_market_default`,
params: data
});
},
/**
* 添加专员
* @param {object} data 专员参数
* @param {number} data.marketId 市场ID
* @param {string} data.name 专员姓名
* @param {string} data.mobile 专员账号
* @param {string} data.type 专员类型1:调度专员 2:配送专员 3:客服专员多个用逗号分隔
* @param {number} data.dispatchFee 调度费
* @returns {Promise}
*/
addAssistant: data => {
return $http.post(`/merchant-api/assistant/add`, data);
},
/**
* 编辑专员
* @param {object} query 查询参数
* @param {string} query.assistantId 专员ID
* @param {object} data 专员参数
* @returns {Promise}
*/
editAssistant: (query, data) => {
return $http.request({
method: "put",
url: `/merchant-api/assistant/edit`,
params: query,
data: data
});
},
/**
* 解绑专员
* @param {object} data 解绑参数
* @param {string} data.assistantId 专员ID
* @param {number} data.marketId 市场ID
* @returns {Promise}
*/
unbindAssistant: data => {
return $http.request({
method: "put",
url: `/merchant-api/assistant/unbind`,
params: data
});
},
/**
* 获取配置
* @param {object} data 查询参数
* @param {string} data.configKey 配置键
* @param {string} data.targetId 目标ID
* @returns {Promise}
*/
getConfig: data => {
return $http.request({
method: "get",
url: `/merchant-api/config/value`,
params: data
});
},
/**
* 设置配置
* @param {object} data 配置参数
* @param {string} data.configKey 配置键
* @param {number} data.value 配置值
* @param {string} data.targetId 目标ID
* @returns {Promise}
*/
setConfig: data => {
return $http.request({
method: "post",
url: `/merchant-api/config/update`,
params: data
});
},
// ==================== 市场基础配置相关接口 ====================
/**
* 获取市场基础配置
* @param {object} data 查询参数
* @param {number} data.marketId 市场ID
* @returns {Promise}
*/
getMarketBaseConfig: data => {
return $http.request({
method: "get",
url: `/merchant-api/market/base_config`,
params: data
});
},
/**
* 更新市场基础配置
* @param {object} query 查询参数
* @param {number} query.marketId 市场ID
* @param {object} data 配置数据
* @returns {Promise}
*/
updateMarketBaseConfig: (query, data) => {
return $http.request({
method: "put",
url: `/merchant-api/market/base_config`,
params: query,
data: data
});
}
};

View File

@ -1,3 +1,630 @@
<template>
<div>专员列表</div>
<div class="page-container">
<!-- 顶部提示 -->
<el-alert
v-if="!hasDefault"
title="市场未设置专员无法正常营业 请设置市场专员"
type="error"
:closable="false"
style="margin-bottom: 15px"
/>
<!-- 专员调度费配置 -->
<el-card class="config-card" shadow="never">
<div slot="header" class="clearfix">
<span>专员调度费配置</span>
<el-button
style="float: right; padding: 3px 0"
type="text"
@click="openDescModal"
>
<i class="el-icon-question"></i> 字段说明
</el-button>
</div>
<el-form label-width="120px" size="small">
<el-form-item label="费用支付方" required>
<el-link type="primary" :underline="false" @click="openDispatchModal">
{{ dispatchModeDesc }}
<i class="el-icon-arrow-right"></i>
</el-link>
</el-form-item>
</el-form>
</el-card>
<!-- 专员列表 -->
<div class="table-container">
<div style="margin-bottom: 15px">
<el-button type="primary" size="small" @click="openForm('ADD', {})">
添加专员
</el-button>
</div>
<el-table :data="dataList" border style="width: 100%" v-loading="loading">
<el-table-column type="index" width="60" align="center" label="序号" />
<el-table-column
prop="name"
label="姓名"
align="center"
min-width="120"
>
<template slot-scope="scope">
{{ scope.row.name }}
<el-tag
v-if="scope.row.isDefault"
type="success"
size="mini"
style="margin-left: 8px"
>
当前市场专员
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="username"
label="账号"
align="center"
min-width="130"
/>
<el-table-column
prop="typeList"
label="专员类型"
align="center"
min-width="200"
>
<template slot-scope="scope">
{{ scope.row.typeList.map(type => typeEnum[type]).join("") }}
</template>
</el-table-column>
<el-table-column
prop="dispatchFee"
label="调度费"
align="center"
width="120"
>
<template slot-scope="scope">
{{ scope.row.dispatchFee ? scope.row.dispatchFee + "元/单" : "-" }}
</template>
</el-table-column>
<el-table-column prop="status" label="状态" align="center" width="100">
<template slot-scope="scope">
<el-tag
:type="scope.row.status === 1 ? 'success' : 'info'"
size="small"
>
{{ statusEnum[scope.row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="320">
<template slot-scope="scope">
<template v-if="scope.row.status === 1">
<el-button
size="mini"
type="primary"
@click="handlePrintSetting(scope.row)"
>
打印权限
</el-button>
<el-button
size="mini"
type="success"
@click="setDefault(scope.row.id, scope.row.isDefault)"
>
{{ scope.row.isDefault ? "取消市场专员" : "设为市场专员" }}
</el-button>
<el-button
size="mini"
type="warning"
@click="openForm('EDIT', scope.row)"
>
编辑
</el-button>
</template>
<el-button
v-if="scope.row.status === 0"
size="mini"
type="danger"
@click="handleUnBind(scope.row.id)"
>
取消绑定
</el-button>
</template>
</el-table-column>
</el-table>
<div
v-if="!dataList.length && !loading"
style="text-align: center; padding: 40px 0; color: #909399"
>
暂无数据
</div>
</div>
<!-- 添加/编辑专员弹窗 -->
<el-dialog
:title="formTitle"
:visible.sync="formVisible"
width="500px"
:close-on-click-modal="false"
>
<el-form
ref="formRef"
:model="formModel"
:rules="formRules"
label-width="100px"
>
<el-form-item label="专员姓名" prop="name">
<el-input
v-model="formModel.name"
placeholder="请输入专员姓名"
clearable
/>
</el-form-item>
<el-form-item label="专员账号" prop="mobile">
<el-input
v-model="formModel.mobile"
placeholder="请输入手机号"
:disabled="!!formModel.id"
clearable
/>
</el-form-item>
<el-form-item label="专员类型" prop="typeList">
<el-checkbox-group v-model="formModel.typeList">
<el-checkbox label="1">调度专员</el-checkbox>
<el-checkbox label="2">配送专员</el-checkbox>
<el-checkbox label="3">客服专员</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item
v-if="formModel.typeList.includes('1')"
label="调度费"
prop="dispatchFee"
>
<el-input
v-model="formModel.dispatchFee"
placeholder="请输入"
clearable
>
<template slot="append">/每单</template>
</el-input>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="formVisible = false">取消</el-button>
<el-button type="primary" @click="handleFormSubmit">确定</el-button>
</div>
</el-dialog>
<!-- 调度费配置弹窗 -->
<el-dialog
title="专员调度费配置"
:visible.sync="dispatchVisible"
width="500px"
:close-on-click-modal="false"
>
<el-radio-group v-model="newDispatchMode">
<el-radio :label="1">
<div style="display: inline-block; width: 350px">
<div>顾客支付</div>
<div style="font-size: 12px; color: #909399; line-height: 16px">
顾客在下单时将根据配置内容额外收取专员调度费用
</div>
</div>
</el-radio>
<el-radio :label="2" style="margin-top: 15px">
<div style="display: inline-block; width: 350px">
<div>经营者支付</div>
<div style="font-size: 12px; color: #909399; line-height: 16px">
根据配置从经营者收益中扣除相应费用作为专员调度费
</div>
</div>
</el-radio>
</el-radio-group>
<div slot="footer">
<el-button @click="dispatchVisible = false">取消</el-button>
<el-button type="primary" @click="handleDispatchSubmit">确定</el-button>
</div>
</el-dialog>
<!-- 打印权限设置弹窗 -->
<el-dialog
title="设置"
:visible.sync="printVisible"
width="400px"
:close-on-click-modal="false"
>
<el-form
ref="printFormRef"
:model="printFormModel"
:rules="printFormRules"
label-width="100px"
>
<el-form-item label="打印权限" prop="enable">
<el-switch
v-model="printFormModel.enable"
:active-value="true"
:inactive-value="false"
/>
<span style="margin-left: 10px">
{{ printFormModel.enable ? "开启" : "关闭" }}
</span>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="printVisible = false">取消</el-button>
<el-button type="primary" @click="handlePrintSubmit">确定</el-button>
</div>
</el-dialog>
<!-- 字段说明弹窗 -->
<el-dialog
title="专员调度费用配置-字段说明"
:visible.sync="descVisible"
width="600px"
>
<el-descriptions :column="1" border>
<el-descriptions-item label="经营者支付" label-class-name="desc-label">
<div style="line-height: 1.6">
根据配置从经营者收益中扣除相应费用作为专员调度费<br />
例如专员调度费2.00/每订单当订单完成时将从经营者收益中扣除2.00结算给该订单的处理专员
</div>
</el-descriptions-item>
<el-descriptions-item label="顾客支付" label-class-name="desc-label">
<div style="line-height: 1.6">
顾客在下单时将根据配置内容额外收取专员调度费用<br />
例如专员调度费2.00/每订单顾客下单时需支付"专员调度费¥2.00";当订单完成时2.00结算给该订单的处理专员
</div>
</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button type="primary" @click="descVisible = false"
>知道了</el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
import { mer_admin } from "@/api/modules/mer_admin.js";
export default {
data() {
//
const validateMobile = (rule, value, callback) => {
if (!value) {
callback(new Error("请输入手机号"));
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error("请输入正确的手机号"));
} else {
callback();
}
};
//
const validatePrice = (rule, value, callback) => {
if (!value) {
callback();
return;
}
const pattern = /^\$?(\d+)(\.\d{0,2})?$/;
if (pattern.test(value)) {
callback();
} else {
callback(new Error("请输入数字,支持两位小数"));
}
};
return {
loading: false,
dataList: [],
dispatchMode: undefined,
newDispatchMode: undefined,
//
statusEnum: {
0: "待确认",
1: "已绑定"
},
typeEnum: {
1: "调度专员",
2: "配送专员",
3: "客服专员"
},
//
formVisible: false,
formTitle: "",
formType: "",
formModel: {
id: "",
name: "",
mobile: "",
typeList: [],
dispatchFee: undefined
},
formRules: {
name: [{ required: true, message: "请输入专员姓名", trigger: "blur" }],
mobile: [
{ required: true, validator: validateMobile, trigger: "blur" }
],
typeList: [
{
type: "array",
required: true,
message: "请选择专员类型",
trigger: "change"
}
],
dispatchFee: [{ validator: validatePrice, trigger: "blur" }]
},
//
dispatchVisible: false,
//
printVisible: false,
printFormModel: {
targetId: "",
enable: true
},
printFormRules: {
enable: [
{ required: true, message: "请选择是否开启", trigger: "change" }
]
},
//
descVisible: false
};
},
computed: {
marketId() {
return this.$store.state.userData.marketId;
},
dispatchModeDesc() {
if (!this.dispatchMode) return "请配置";
const modeEnum = {
1: "顾客支付",
2: "经营者支付"
};
return modeEnum[this.dispatchMode];
},
hasDefault() {
return this.dataList.some(item => item.isDefault);
}
},
mounted() {
this.getAssistantDispatch();
this.getAssistantList();
},
methods: {
//
async getAssistantList() {
this.loading = true;
try {
const res = await mer_admin.listAssistant({ marketId: this.marketId });
// res.data.data res.data
const list = res.data?.data || res.data || [];
this.dataList = list.map(item => ({
...item,
typeList: item.type.split(",")
}));
} catch (error) {
console.error(error);
this.$message.error("获取专员列表失败");
} finally {
this.loading = false;
}
},
//
async getAssistantDispatch() {
try {
const res = await mer_admin.getAssistantMode({
marketId: this.marketId
});
//
const data = res.data?.data || res.data || {};
this.dispatchMode = data.dispatchMode;
console.log("调度费模式:", this.dispatchMode);
} catch (error) {
console.error("获取调度费模式失败:", error);
}
},
//
openDescModal() {
this.descVisible = true;
},
//
openDispatchModal() {
this.newDispatchMode = this.dispatchMode;
this.dispatchVisible = true;
},
//
async handleDispatchSubmit() {
try {
const res = await mer_admin.setAssistantMode({
marketId: this.marketId,
dispatchMode: this.newDispatchMode
});
console.log("设置调度费模式返回:", res);
this.$message.success("设置成功");
this.dispatchMode = this.newDispatchMode;
this.dispatchVisible = false;
} catch (error) {
console.error("设置调度费模式失败:", error);
this.$message.error("设置失败");
}
},
//
openForm(type, data) {
this.formType = type;
this.formTitle = type === "ADD" ? "添加专员" : "编辑专员";
this.formModel = {
id: data?.id || "",
name: data?.name || "",
mobile: data?.username || "",
typeList: data?.typeList || [],
dispatchFee: data?.dispatchFee
};
this.formVisible = true;
this.$nextTick(() => {
this.$refs.formRef && this.$refs.formRef.clearValidate();
});
},
//
handleFormSubmit() {
this.$refs.formRef.validate(async valid => {
if (!valid) return;
try {
const params = {
marketId: this.marketId,
name: this.formModel.name,
mobile: this.formModel.mobile,
type: this.formModel.typeList.sort().join(","),
dispatchFee: this.formModel.dispatchFee
};
if (this.formType === "ADD") {
const res = await mer_admin.addAssistant(params);
console.log("添加专员返回:", res);
} else {
const res = await mer_admin.editAssistant(
{ assistantId: this.formModel.id },
params
);
console.log("编辑专员返回:", res);
}
this.$message.success("设置成功");
this.formVisible = false;
this.getAssistantList();
} catch (error) {
console.error("操作失败:", error);
this.$message.error("操作失败");
}
});
},
// /
async setDefault(assistantId, isDefault) {
try {
const res = await mer_admin.setAssistantDefault({
assistantId,
marketId: this.marketId,
status: isDefault ? 0 : 1
});
console.log("设置市场专员返回:", res);
this.$message.success("设置成功");
this.getAssistantList();
} catch (error) {
console.error("设置市场专员失败:", error);
this.$message.error("设置失败");
}
},
//
handleUnBind(assistantId) {
this.$confirm("是否确认取消绑定?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(async () => {
const res = await mer_admin.unbindAssistant({
assistantId,
marketId: this.marketId
});
console.log("取消绑定返回:", res);
this.$message.success("取消绑定成功");
this.getAssistantList();
})
.catch(error => {
if (error !== "cancel") {
console.error("取消绑定失败:", error);
this.$message.error("取消绑定失败");
}
});
},
//
async handlePrintSetting(item) {
try {
const res = await mer_admin.getConfig({
configKey: "enablePrint",
targetId: item.id
});
console.log("获取打印权限返回:", res);
const data = res.data?.data || res.data;
if (data) {
this.printFormModel = {
targetId: data.targetId,
enable: data.configValue === "1"
};
} else {
this.printFormModel = {
targetId: item.id,
enable: true
};
}
this.printVisible = true;
this.$nextTick(() => {
this.$refs.printFormRef && this.$refs.printFormRef.clearValidate();
});
} catch (error) {
console.error("获取打印权限失败:", error);
// 使
this.printFormModel = {
targetId: item.id,
enable: true
};
this.printVisible = true;
}
},
//
handlePrintSubmit() {
this.$refs.printFormRef.validate(async valid => {
if (!valid) return;
try {
const res = await mer_admin.setConfig({
configKey: "enablePrint",
value: this.printFormModel.enable ? 1 : 0,
targetId: this.printFormModel.targetId
});
console.log("设置打印权限返回:", res);
this.$message.success("设置成功");
this.printVisible = false;
} catch (error) {
console.error("设置打印权限失败:", error);
this.$message.error("设置失败");
}
});
}
}
};
</script>
<style lang="scss" scoped>
.config-card {
margin-bottom: 15px;
}
.desc-label {
width: 120px;
}
</style>