商户管理

This commit is contained in:
DESKTOP-GMUNQ1B\k 2024-04-24 09:56:53 +08:00
parent 470b4f540d
commit 5622b0d02d
22 changed files with 2823 additions and 558 deletions

12
package-lock.json generated
View File

@ -18,6 +18,7 @@
"echarts": "^5.3.3", "echarts": "^5.3.3",
"element-plus": "2.2.27", "element-plus": "2.2.27",
"highlight.js": "^11.6.0", "highlight.js": "^11.6.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.14", "pinia": "^2.0.14",
"vue": "^3.2.37", "vue": "^3.2.37",
@ -5655,6 +5656,12 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/mixin-deep": { "node_modules/mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz",
@ -13509,6 +13516,11 @@
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
"dev": true "dev": true
}, },
"mitt": {
"version": "3.0.1",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="
},
"mixin-deep": { "mixin-deep": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz",

View File

@ -19,6 +19,7 @@
"echarts": "^5.3.3", "echarts": "^5.3.3",
"element-plus": "2.2.27", "element-plus": "2.2.27",
"highlight.js": "^11.6.0", "highlight.js": "^11.6.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"pinia": "^2.0.14", "pinia": "^2.0.14",
"vue": "^3.2.37", "vue": "^3.2.37",

View File

@ -1,40 +1,40 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDark, useWindowSize, useThrottleFn } from '@vueuse/core' import { useDark, useWindowSize, useThrottleFn } from "@vueuse/core";
import zhCn from "element-plus/es/locale/lang/zh-cn"; import zhCn from "element-plus/es/locale/lang/zh-cn";
import useAppStore from './stores/modules/app' import useAppStore from "./stores/modules/app";
import useSettingStore from './stores/modules/setting' import useSettingStore from "./stores/modules/setting";
import { ScreenEnum } from './enums/appEnums' import { ScreenEnum } from "./enums/appEnums";
const appStore = useAppStore() const appStore = useAppStore();
const settingStore = useSettingStore() const settingStore = useSettingStore();
const elConfig = { const elConfig = {
zIndex: 3000, zIndex: 3000,
locale: zhCn locale: zhCn,
} };
const isDark = useDark() const isDark = useDark();
onMounted(async () => { onMounted(async () => {
// //
settingStore.setTheme(isDark.value) settingStore.setTheme(isDark.value);
}) });
const { width } = useWindowSize() const { width } = useWindowSize();
watch( watch(
width, width,
useThrottleFn((value) => { useThrottleFn((value) => {
if (value > ScreenEnum.SM) { if (value > ScreenEnum.SM) {
appStore.setMobile(false) appStore.setMobile(false);
appStore.toggleCollapsed(false) appStore.toggleCollapsed(false);
} else { } else {
appStore.setMobile(true) appStore.setMobile(true);
appStore.toggleCollapsed(true) appStore.toggleCollapsed(true);
} }
if (value < ScreenEnum.MD) { if (value < ScreenEnum.MD) {
appStore.toggleCollapsed(true) appStore.toggleCollapsed(true);
} }
}), }),
{ {
immediate: true immediate: true,
} }
) );
</script> </script>
<template> <template>

21
src/api/cate_time.ts Normal file
View File

@ -0,0 +1,21 @@
import request from '@/utils/request';
// 分类定时列表
export function cateTimeListsApi(params: any) {
return request.get({ url: '/cate.cate_time/lists', params })
}
// 分类定时添加
export function cateTimeAddApi(params: any) {
return request.post({ url: '/cate.cate_time/add', params })
}
// 分类定时修改
export function cateTimeEditApi(params: any) {
return request.post({ url: '/cate.cate_time/edit', params })
}
// 分类定时删除
export function cateTimeDelApi(params: any) {
return request.post({ url: '/cate.cate_time/delete', params })
}

View File

@ -5,6 +5,11 @@ export function merchantList(params: any) {
return request.get({ url: '/merchant.merchant/lists', params }) return request.get({ url: '/merchant.merchant/lists', params })
} }
// 商户列表----无分页
export function merchantListNoPage(params: any) {
return request.get({ url: '/merchant.merchant/merchantList', params })
}
// 修改商户相关信息 // 修改商户相关信息
export function merchantUpdate(params: any) { export function merchantUpdate(params: any) {
return request.post({ url: '/merchant.merchant/changeMerchant', params }) return request.post({ url: '/merchant.merchant/changeMerchant', params })
@ -15,12 +20,16 @@ export function merchantDetail(params: any) {
return request.post({ url: '/merchant.merchant/detail', params }) return request.post({ url: '/merchant.merchant/detail', params })
} }
// 获取记录人 子管理员 // 获取记录人 子管理员
export function childManagement(params: any) { export function childManagement(params: any) {
return request.get({ url: '/record.record/info', params }) return request.get({ url: '/record.record/info', params })
} }
// 获取记录人 子管理员
export function recordManApi(params: any) {
return request.get({ url: '/merchant.merchant/recordMan', params })
}
// 商户管理员绑定 // 商户管理员绑定
export function merchantBind(params: any) { export function merchantBind(params: any) {
return request.post({ url: '/merchant.merchant/merchantPermision', params }) return request.post({ url: '/merchant.merchant/merchantPermision', params })
@ -36,3 +45,39 @@ export function storeSuper(params: any) {
return request.post({ url: '/merchant.merchant/storeSuper', params }) return request.post({ url: '/merchant.merchant/storeSuper', params })
} }
// 保存督导
export function saveConditionApi(params: any) {
return request.get({ url: '/store.store_condition/saveCondition', params })
}
// 消息列表
export function newList(params: any) {
return request.get({ url: '/new.new/lists', params })
}
// excel 导出
export function excelListApi(params: any) {
return request.get({ url: '/merchant.merchant/excelList', params })
}
// excel 预下载
export function excelApi(params: any) {
return request.get({ url: '/merchant.merchant/excel', params })
}
// 商户分类
export function merchantCategoryApi(params: any) {
return request.get({ url: '/merchant.merchant/merchantCategory', params })
}
// 区域分类
export function merchantAreaListApi(params: any) {
return request.get({ url: '/merchant.merchant/AreatList', params })
}
// 镇街道
export function streetListApi(params: any) {
return request.get({ url: '/merchant.merchant/streetList', params })
}

31
src/api/monitor.ts Normal file
View File

@ -0,0 +1,31 @@
import request from '@/utils/request';
// 督导列表
export function monitorListApi(params: any) {
return request.get({ url: '/store.store_condition/lists', params })
}
// 督导添加
export function monitorAddApi(params: any) {
return request.post({ url: '/store.store_condition/add', params })
}
// 督导修改
export function monitorEditApi(params: any) {
return request.post({ url: '/store.store_condition/edit', params })
}
// 督导删除
export function monitorDelApi(params: any) {
return request.post({ url: '/store.store_condition/delete', params })
}
// 督导详情by id
export function monitorDetailApi(params: any) {
return request.get({ url: '/store.store_condition/detail', params })
}
// 督导详情by mer_id
export function monitorByMerIdApi(params: any) {
return request.get({ url: '/store.store_condition/merchantDetail', params })
}

View File

@ -0,0 +1,26 @@
import request from '@/utils/request'
// 店铺督促表列表
export function apiStoreConditionLists(params: any) {
return request.get({ url: '/store.store_condition/lists', params })
}
// 添加店铺督促表
export function apiStoreConditionAdd(params: any) {
return request.post({ url: '/store.store_condition/add', params })
}
// 编辑店铺督促表
export function apiStoreConditionEdit(params: any) {
return request.post({ url: '/store.store_condition/edit', params })
}
// 删除店铺督促表
export function apiStoreConditionDelete(params: any) {
return request.post({ url: '/store.store_condition/delete', params })
}
// 店铺督促表详情
export function apiStoreConditionDetail(params: any) {
return request.get({ url: '/store.store_condition/detail', params })
}

View File

@ -0,0 +1,64 @@
<template>
<div style="display: flex">
<el-icon :size="18" @click="onOpen"><Download /></el-icon>
<el-drawer v-model="drawShow" title="Excel下载" :direction="'rtl'" size="35%">
<el-table :data="excelData" stripe>
<el-table-column type="selection" width="55" />
<el-table-column
label="名称"
prop="record"
min-width="120"
show-overflow-tooltip
/>
<el-table-column label="状态" prop="status" min-width="80">
<template #default="{ row }">
<el-tag type="primary" v-if="row.status == 0">生成中</el-tag>
<el-tag type="success" v-if="row.status == 1">成功</el-tag>
<el-tag type="danger" v-if="row.status == 2">失败</el-tag>
</template>
</el-table-column>
<el-table-column
label="下载地址"
prop="excel_path"
min-width="200"
show-overflow-tooltip
>
<template #default="{ row }">
<span type="success" v-if="row.status == 1">{{ row.excel_path }}</span>
<span type="danger" v-else>-</span>
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{ row }">
<el-link :href="row.excel_path" target="_blank">下载</el-link>
</template>
</el-table-column>
</el-table>
</el-drawer>
</div>
</template>
<script setup lang="ts">
//
import { useRouter } from "vue-router";
import { excelListApi } from "@/api/merchant";
import { getRoutePath } from "@/router";
const drawShow = ref(false);
//
const onOpen = () => {
drawShow.value = true;
getExcelData();
};
//
const excelData = ref([]);
const getExcelData = () => {
excelListApi("").then((res) => {
excelData.value = res;
console.log(excelData);
});
};
</script>

View File

@ -13,7 +13,15 @@
</div> </div>
</div> </div>
<div class="flex"> <div class="flex">
<div class="navbar-item" v-if="!isMobile"> <div class="navbar-item">
<download />
</div>
<div class="navbar-item" style="margin-left: 10px">
<notice />
</div>
<div class="navbar-item" v-if="!isMobile" style="margin-left: 10px">
<full-screen /> <full-screen />
</div> </div>
<div class="navbar-item"> <div class="navbar-item">
@ -29,19 +37,21 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useAppStore from '@/stores/modules/app' import useAppStore from "@/stores/modules/app";
import Fold from './fold.vue' import Fold from "./fold.vue";
import Refresh from './refresh.vue' import Refresh from "./refresh.vue";
import Breadcrumb from './breadcrumb.vue' import Breadcrumb from "./breadcrumb.vue";
import FullScreen from './full-screen.vue' import FullScreen from "./full-screen.vue";
import UserDropDown from './user-drop-down.vue' import notice from "./notice.vue";
import Setting from '../setting/index.vue' import download from "./download.vue";
import MultipleTabs from './multiple-tabs.vue' import UserDropDown from "./user-drop-down.vue";
import Setting from "../setting/index.vue";
import MultipleTabs from "./multiple-tabs.vue";
import useSettingStore from '@/stores/modules/setting' import useSettingStore from "@/stores/modules/setting";
const appStore = useAppStore() const appStore = useAppStore();
const isMobile = computed(() => appStore.isMobile) const isMobile = computed(() => appStore.isMobile);
const settingStore = useSettingStore() const settingStore = useSettingStore();
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -0,0 +1,58 @@
<template>
<div>
<el-popover placement="bottom" trigger="hover" width="200px">
<template #reference>
<el-badge
:is-dot="newsList.length > 0 ? true : false"
class="item"
style="display: flex; align-items: center; height: 100%; cursor: pointer"
>
<el-icon :size="16"><Bell /></el-icon>
</el-badge>
</template>
<el-table
:data="newsList"
@row-click="rowClick"
max-height="300"
:show-header="false"
>
<el-table-column
width="160"
property="type_name"
label="店铺类型"
align="center"
/>
</el-table>
</el-popover>
</div>
</template>
<script setup lang="ts">
//
import { useRouter } from "vue-router";
import { newList } from "@/api/merchant";
import { getRoutePath } from "@/router";
import { ref, getCurrentInstance, type ComponentInternalInstance, onMounted, onBeforeMount } from "vue";
const { appContext } = getCurrentInstance() as ComponentInternalInstance;
const newsList = ref([]);
onMounted(()=>{
appContext.config.globalProperties.$mitt.on('selfEvent',(res:any)=>{
console.log('执行了自定义方法')
//
newList("").then((res) => {
newsList.value = res;
});
})
})
//
const router = useRouter();
const rowClick = (row: any) => {
router.push({
path: getRoutePath("merchant.merchant/lists"),
query: { mer_ids: row.mer_ids.join(",") },
});
};
</script>

View File

@ -4,7 +4,11 @@ import install from './install'
import './permission' import './permission'
import './styles/index.scss' import './styles/index.scss'
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
import mitt from 'mitt';
const app = createApp(App) const app = createApp(App)
app.config.globalProperties.$mitt = mitt();
app.use(install) app.use(install)
app.mount('#app') app.mount('#app')

View File

@ -0,0 +1,151 @@
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="550px"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="店铺类型" prop="type_id">
<el-select
class="w-[280px]"
placeholder="请输入店铺类型"
v-model="formData.type_id"
>
<el-option
v-for="item in merchantData"
:label="item.type_name"
:value="item.mer_type_id"
:key="item.mer_type_id"
/>
</el-select>
</el-form-item>
<el-form-item label="店铺类型" prop="open">
<el-switch v-model="formData.open" active-text="开启" inactive-text="关闭">
</el-switch>
</el-form-item>
<el-form-item label="日期" prop="time">
<el-date-picker v-model="formData.time" type="date" placeholder="选择天数">
</el-date-picker>
</el-form-item>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup name="cateTimeEdit">
import type { FormInstance } from "element-plus";
import Popup from "@/components/popup/index.vue";
import { merchantCate } from "@/api/merchant";
import { apiRecordDetail } from "@/api/record";
import { cateTimeAddApi, cateTimeEditApi } from "@/api/cate_time";
const emit = defineEmits(["success", "close"]);
const formRef = shallowRef<FormInstance>();
const popupRef = shallowRef<InstanceType<typeof Popup>>();
const mode = ref("add");
//
const popupTitle = computed(() => {
return mode.value == "edit" ? "编辑分类定时" : "新增分类定时";
});
//
const formData = reactive({
id: "",
type_id: "",
time: "",
open: true,
});
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
};
//
const formRules = reactive<any>({
type_id: [
{
required: true,
message: "请选择店铺类型",
trigger: ["change"],
},
],
time: [
{
required: true,
message: "请选择执行时间",
trigger: ["change"],
},
],
open: [
{
required: true,
message: "请选择状态",
trigger: ["change"],
},
],
});
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
if (key == "open") {
formData[key] = data[key] == 1 ? true : false;
} else if (key == "time") {
let year = new Date().getFullYear();
let month = new Date().getMonth() + 1;
formData[key] = `${year}-${month}-${data[key]}`;
} else {
formData[key] = data[key];
}
}
}
};
//
const handleSubmit = async () => {
await formRef.value?.validate();
const data = { ...formData };
data.time = new Date(data.time).getDate();
mode.value == "edit" ? await cateTimeEditApi(data) : await cateTimeAddApi(data);
popupRef.value?.close();
emit("success");
};
//
const open = (type = "add", merId = "") => {
mode.value = type;
popupRef.value?.open();
if (merId) {
formData.merchant_id = merId;
}
};
//
const handleClose = () => {
emit("close");
};
getMerchantCate();
defineExpose({
open,
setFormData,
});
</script>
<style lang="scss">
.el-input__wrapper {
width: 100%;
}
</style>

View File

@ -0,0 +1,133 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-form class="mb-[-16px]" :model="queryParams" inline>
<el-form-item label="店铺类型" prop="type_id">
<el-select
placeholder="请选择商户"
v-model="queryParams.type_id"
class="w-[280px]"
filterable
>
<el-option
v-for="(item, indx) in merchantData"
:key="item.mer_type_id"
:value="item.mer_type_id + ''"
:label="item.type_name"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never">
<el-button v-perms="['record.record/add']" type="primary" @click="handleAdd">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
<div class="mt-4">
<el-table :data="pager.lists">
<el-table-column type="selection" width="55" />
<el-table-column
label="店铺类型"
prop="cate_name"
min-width="200"
show-overflow-tooltip
/>
<el-table-column label="执行时间" prop="time" show-overflow-tooltip />
<el-table-column label="状态" prop="open" show-overflow-tooltip>
<template #default="{ row }">
<el-tag v-if="row.open == 1" type="success">开启</el-tag>
<el-tag v-else type="danger">关闭</el-tag>
</template>
</el-table-column>
<el-table-column
label="创建时间"
prop="create_time"
min-width="160"
show-overflow-tooltip
/>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)"> 编辑 </el-button>
<el-button type="danger" link @click="handleDelete(row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup
v-if="showEdit"
ref="editRef"
@success="getLists"
@close="showEdit = false"
/>
</div>
</template>
<script lang="ts" setup name="cateTimeList">
import { usePaging } from "@/hooks/usePaging";
import { recordAll, recordInfo } from "@/api/record";
import { cateTimeListsApi, cateTimeDelApi } from "@/api/cate_time";
import feedback from "@/utils/feedback";
import { merchantCate } from "@/api/merchant";
import EditPopup from "./edit.vue";
const editRef = shallowRef<InstanceType<typeof EditPopup>>();
//
const showEdit = ref(false);
//
const queryParams = reactive({
type_id: "",
});
//
const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: cateTimeListsApi,
params: queryParams,
});
//
const handleAdd = async () => {
showEdit.value = true;
await nextTick();
editRef.value?.open("add");
};
//
const handleEdit = async (data: any) => {
showEdit.value = true;
await nextTick();
editRef.value?.open("edit");
editRef.value?.setFormData(data);
};
//
const handleDelete = async (id: number | any[]) => {
await feedback.confirm("确定要删除?");
await cateTimeDelApi({ id });
getLists();
};
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
};
getMerchantCate();
getLists();
</script>

View File

@ -17,42 +17,93 @@
<el-page-header :content="$route.meta.title" @back="$router.back()" /> <el-page-header :content="$route.meta.title" @back="$router.back()" />
</el-card> </el-card>
<el-card class="mt-4 !border-none" shadow="never"> <el-card class="mt-4 !border-none" shadow="never">
<el-form ref="formRef" class="ls-form" :model="formData" label-width="100px" :rules="rules"> <el-form
ref="formRef"
class="ls-form"
:model="formData"
label-width="100px"
:rules="rules"
>
<div class="form-wrap"> <div class="form-wrap">
<el-form-item label="商户名称" prop="mer_name"> <el-form-item label="商户名称" prop="mer_name">
<el-input class="w-80" v-model="formData.mer_name" placeholder="请输入商户名称" clearable /> <el-input
class="w-80"
v-model="formData.mer_name"
placeholder="请输入商户名称"
clearable
/>
</el-form-item> </el-form-item>
<el-form-item label="真实姓名" prop="real_name"> <el-form-item label="真实姓名" prop="real_name">
<el-input class="w-80" v-model="formData.real_name" placeholder="请输入真实姓名" /> <el-input
class="w-80"
v-model="formData.real_name"
placeholder="请输入真实姓名"
/>
</el-form-item> </el-form-item>
<el-form-item label="总采购金额" prop="purchase_amount"> <el-form-item label="总采购金额" prop="purchase_amount">
<el-input class="w-80" v-model="formData.purchase_amount" placeholder="请输入总采购金额" readonly /> <el-input
class="w-80"
v-model="formData.purchase_amount"
placeholder="请输入总采购金额"
readonly
/>
</el-form-item> </el-form-item>
<el-form-item label="总销售金额" prop="sale_amount"> <el-form-item label="总销售金额" prop="sale_amount">
<el-input class="w-80" v-model="formData.sale_amount" placeholder="请输入总销售金额" readonly /> <el-input
class="w-80"
v-model="formData.sale_amount"
placeholder="请输入总销售金额"
readonly
/>
</el-form-item> </el-form-item>
<el-form-item label="手机号" prop="service_phone"> <el-form-item label="手机号" prop="service_phone">
<el-input class="w-80" v-model="formData.service_phone" placeholder="请输入手机号" clearable /> <el-input
class="w-80"
v-model="formData.service_phone"
placeholder="请输入手机号"
clearable
/>
</el-form-item> </el-form-item>
<el-form-item label="评分" prop="product_score"> <el-form-item label="评分" prop="product_score">
<el-input class="w-80" v-model="formData.product_score" placeholder="请输入评分" type="number" <el-input
maxlength="1" max="5" min="1" clearable /> class="w-80"
v-model="formData.product_score"
placeholder="请输入评分"
type="number"
maxlength="1"
max="5"
min="1"
clearable
/>
</el-form-item> </el-form-item>
<el-form-item label="店铺类型" prop="type_name"> <el-form-item label="店铺类型" prop="type_name">
<el-select class="w-80" v-model="formData.type_name" placeholder="请输入店铺类型"> <el-select
<el-option :label="item.type_name" :value="item.mer_type_id" v-for="item in merchantData" class="w-80"
:key="item.mer_type_id" /> v-model="formData.type_name"
placeholder="请输入店铺类型"
>
<el-option
:label="item.type_name"
:value="item.mer_type_id"
v-for="item in merchantData"
:key="item.mer_type_id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="地址" prop="mer_address"> <el-form-item label="地址" prop="mer_address">
<el-input class="w-80" v-model="formData.mer_address" placeholder="请输入地址" clearable /> <el-input
class="w-80"
v-model="formData.mer_address"
placeholder="请输入地址"
clearable
/>
</el-form-item> </el-form-item>
</div> </div>
</el-form> </el-form>
@ -64,12 +115,11 @@
</template> </template>
<script lang="ts" setup name="articleListsEdit"> <script lang="ts" setup name="articleListsEdit">
import type { FormInstance } from 'element-plus' import type { FormInstance } from "element-plus";
import { merchantDetail, merchantUpdate, merchantCate } from "@/api/merchant"; import { merchantDetail, merchantUpdate, merchantCate } from "@/api/merchant";
import { articleDetail, articleEdit, articleAdd, articleCateAll } from '@/api/article'
const route = useRoute() const route = useRoute();
const router = useRouter() const router = useRouter();
const formData = ref({ const formData = ref({
mer_id: "", //id mer_id: "", //id
mer_name: "", // mer_name: "", //
@ -84,40 +134,37 @@ const formData = ref({
goods_num: "", // goods_num: "", //
}); });
const formRef = shallowRef<FormInstance>();
const formRef = shallowRef<FormInstance>()
const rules = reactive({ const rules = reactive({
mer_name: [{ required: true, message: '请输入商户名称', trigger: 'blur' }], mer_name: [{ required: true, message: "请输入商户名称", trigger: "blur" }],
real_name: [{ required: true, message: '请输入商户姓名', trigger: 'blur' }], real_name: [{ required: true, message: "请输入商户姓名", trigger: "blur" }],
mer_address: [{ required: true, message: '请输入商户地址', trigger: 'blur' }], mer_address: [{ required: true, message: "请输入商户地址", trigger: "blur" }],
service_phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }], service_phone: [{ required: true, message: "请输入手机号", trigger: "blur" }],
product_score: [{ required: true, message: '请输入评分', trigger: 'blur' }], product_score: [{ required: true, message: "请输入评分", trigger: "blur" }],
type_name: [{ required: true, message: '请输入店铺类型', trigger: 'blur' }], type_name: [{ required: true, message: "请输入店铺类型", trigger: "blur" }],
}) });
const getDetails = async () => { const getDetails = async () => {
const data = await merchantDetail({ const data = await merchantDetail({
mer_id: route.query.id mer_id: route.query.id,
}); });
formData.value = data; formData.value = data;
} };
// //
const merchantData: any = ref([]); const merchantData: any = ref([]);
const getMerchantCate = async () => { const getMerchantCate = async () => {
merchantData.value = await merchantCate(""); merchantData.value = await merchantCate("");
} };
const handleSave = async () => { const handleSave = async () => {
await formRef.value?.validate() await formRef.value?.validate();
if (route.query.id) { if (route.query.id) {
await merchantUpdate(formData.value) await merchantUpdate(formData.value);
} else {
await articleAdd(formData.value)
}
router.back()
} }
router.back();
};
route.query.id && getDetails(); route.query.id && getDetails();
getMerchantCate(); getMerchantCate();

View File

@ -6,8 +6,9 @@
class="mb-[-16px] mt-[16px]" class="mb-[-16px] mt-[16px]"
:model="queryParams" :model="queryParams"
:inline="true" :inline="true"
label-width="80"
> >
<el-form-item label="商户名称"> <el-form-item label="商户名称" prop="mer_name">
<el-input <el-input
class="w-[280px]" class="w-[280px]"
v-model="queryParams.mer_name" v-model="queryParams.mer_name"
@ -17,28 +18,9 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="真实姓名"> <el-form-item label="店铺类型" prop="type_id">
<el-input
class="w-[280px]"
v-model="queryParams.real_name"
placeholder="请输入真实姓名"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="商户地址">
<el-input
class="w-[280px]"
v-model="queryParams.mer_address"
placeholder="请输入地址"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="店铺类型">
<el-select <el-select
class="w-[280px]"
placeholder="请输入店铺类型" placeholder="请输入店铺类型"
v-model="queryParams.type_id" v-model="queryParams.type_id"
@keyup.enter="resetPage" @keyup.enter="resetPage"
@ -51,6 +33,129 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="商户分类" prop="category_id">
<el-select
class="w-[280px]"
placeholder="请选择商户分类"
v-model="queryParams.category_id"
@keyup.enter="resetPage"
>
<el-option
v-for="item in categoryList"
:label="item.category_name"
:value="item.merchant_category_id"
:key="item.merchant_category_id"
/>
</el-select>
</el-form-item>
<el-form-item label="是否批发" prop="wholesale">
<el-select
class="w-[280px]"
placeholder="请选择是否批发"
v-model="queryParams.wholesale"
@keyup.enter="resetPage"
>
<el-option label="仅零售" value="0" />
<el-option label="仅批发" value="1" />
<el-option label="零售批发" value="2" />
</el-select>
</el-form-item>
<el-form-item label="商户姓名" prop="real_name">
<el-input
class="w-[280px]"
v-model="queryParams.real_name"
placeholder="请输入商户姓名"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="详细地址" prop="mer_address">
<el-input
class="w-[280px]"
v-model="queryParams.mer_address"
placeholder="请输入详细地址"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="采购金额" prop="purchase_amount">
<el-input
class="w-[280px]"
v-model="queryParams.purchase_amount"
placeholder="请输入采购金额"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="销售金额" prop="sale_amount">
<el-input
class="w-[280px]"
v-model="queryParams.sale_amount"
placeholder="请输入销售金额"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="店铺评分" prop="product_score">
<el-input
class="w-[280px]"
v-model="queryParams.product_score"
placeholder="请输入店铺评分"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="真实姓名" prop="real_name">
<el-input
class="w-[280px]"
v-model="queryParams.real_name"
placeholder="请输入真实姓名"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="区、县" prop="category_id">
<el-select
class="w-[280px]"
placeholder="请选择区、县"
v-model="queryParams.area_id"
@keyup.enter="resetPage"
@change="areaChange"
>
<el-option
v-for="item in areaList"
:label="item.area_name"
:value="item.area_code"
:key="item.area_code"
/>
</el-select>
</el-form-item>
<el-form-item label="镇、街道" prop="street_id">
<el-select
class="w-[280px]"
placeholder="请选择镇、街道"
v-model="queryParams.street_id"
@keyup.enter="resetPage"
>
<el-option
v-for="item in streetList"
:label="item.street_name"
:value="item.street_code"
:key="item.street_code"
/>
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button> <el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button> <el-button @click="resetParams">重置</el-button>
@ -60,6 +165,7 @@
<el-card class="!border-none mt-4" shadow="never"> <el-card class="!border-none mt-4" shadow="never">
<div class="toolbar"> <div class="toolbar">
<el-button type="primary" @click="onBindBatch" size="small"> 批量绑定 </el-button> <el-button type="primary" @click="onBindBatch" size="small"> 批量绑定 </el-button>
<el-button type="primary" @click="onDownload" size="small">导出</el-button>
</div> </div>
<el-table <el-table
@ -70,6 +176,26 @@
@selection-change="onSelectionChange" @selection-change="onSelectionChange"
> >
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column type="expand">
<template #default="props">
<el-form label-position="left" inline>
<el-row style="margin-left: 120px">
<el-col :span="6">
<el-form-item label="跟进时间:" style="margin-bottom: 0px">
{{
(props.row.follow_log && props.row.follow_log.create_time) || "-"
}}
</el-form-item>
</el-col>
<el-col :span="18">
<el-form-item label="备注:" style="margin-bottom: 0px">
{{ (props.row.follow_log && props.row.follow_log.remark) || "-" }}
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
</el-table-column>
<el-table-column label="商户名称" prop="mer_name" min-width="220" /> <el-table-column label="商户名称" prop="mer_name" min-width="220" />
<el-table-column label="真实姓名" prop="real_name" min-width="100" /> <el-table-column label="真实姓名" prop="real_name" min-width="100" />
<el-table-column label="总采购金额" prop="purchase_amount" min-width="100" /> <el-table-column label="总采购金额" prop="purchase_amount" min-width="100" />
@ -79,11 +205,12 @@
<el-table-column label="店铺类型" prop="type_name" min-width="120" /> <el-table-column label="店铺类型" prop="type_name" min-width="120" />
<el-table-column label="店铺商品数" prop="goods_num" min-width="110" /> <el-table-column label="店铺商品数" prop="goods_num" min-width="110" />
<el-table-column label="地址" prop="mer_address" min-width="220" /> <el-table-column label="地址" prop="mer_address" min-width="220" />
<el-table-column label="操作" width="200" fixed="right"> <el-table-column label="操作" width="240" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button <el-button
v-perms="['merchant.merchant/edit', 'merchant.merchant/add:edit']" v-perms="['merchant.merchant/edit', 'merchant.merchant/add:edit']"
type="primary" type="primary"
size="small"
link link
> >
<router-link <router-link
@ -97,20 +224,38 @@
编辑 编辑
</router-link> </router-link>
</el-button> </el-button>
<el-button <el-button
v-perms="['merchant.merchant/share']" v-perms="['merchant.merchant/edit', 'merchant.merchant/add:edit']"
type="primary" type="primary"
@click="onBind(row)"
link
size="small" size="small"
link
@click="onTraceRecord(row)"
> >
商户分配 添加跟进记录
</el-button>
<el-button type="primary" @click="onMonitor(row)" link size="small">
督导情况
</el-button> </el-button>
<el-dropdown @command="onDropdownClick($event, row)">
<el-button type="primary" link size="small"
>更多操作 <el-icon class="el-icon--right"><arrow-down /></el-icon
></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="mer_share">商户绑定</el-dropdown-item>
<el-dropdown-item command="mer_monitor">督导情况</el-dropdown-item>
<el-dropdown-item command="mer_record">
<router-link
:to="{
path: getRoutePath('record.record/lists'),
query: {
id: row.mer_id,
},
}"
>
跟进记录
</router-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -160,18 +305,18 @@
:async="true" :async="true"
width="70%" width="70%"
@confirm="onBindSubmit" @confirm="onBindSubmit"
@close="onClose" @close="onMonitorClose"
> >
<el-form ref="formRef" :model="monitorForm" label-width="180px"> <el-form ref="formRef" :model="monitorForm" label-width="180px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="督导师" label="督导师"
prop="supervisor" prop="store_condition.supervisor"
:rules="{ required: true, message: '督导师不能为空', trigger: 'blur' }" :rules="{ required: true, message: '督导师不能为空', trigger: 'blur' }"
> >
<el-input <el-input
v-model="monitorForm.supervisor" v-model="monitorForm.store_condition.supervisor"
placeholder="请输入督导师" placeholder="请输入督导师"
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -180,11 +325,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="成活率" label="成活率"
prop="supervisor_rate" prop="store_condition.survival_rate"
:rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }" :rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }"
> >
<el-input-number <el-input-number
v-model="monitorForm.supervisor_rate" v-model="monitorForm.store_condition.survival_rate"
placeholder="请输入成活率" placeholder="请输入成活率"
controls-position="right" controls-position="right"
:min="0.01" :min="0.01"
@ -198,11 +343,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="督导员" label="督导员"
prop="administrator" prop="store_condition.administrator"
:rules="{ required: true, message: '督导员不能为空', trigger: 'blur' }" :rules="{ required: true, message: '督导员不能为空', trigger: 'blur' }"
> >
<el-input <el-input
v-model="monitorForm.administrator" v-model="monitorForm.store_condition.administrator"
placeholder="请输入督导员" placeholder="请输入督导员"
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -211,11 +356,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="成活率" label="成活率"
prop="survival_rate" prop="store_condition.administrator_rate"
:rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }" :rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }"
> >
<el-input-number <el-input-number
v-model="monitorForm.survival_rate" v-model="monitorForm.store_condition.administrator_rate"
placeholder="请输入成活率" placeholder="请输入成活率"
controls-position="right" controls-position="right"
:min="0.01" :min="0.01"
@ -229,11 +374,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="运营对接人" label="运营对接人"
prop="operate" prop="store_condition.operate"
:rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }" :rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }"
> >
<el-input <el-input
v-model="monitorForm.operate" v-model="monitorForm.store_condition.operate"
placeholder="请输入运营对接人" placeholder="请输入运营对接人"
></el-input> ></el-input>
</el-form-item> </el-form-item>
@ -242,11 +387,11 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="成活率" label="成活率"
prop="operate_rate" prop="store_condition.operate_rate"
:rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }" :rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }"
> >
<el-input-number <el-input-number
v-model="monitorForm.operate_rate" v-model="monitorForm.store_condition.operate_rate"
placeholder="请输入成活率" placeholder="请输入成活率"
controls-position="right" controls-position="right"
:min="0.01" :min="0.01"
@ -260,7 +405,7 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item
label="监督人商户总体情况" label="监督人商户总体情况"
prop="condition" prop="store_condition.condition"
:rules="{ :rules="{
required: true, required: true,
message: '监督人商户总体情况不能为空', message: '监督人商户总体情况不能为空',
@ -268,12 +413,84 @@
}" }"
> >
<el-input <el-input
v-model="monitorForm.condition" v-model="monitorForm.store_condition.condition"
placeholder="请输入监督人商户总体情况" placeholder="请输入监督人商户总体情况"
></el-input> ></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item
label="技术"
prop="store_condition.technology"
:rules="{
required: true,
message: '技术不能为空',
trigger: 'blur',
}"
>
<el-input
v-model="monitorForm.store_condition.technology"
clearable
placeholder="请输入技术"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="财务"
prop="store_condition.finance"
:rules="{
required: true,
message: '财务不能为空',
trigger: 'blur',
}"
>
<el-input
v-model="monitorForm.store_condition.finance"
clearable
placeholder="请输入财务"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="推广"
prop="store_condition.extend"
:rules="{
required: true,
message: '推广不能为空',
trigger: 'blur',
}"
>
<el-input
v-model="monitorForm.store_condition.extend"
clearable
placeholder="请输入推广"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="风控"
prop="store_condition.risk"
:rules="{
required: true,
message: '风控不能为空',
trigger: 'blur',
}"
>
<el-input
v-model="monitorForm.store_condition.risk"
clearable
placeholder="请输入风控"
/>
</el-form-item>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="商户总更新"> <el-form-item label="商户总更新">
<el-input <el-input
@ -593,6 +810,8 @@
</el-row> </el-row>
</el-form> </el-form>
</Popup> </Popup>
<!-- 跟进记录 -->
<edit-popup ref="editRef" @success="getLists" @close="showEdit = false" />
</div> </div>
</template> </template>
@ -604,16 +823,24 @@ import {
merchantCate, merchantCate,
storeSuper, storeSuper,
merchantUpdate, merchantUpdate,
excelApi,
merchantCategoryApi,
merchantAreaListApi,
streetListApi,
saveConditionApi,
recordManApi,
} from "@/api/merchant"; } from "@/api/merchant";
import { usePaging } from "@/hooks/usePaging"; import { usePaging } from "@/hooks/usePaging";
import { useRoute } from "vue-router";
import { getRoutePath } from "@/router"; import { getRoutePath } from "@/router";
import feedback from "@/utils/feedback"; import feedback from "@/utils/feedback";
import { ref } from "vue"; import { ref } from "vue";
import Popup from "@/components/popup/index.vue"; import Popup from "@/components/popup/index.vue";
import EditPopup from "@/views/record/edit.vue"; //
const monitorRef: any = ref(null); // const monitorRef: any = ref(null); //
const formRef: any = ref(null); // const formRef: any = ref(null); //
const popupRef = shallowRef<InstanceType<typeof Popup>>(); // const popupRef = shallowRef<InstanceType<typeof Popup>>(); //dialog
const tableRef: any = ref(null); // const tableRef: any = ref(null); //
const emit = defineEmits(["success", "close"]); const emit = defineEmits(["success", "close"]);
@ -626,9 +853,37 @@ const queryParams = reactive({
sale_amount: "", sale_amount: "",
type_id: "", type_id: "",
product_score: "", product_score: "",
mer_ids: "",
category_id: "",
wholesale: "",
area_id: "",
street_id: "",
}); });
//
const route = useRoute();
const merIds = route.query.mer_ids || "";
if (merIds) {
queryParams.mer_ids = merIds as string;
}
// //
const onDropdownClick = (type: any, item: any) => {
switch (type) {
case "mer_share": //
onBind(item);
break;
case "mer_monitor": //
onMonitor(item);
break;
case "mer_record": //
onTraceRecord(item);
break;
}
};
//
const bindForm: any = reactive({ const bindForm: any = reactive({
id: "", id: "",
merchant_ids: [], merchant_ids: [],
@ -653,6 +908,11 @@ const onBindBatch = async () => {
}); });
}; };
//
const onDownload = () => {
excelApi(queryParams).then((res) => {});
};
// //
const handleSubmit = async () => { const handleSubmit = async () => {
if (!bindForm.admin_id) return feedback.msgError("请选择子管理员!"); if (!bindForm.admin_id) return feedback.msgError("请选择子管理员!");
@ -661,20 +921,21 @@ const handleSubmit = async () => {
onClose(); onClose();
}; };
// dialog
const onClose = () => {
bindForm.merchant_ids = [];
tableRef.value?.clearSelection();
monitorRef.value?.close();
popupRef.value?.close();
};
// //
const selectData = ref<any[]>([]); const selectData = ref<any[]>([]);
const onSelectionChange = (val: any[]) => { const onSelectionChange = (val: any[]) => {
selectData.value = val.map(({ mer_id }) => mer_id); selectData.value = val.map(({ mer_id }) => mer_id);
}; };
//
const onClose = () => {
bindForm.merchant_ids = [];
tableRef.value?.clearSelection();
};
//
const onMonitorClose = () => {};
// //
const onMonitor = async (row: any) => { const onMonitor = async (row: any) => {
let detail = await storeSuper({ mer_id: row.mer_id }); let detail = await storeSuper({ mer_id: row.mer_id });
@ -685,7 +946,7 @@ const onMonitor = async (row: any) => {
// //
const onBindSubmit = async () => { const onBindSubmit = async () => {
await formRef.value?.validate(); await formRef.value?.validate();
await merchantUpdate(monitorForm.value); await saveConditionApi(monitorForm.value.store_condition);
monitorRef.value?.close(); monitorRef.value?.close();
emit("success"); emit("success");
}; };
@ -732,31 +993,61 @@ const monitorForm: any = ref({
incomeAllSubsidies: "", // incomeAllSubsidies: "", //
incomeBalanceSubsidies: "", // incomeBalanceSubsidies: "", //
incomeTodaySubsides: "", // incomeTodaySubsides: "", //
store_condition: {},
}); });
//
const editRef = shallowRef<InstanceType<typeof EditPopup>>();
//
const showEdit = ref(false);
//
const onTraceRecord = (item: any) => {
showEdit.value = true;
editRef.value?.open("add", item.mer_id);
};
// //
const { pager, getLists, resetPage, resetParams } = usePaging({ const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: merchantList, fetchFun: merchantList,
params: queryParams, params: queryParams,
}); });
// //
const merchantData: any = ref([]); const merchantData: any = ref([]);
const getMerchantCate = async () => { const getMerchantCate = async () => {
merchantData.value = await merchantCate(""); merchantData.value = await merchantCate("");
}; };
const categoryList = ref([]);
merchantCategoryApi("").then((res) => {
categoryList.value = res;
});
// //
const children: any = ref([]); const children = ref([]);
const getChildManagement = async () => { recordManApi("").then((res) => {
children.value = await childManagement(""); children.value = res;
});
//
const areaList = ref([]);
merchantAreaListApi("").then((res) => {
areaList.value = res;
});
//
const streetList = ref([]);
const areaChange = () => {
queryParams.street_id = "";
streetListApi({ area_code: queryParams.area_id }).then((res) => {
streetList.value = res;
});
}; };
getLists(); getLists();
//
getChildManagement();
// //
getMerchantCate(); getMerchantCate();
</script> </script>
@ -765,7 +1056,7 @@ getMerchantCate();
margin-bottom: 10px; margin-bottom: 10px;
} }
.el-row { .el-row {
height: 70vh; max-height: 70vh;
overflow: auto; overflow: auto;
} }
@ -775,4 +1066,11 @@ getMerchantCate();
.el-input-number { .el-input-number {
width: 100%; width: 100%;
} }
.el-dropdown {
margin-left: 12px;
vertical-align: middle;
}
.el-table__expanded-cell {
background: #efefef;
}
</style> </style>

342
src/views/monitor/edit.vue Normal file
View File

@ -0,0 +1,342 @@
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="800px"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="160px" :rules="formRules">
<el-row>
<el-col :span="12">
<el-form-item label="商户名称" prop="mer_id">
<el-select
placeholder="请选择商户"
v-model="formData.mer_id"
style="width: 100%"
filterable
>
<el-option
v-for="(item, indx) in merListNoPage"
:key="item.mer_id"
:value="item.mer_id"
:label="item.mer_name"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="监督人" prop="supervisor_more">
<el-input
v-model="formData.supervisor_more"
clearable
placeholder="请输入监督人"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运营人员" prop="operate">
<el-input
v-model="formData.operate"
clearable
placeholder="请输入运营人员"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运营成活率" prop="operate_rate">
<el-input-number
v-model="formData.operate_rate"
placeholder="请输入运营成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导师" prop="supervisor">
<el-input
v-model="formData.supervisor"
clearable
placeholder="请输入督导师"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导师成活率" prop="survival_rate">
<el-input-number
v-model="formData.survival_rate"
placeholder="请输入督导师成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导员" prop="administrator">
<el-input
v-model="formData.administrator"
clearable
placeholder="请输入督导员"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导员成活率" prop="administrator_rate">
<el-input-number
v-model="formData.administrator_rate"
placeholder="请输入督导员成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="监督人商户总体情况" prop="condition">
<el-input
v-model="formData.condition"
clearable
placeholder="请输入监督人商户总体情况"
type="textarea"
:rows="3"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="技术" prop="technology">
<el-input
v-model="formData.technology"
clearable
placeholder="请输入技术"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="财务" prop="finance">
<el-input v-model="formData.finance" clearable placeholder="请输入财务" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="推广" prop="extend">
<el-input v-model="formData.extend" clearable placeholder="请输入推广" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="风控" prop="risk">
<el-input v-model="formData.risk" clearable placeholder="请输入风控" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup name="recordEdit">
import type { FormInstance } from "element-plus";
import Popup from "@/components/popup/index.vue";
import { merchantListNoPage } from "@/api/merchant";
import { monitorAddApi, monitorEditApi, monitorDetailApi } from "@/api/monitor";
import type { PropType } from "vue";
const emit = defineEmits(["success", "close"]);
const formRef = shallowRef<FormInstance>();
const popupRef = shallowRef<InstanceType<typeof Popup>>();
const mode = ref("add");
//
const popupTitle = computed(() => {
return mode.value == "edit" ? "编辑督导记录" : "新增督导记录";
});
//
const formData = reactive({
id: "",
admin_id: "",
mer_id: "",
operate: "",
operate_rate: 0,
supervisor: "",
survival_rate: 0,
administrator: "",
administrator_rate: 0,
supervisor_more: "",
technology: "",
finance: "",
extend: "",
risk: "",
condition: "",
});
//
const formRules = reactive<any>({
mer_id: [
{
required: true,
message: "请选择商户",
trigger: ["change"],
},
],
operate: [
{
required: true,
message: "请输入运营人员",
trigger: ["change"],
},
],
operate_rate: [
{
required: true,
message: "请输入运营成活率",
trigger: ["change"],
},
],
supervisor: [
{
required: true,
message: "请输入督导师",
trigger: ["change"],
},
],
survival_rate: [
{
required: true,
message: "请输入督导成活率",
trigger: ["change"],
},
],
administrator: [
{
required: true,
message: "请输入督导员",
trigger: ["change"],
},
],
administrator_rate: [
{
required: true,
message: "请输入督导成活率",
trigger: ["change"],
},
],
supervisor_more: [
{
required: true,
message: "请输入监督人",
trigger: ["change"],
},
],
technology: [
{
required: true,
message: "请输入技术",
trigger: ["change"],
},
],
finance: [
{
required: true,
message: "请输入财务",
trigger: ["change"],
},
],
extend: [
{
required: true,
message: "请输入推广",
trigger: ["change"],
},
],
risk: [
{
required: true,
message: "请输入风控",
trigger: ["change"],
},
],
condition: [
{
required: true,
message: "请输入监督人商户总体情况",
trigger: ["change"],
},
],
});
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key];
}
}
};
//
const merListNoPage: any = ref([]);
merchantListNoPage("").then((rs) => {
merListNoPage.value = rs;
});
const getDetail = async (row: Record<string, any>) => {
const data = await monitorDetailApi({
id: row.id,
});
setFormData(data);
};
//
const handleSubmit = async () => {
await formRef.value?.validate();
const data = { ...formData };
mode.value == "edit" ? await monitorEditApi(data) : await monitorAddApi(data);
popupRef.value?.close();
emit("success");
};
//
const open = (type = "add") => {
mode.value = type;
popupRef.value?.open();
};
//
const handleClose = () => {
emit("close");
};
defineExpose({
open,
setFormData,
getDetail,
});
</script>
<style lang="scss">
.el-input__wrapper,
.el-input-number {
width: 100%;
}
</style>

259
src/views/monitor/index.vue Normal file
View File

@ -0,0 +1,259 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-form class="mb-[-16px]" :model="queryParams" inline label-width="80">
<el-form-item label="店铺类型" prop="mer_id">
<el-select
class="w-[280px]"
placeholder="请输入店铺类型"
v-model="queryParams.mer_id"
@keyup.enter="resetPage"
>
<el-option
v-for="item in merchantData"
:label="item.type_name"
:value="item.mer_type_id"
:key="item.mer_type_id"
/>
</el-select>
</el-form-item>
<el-form-item label="监督人" prop="supervisor_more">
<el-input
class="w-[280px]"
v-model="queryParams.supervisor_more"
placeholder="请输入监督人"
/>
</el-form-item>
<el-form-item label="运营" prop="operate">
<el-input
class="w-[280px]"
v-model="queryParams.operate"
placeholder="请输入运营人姓名"
/>
</el-form-item>
<el-form-item label="督导师" prop="supervisor">
<el-input
class="w-[280px]"
v-model="queryParams.supervisor"
placeholder="请输入督导师"
/>
</el-form-item>
<el-form-item label="督导员" prop="administrator">
<el-input
class="w-[280px]"
v-model="queryParams.administrator"
placeholder="请输入督导员"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never">
<el-button v-perms="['monitor.monitor/add']" type="primary" @click="handleAdd">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
<div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column
label="商户名称"
prop="merchant_name"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
label="运营"
prop="operate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="operate_rate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="督导师"
prop="supervisor"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="survival_rate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="督导员"
prop="administrator"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="administrator_rate"
show-overflow-tooltip
/>
<el-table-column
label="监督人商户总体情况"
prop="condition"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
label="监督人"
prop="supervisor_more"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="技术"
prop="technology"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="财务"
prop="finance"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="推广"
prop="extend"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="风控"
prop="risk"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="创建管理员"
prop="admin_name"
min-width="160"
show-overflow-tooltip
/>
<el-table-column
label="创建时间"
prop="create_time"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['record.record/edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
v-perms="['record.record/delete']"
type="danger"
link
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup
v-if="showEdit"
ref="editRef"
@success="getLists"
@close="showEdit = false"
/>
</div>
</template>
<script lang="ts" setup name="recordLists">
import { usePaging } from "@/hooks/usePaging";
import { monitorListApi, monitorDelApi } from "@/api/monitor";
import { merchantCate } from "@/api/merchant";
import { timeFormat } from "@/utils/util";
import feedback from "@/utils/feedback";
import EditPopup from "./edit.vue";
const editRef = shallowRef<InstanceType<typeof EditPopup>>();
//
const showEdit = ref(false);
//
const queryParams = reactive({
mer_id: "",
operate: "",
supervisor: "",
administrator: "",
supervisor_more: "",
});
//
const selectData = ref<any[]>([]);
//
const handleSelectionChange = (val: any[]) => {
selectData.value = val.map(({ id }) => id);
};
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
};
//
const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: monitorListApi,
params: queryParams,
});
//
const handleAdd = async () => {
showEdit.value = true;
await nextTick();
editRef.value?.open("add");
};
//
const handleEdit = async (data: any) => {
showEdit.value = true;
await nextTick();
editRef.value?.open("edit");
editRef.value?.setFormData(data);
};
//
const handleDelete = async (id: number | any[]) => {
await feedback.confirm("确定要删除?");
await monitorDelApi({ id });
getLists();
};
getLists();
getMerchantCate();
</script>

View File

@ -1,34 +1,71 @@
<template> <template>
<div class="edit-popup"> <div class="edit-popup">
<popup ref="popupRef" :title="popupTitle" :async="true" width="550px" @confirm="handleSubmit" <popup
@close="handleClose"> ref="popupRef"
:title="popupTitle"
:async="true"
width="550px"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules"> <el-form ref="formRef" :model="formData" label-width="90px" :rules="formRules">
<el-form-item label="主题" prop="theme"> <el-form-item label="主题" prop="theme">
<el-input v-model="formData.theme" clearable placeholder="请输入主题" /> <el-input v-model="formData.theme" clearable placeholder="请输入主题" />
</el-form-item> </el-form-item>
<el-form-item label="商户名称" prop="merchant_id"> <el-form-item label="商户名称" prop="merchant_id">
<el-select placeholder="请选择商户" v-model="formData.merchant_id" style="width:100%;"> <el-select
<el-option v-for="(item, indx) in recordData" :key="item.id" :value="item.id" placeholder="请选择商户"
:label="item.name"></el-option> v-model="formData.merchant_id"
style="width: 100%"
filterable
>
<el-option
v-for="(item, indx) in merListNoPage"
:key="item.mer_id"
:value="item.mer_id"
:label="item.mer_name"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="记录类型" prop="rid"> <el-form-item label="记录类型" prop="rid">
<el-select placeholder="请选择记录类型" v-model="formData.rid" style="width:100%;"> <el-select
<el-option v-for="(item, indx) in recordData" :key="item.id" :value="item.id" placeholder="请选择记录类型"
:label="item.name"></el-option> v-model="formData.rid"
style="width: 100%"
>
<el-option
v-for="(item, indx) in recordList"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="记录人" prop="admin_id"> <el-form-item label="记录人" prop="admin_id">
<el-select placeholder="请选择记录人" v-model="formData.admin_id" style="width:100%;"> <el-select
<el-option v-for="(item, indx) in recordInfo" :key="item.id" :value="item.id" placeholder="请选择记录人"
:label="item.name"></el-option> v-model="formData.admin_id"
style="width: 100%"
>
<el-option
v-for="(item, indx) in recordInfoList"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="跟进时间" prop="flow_time"> <el-form-item label="跟进时间" prop="flow_time">
<el-date-picker v-model="formData.flow_time" type="date" placeholder="请选择跟进时间" style="width: 100%;"> <el-date-picker
v-model="formData.flow_time"
type="date"
placeholder="请选择跟进时间"
style="width: 100%"
>
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
@ -41,115 +78,136 @@
</template> </template>
<script lang="ts" setup name="recordEdit"> <script lang="ts" setup name="recordEdit">
import type { FormInstance } from 'element-plus' import type { FormInstance } from "element-plus";
import Popup from '@/components/popup/index.vue' import Popup from "@/components/popup/index.vue";
import { apiRecordAdd, apiRecordEdit, apiRecordDetail } from '@/api/record' import {
import { timeFormat } from '@/utils/util' apiRecordAdd,
import type { PropType } from 'vue'; apiRecordEdit,
import { defineProps } from 'vue' apiRecordDetail,
defineProps({ recordAll,
recordData: { recordInfo,
type: Array as PropType<any[]>, } from "@/api/record";
default: () => [] import { merchantListNoPage } from "@/api/merchant";
}, import { getCurrentInstance,type ComponentInternalInstance ,onMounted} from 'vue';
recordInfo: {
type: Array as PropType<any[]>, const emit = defineEmits(["success", "close"]);
default: () => [] const formRef = shallowRef<FormInstance>();
} const popupRef = shallowRef<InstanceType<typeof Popup>>();
}) const mode = ref("add");
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
// //
const popupTitle = computed(() => { const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑跟踪记录表' : '新增跟踪记录表' return mode.value == "edit" ? "编辑跟踪记录" : "新增跟踪记录";
}) });
// //
const formData = reactive({ const formData = reactive({
id: '', id: "",
theme: '', theme: "",
rid: '', rid: "",
admin_id: '', admin_id: "",
remark: '', remark: "",
flow_time: '', flow_time: "",
merchant_id: '' merchant_id: "",
}) });
// //
const formRules = reactive<any>({ const formRules = reactive<any>({
theme: [{ theme: [
{
required: true, required: true,
message: '请输入主题', message: "请输入主题",
trigger: ['blur'] trigger: ["blur"],
}], },
rid: [{ ],
rid: [
{
required: true, required: true,
message: '请选择商户记录类型', message: "请选择商户记录类型",
trigger: ['change'] trigger: ["change"],
}], },
admin_id: [{ ],
admin_id: [
{
required: true, required: true,
message: '请选择商户记录人', message: "请选择商户记录人",
trigger: ['change'] trigger: ["change"],
}], },
merchant_id: [{ ],
merchant_id: [
{
required: true, required: true,
message: '请选择商户', message: "请选择商户",
trigger: ['change'] trigger: ["change"],
}], },
}) ],
});
// //
const setFormData = async (data: Record<any, any>) => { const setFormData = async (data: Record<any, any>) => {
for (const key in formData) { for (const key in formData) {
if (data[key] != null && data[key] != undefined) { if (data[key] != null && data[key] != undefined) {
//@ts-ignore //@ts-ignore
formData[key] = data[key] formData[key] = data[key];
}
} }
} }
};
//
const recordList: any = ref([]);
recordAll("").then((res) => {
recordList.value = res;
});
//
const recordInfoList: any = ref([]);
recordInfo("").then((res) => {
recordInfoList.value = res;
});
//
const merListNoPage: any = ref([]);
merchantListNoPage("").then((rs) => {
merListNoPage.value = rs;
});
const getDetail = async (row: Record<string, any>) => { const getDetail = async (row: Record<string, any>) => {
const data = await apiRecordDetail({ const data = await apiRecordDetail({
id: row.id id: row.id,
}) });
setFormData(data) setFormData(data);
} };
// //
const handleSubmit = async () => { const handleSubmit = async () => {
await formRef.value?.validate() await formRef.value?.validate();
const data = { ...formData, } const data = { ...formData };
mode.value == 'edit' mode.value == "edit" ? await apiRecordEdit(data) : await apiRecordAdd(data);
? await apiRecordEdit(data) popupRef.value?.close();
: await apiRecordAdd(data) emit("success");
popupRef.value?.close() const {appContext} = getCurrentInstance()as ComponentInternalInstance;
emit('success') appContext.config.globalProperties.$mitt.emit("selfEvent");
} };
// //
const open = (type = 'add') => { const open = (type = "add", merId = "") => {
mode.value = type mode.value = type;
popupRef.value?.open() popupRef.value?.open();
if (merId) {
formData.merchant_id = merId;
} }
};
// //
const handleClose = () => { const handleClose = () => {
emit('close') emit("close");
} };
defineExpose({ defineExpose({
open, open,
setFormData, setFormData,
getDetail getDetail,
}) });
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -2,28 +2,68 @@
<div> <div>
<el-card class="!border-none mb-4" shadow="never"> <el-card class="!border-none mb-4" shadow="never">
<el-form class="mb-[-16px]" :model="queryParams" inline> <el-form class="mb-[-16px]" :model="queryParams" inline>
<el-form-item label="主题" prop="theme"> <el-form-item label="商户名称" prop="merchant_id">
<el-input class="w-[280px]" v-model="queryParams.theme" clearable placeholder="请输入主题" /> <el-select
</el-form-item> placeholder="请选择商户"
<el-form-item label="记录类型" prop="rid"> v-model="queryParams.merchant_id"
<el-select placeholder="请选择记录类型" v-model="queryParams.rid" class="w-[280px]"> class="w-[280px]"
<el-option v-for="item in recordList" :key="item.id" :value="item.id" filterable
:label="item.name"></el-option> >
</el-select> <el-option
</el-form-item> v-for="(item, indx) in merListNoPage"
<el-form-item label="记录人" prop="admin_id"> :key="item.mer_id"
<el-select placeholder="请选择记录人" v-model="queryParams.admin_id" class="w-[280px]"> :value="item.mer_id + ''"
<el-option v-for="item in recordInfoList" :key="item.id" :value="item.id" :label="item.mer_name"
:label="item.name"></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="商户" prop="merchant_id"> <el-form-item label="跟进主题" prop="theme">
<el-input class="w-[280px]" v-model="queryParams.merchant_id" clearable placeholder="请选择商户" /> <el-input
class="w-[280px]"
v-model="queryParams.theme"
clearable
placeholder="请输入主题"
/>
</el-form-item>
<el-form-item label="记录类型" prop="rid">
<el-select
placeholder="请选择记录类型"
v-model="queryParams.rid"
class="w-[280px]"
>
<el-option
v-for="item in recordList"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="记录人" prop="admin_id">
<el-select
placeholder="请选择记录人"
v-model="queryParams.admin_id"
class="w-[280px]"
>
<el-option
v-for="item in recordInfoList"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="跟进时间" prop="flow_time"> <el-form-item label="跟进时间" prop="flow_time">
<el-date-picker class="w-[280px]" v-model="queryParams.flow_time" type="date" placeholder="请选择跟进时间"> <el-date-picker
class="w-[280px]"
v-model="queryParams.flow_time"
type="date"
placeholder="请选择跟进时间"
>
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
@ -40,27 +80,59 @@
</template> </template>
新增 新增
</el-button> </el-button>
<el-button v-perms="['record.record/delete']" :disabled="!selectData.length" <el-button
@click="handleDelete(selectData)"> v-perms="['record.record/delete']"
:disabled="!selectData.length"
@click="handleDelete(selectData)"
>
删除 删除
</el-button> </el-button>
<div class="mt-4"> <div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange"> <el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" /> <el-table-column type="selection" width="55" />
<el-table-column
label="商户名称"
prop="merchant_name"
min-width="200"
show-overflow-tooltip
/>
<el-table-column label="主题" prop="theme" show-overflow-tooltip /> <el-table-column label="主题" prop="theme" show-overflow-tooltip />
<el-table-column label="记录类型" prop="cate_name" show-overflow-tooltip /> <el-table-column label="记录类型" prop="cate_name" show-overflow-tooltip />
<el-table-column label="记录人" prop="admin_info" show-overflow-tooltip /> <el-table-column label="记录人" prop="admin_info" show-overflow-tooltip />
<el-table-column label="商户" prop="merchant_name" show-overflow-tooltip /> <el-table-column
<el-table-column label="备注" prop="remark" show-overflow-tooltip /> label="跟进时间"
<el-table-column label="更进时间" prop="flow_time" show-overflow-tooltip /> prop="flow_time"
<el-table-column label="创建时间" prop="create_time" show-overflow-tooltip /> min-width="160"
show-overflow-tooltip
/>
<el-table-column
label="创建时间"
prop="create_time"
min-width="160"
show-overflow-tooltip
/>
<el-table-column
label="备注"
prop="remark"
min-width="150"
show-overflow-tooltip
/>
<el-table-column label="操作" width="120" fixed="right"> <el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button v-perms="['record.record/edit']" type="primary" link @click="handleEdit(row)"> <el-button
v-perms="['record.record/edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑 编辑
</el-button> </el-button>
<el-button v-perms="['record.record/delete']" type="danger" link <el-button
@click="handleDelete(row.id)"> v-perms="['record.record/delete']"
type="danger"
link
@click="handleDelete(row.id)"
>
删除 删除
</el-button> </el-button>
</template> </template>
@ -72,80 +144,95 @@
</div> </div>
</el-card> </el-card>
<edit-popup v-if="showEdit" ref="editRef" :record-info="recordInfoList" :record-data="recordList" <edit-popup
@success="getLists" @close="showEdit = false" /> v-if="showEdit"
ref="editRef"
:record-info="recordInfoList"
:record-data="recordList"
@success="getLists"
@close="showEdit = false"
/>
</div> </div>
</template> </template>
<script lang="ts" setup name="recordLists"> <script lang="ts" setup name="recordLists">
import { usePaging } from '@/hooks/usePaging' import { usePaging } from "@/hooks/usePaging";
import { useDictData } from '@/hooks/useDictOptions' import { apiRecordLists, apiRecordDelete, recordAll, recordInfo } from "@/api/record";
import { apiRecordLists, apiRecordDelete, recordAll, recordInfo } from '@/api/record' import { merchantListNoPage } from "@/api/merchant";
import { timeFormat } from '@/utils/util' import feedback from "@/utils/feedback";
import feedback from '@/utils/feedback' import EditPopup from "./edit.vue";
import EditPopup from './edit.vue'
const editRef = shallowRef<InstanceType<typeof EditPopup>>() const route = useRoute();
const editRef = shallowRef<InstanceType<typeof EditPopup>>();
// //
const showEdit = ref(false); const showEdit = ref(false);
// //
const queryParams = reactive({ const queryParams = reactive({
theme: '', theme: "",
rid: '', rid: "",
admin_id: '', admin_id: "",
merchant_id: '', merchant_id: "",
remark: '', remark: "",
flow_time: '', flow_time: "",
}) });
// //
const selectData = ref<any[]>([]) const selectData = ref<any[]>([]);
// //
const handleSelectionChange = (val: any[]) => { const handleSelectionChange = (val: any[]) => {
selectData.value = val.map(({ id }) => id) selectData.value = val.map(({ id }) => id);
} };
// //
const recordList: any = ref([]); const recordList: any = ref([]);
recordAll("").then(res => { recordAll("").then((res) => {
recordList.value = res; recordList.value = res;
}) });
// //
const recordInfoList: any = ref([]); const recordInfoList: any = ref([]);
recordInfo("").then(res => { recordInfo("").then((res) => {
recordInfoList.value = res; recordInfoList.value = res;
}) });
// //
const { pager, getLists, resetParams, resetPage } = usePaging({ const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: apiRecordLists, fetchFun: apiRecordLists,
params: queryParams params: queryParams,
}) });
// //
const handleAdd = async () => { const handleAdd = async () => {
showEdit.value = true showEdit.value = true;
await nextTick() await nextTick();
editRef.value?.open('add') editRef.value?.open("add");
} };
// //
const handleEdit = async (data: any) => { const handleEdit = async (data: any) => {
showEdit.value = true showEdit.value = true;
await nextTick() await nextTick();
editRef.value?.open('edit') editRef.value?.open("edit");
editRef.value?.setFormData(data) editRef.value?.setFormData(data);
} };
// //
const handleDelete = async (id: number | any[]) => { const handleDelete = async (id: number | any[]) => {
await feedback.confirm('确定要删除?') await feedback.confirm("确定要删除?");
await apiRecordDelete({ id }) await apiRecordDelete({ id });
getLists() getLists();
} };
getLists() //
const merListNoPage: any = ref([]);
merchantListNoPage("").then((rs) => {
merListNoPage.value = rs;
});
const id = route.query.id;
if (id) queryParams.merchant_id = id;
getLists();
</script> </script>

View File

@ -0,0 +1,342 @@
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="800px"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="160px" :rules="formRules">
<el-row>
<el-col :span="12">
<el-form-item label="商户名称" prop="mer_id">
<el-select
placeholder="请选择商户"
v-model="formData.mer_id"
style="width: 100%"
filterable
>
<el-option
v-for="(item, indx) in merListNoPage"
:key="item.mer_id"
:value="item.mer_id"
:label="item.mer_name"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="监督人" prop="supervisor_more">
<el-input
v-model="formData.supervisor_more"
clearable
placeholder="请输入监督人"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运营人员" prop="operate">
<el-input
v-model="formData.operate"
clearable
placeholder="请输入运营人员"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运营成活率" prop="operate_rate">
<el-input-number
v-model="formData.operate_rate"
placeholder="请输入运营成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导师" prop="supervisor">
<el-input
v-model="formData.supervisor"
clearable
placeholder="请输入督导师"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导师成活率" prop="survival_rate">
<el-input-number
v-model="formData.survival_rate"
placeholder="请输入督导师成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导员" prop="administrator">
<el-input
v-model="formData.administrator"
clearable
placeholder="请输入督导员"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="督导员成活率" prop="administrator_rate">
<el-input-number
v-model="formData.administrator_rate"
placeholder="请输入督导员成活率"
controls-position="right"
:min="0"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="监督人商户总体情况" prop="condition">
<el-input
v-model="formData.condition"
clearable
placeholder="请输入监督人商户总体情况"
type="textarea"
:rows="3"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="技术" prop="technology">
<el-input
v-model="formData.technology"
clearable
placeholder="请输入技术"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="财务" prop="finance">
<el-input v-model="formData.finance" clearable placeholder="请输入财务" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="推广" prop="extend">
<el-input v-model="formData.extend" clearable placeholder="请输入推广" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="风控" prop="risk">
<el-input v-model="formData.risk" clearable placeholder="请输入风控" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</popup>
</div>
</template>
<script lang="ts" setup name="recordEdit">
import type { FormInstance } from "element-plus";
import Popup from "@/components/popup/index.vue";
import { merchantListNoPage } from "@/api/merchant";
import { monitorAddApi, monitorEditApi, monitorDetailApi } from "@/api/monitor";
import type { PropType } from "vue";
const emit = defineEmits(["success", "close"]);
const formRef = shallowRef<FormInstance>();
const popupRef = shallowRef<InstanceType<typeof Popup>>();
const mode = ref("add");
//
const popupTitle = computed(() => {
return mode.value == "edit" ? "编辑督导记录" : "新增督导记录";
});
//
const formData = reactive({
id: "",
admin_id: "",
mer_id: "",
operate: "",
operate_rate: 0,
supervisor: "",
survival_rate: 0,
administrator: "",
administrator_rate: 0,
supervisor_more: "",
technology: "",
finance: "",
extend: "",
risk: "",
condition: "",
});
//
const formRules = reactive<any>({
mer_id: [
{
required: true,
message: "请选择商户",
trigger: ["change"],
},
],
operate: [
{
required: true,
message: "请输入运营人员",
trigger: ["change"],
},
],
operate_rate: [
{
required: true,
message: "请输入运营成活率",
trigger: ["change"],
},
],
supervisor: [
{
required: true,
message: "请输入督导师",
trigger: ["change"],
},
],
survival_rate: [
{
required: true,
message: "请输入督导成活率",
trigger: ["change"],
},
],
administrator: [
{
required: true,
message: "请输入督导员",
trigger: ["change"],
},
],
administrator_rate: [
{
required: true,
message: "请输入督导成活率",
trigger: ["change"],
},
],
supervisor_more: [
{
required: true,
message: "请输入监督人",
trigger: ["change"],
},
],
technology: [
{
required: true,
message: "请输入技术",
trigger: ["change"],
},
],
finance: [
{
required: true,
message: "请输入财务",
trigger: ["change"],
},
],
extend: [
{
required: true,
message: "请输入推广",
trigger: ["change"],
},
],
risk: [
{
required: true,
message: "请输入风控",
trigger: ["change"],
},
],
condition: [
{
required: true,
message: "请输入监督人商户总体情况",
trigger: ["change"],
},
],
});
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
formData[key] = data[key];
}
}
};
//
const merListNoPage: any = ref([]);
merchantListNoPage("").then((rs) => {
merListNoPage.value = rs;
});
const getDetail = async (row: Record<string, any>) => {
const data = await monitorDetailApi({
id: row.id,
});
setFormData(data);
};
//
const handleSubmit = async () => {
await formRef.value?.validate();
const data = { ...formData };
mode.value == "edit" ? await monitorEditApi(data) : await monitorAddApi(data);
popupRef.value?.close();
emit("success");
};
//
const open = (type = "add") => {
mode.value = type;
popupRef.value?.open();
};
//
const handleClose = () => {
emit("close");
};
defineExpose({
open,
setFormData,
getDetail,
});
</script>
<style lang="scss">
.el-input__wrapper,
.el-input-number {
width: 100%;
}
</style>

View File

@ -0,0 +1,259 @@
<template>
<div>
<el-card class="!border-none mb-4" shadow="never">
<el-form class="mb-[-16px]" :model="queryParams" inline label-width="80">
<el-form-item label="店铺类型" prop="mer_id">
<el-select
class="w-[280px]"
placeholder="请输入店铺类型"
v-model="queryParams.mer_id"
@keyup.enter="resetPage"
>
<el-option
v-for="item in merchantData"
:label="item.type_name"
:value="item.mer_type_id"
:key="item.mer_type_id"
/>
</el-select>
</el-form-item>
<el-form-item label="监督人" prop="supervisor_more">
<el-input
class="w-[280px]"
v-model="queryParams.supervisor_more"
placeholder="请输入监督人"
/>
</el-form-item>
<el-form-item label="运营" prop="operate">
<el-input
class="w-[280px]"
v-model="queryParams.operate"
placeholder="请输入运营人姓名"
/>
</el-form-item>
<el-form-item label="督导师" prop="supervisor">
<el-input
class="w-[280px]"
v-model="queryParams.supervisor"
placeholder="请输入督导师"
/>
</el-form-item>
<el-form-item label="督导员" prop="administrator">
<el-input
class="w-[280px]"
v-model="queryParams.administrator"
placeholder="请输入督导员"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
<el-button @click="resetParams">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="!border-none" v-loading="pager.loading" shadow="never">
<el-button v-perms="['monitor.monitor/add']" type="primary" @click="handleAdd">
<template #icon>
<icon name="el-icon-Plus" />
</template>
新增
</el-button>
<div class="mt-4">
<el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column
label="商户名称"
prop="merchant_name"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
label="运营"
prop="operate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="operate_rate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="督导师"
prop="supervisor"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="survival_rate"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="督导员"
prop="administrator"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="成活率"
prop="administrator_rate"
show-overflow-tooltip
/>
<el-table-column
label="监督人商户总体情况"
prop="condition"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
label="监督人"
prop="supervisor_more"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="技术"
prop="technology"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="财务"
prop="finance"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="推广"
prop="extend"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="风控"
prop="risk"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
label="创建管理员"
prop="admin_name"
min-width="160"
show-overflow-tooltip
/>
<el-table-column
label="创建时间"
prop="create_time"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['record.record/edit']"
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
v-perms="['record.record/delete']"
type="danger"
link
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup
v-if="showEdit"
ref="editRef"
@success="getLists"
@close="showEdit = false"
/>
</div>
</template>
<script lang="ts" setup name="recordLists">
import { usePaging } from "@/hooks/usePaging";
import { monitorListApi, monitorDelApi } from "@/api/monitor";
import { merchantCate } from "@/api/merchant";
import { timeFormat } from "@/utils/util";
import feedback from "@/utils/feedback";
import EditPopup from "./edit.vue";
const editRef = shallowRef<InstanceType<typeof EditPopup>>();
//
const showEdit = ref(false);
//
const queryParams = reactive({
mer_id: "",
operate: "",
supervisor: "",
administrator: "",
supervisor_more: "",
});
//
const selectData = ref<any[]>([]);
//
const handleSelectionChange = (val: any[]) => {
selectData.value = val.map(({ id }) => id);
};
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
};
//
const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: monitorListApi,
params: queryParams,
});
//
const handleAdd = async () => {
showEdit.value = true;
await nextTick();
editRef.value?.open("add");
};
//
const handleEdit = async (data: any) => {
showEdit.value = true;
await nextTick();
editRef.value?.open("edit");
editRef.value?.setFormData(data);
};
//
const handleDelete = async (id: number | any[]) => {
await feedback.confirm("确定要删除?");
await monitorDelApi({ id });
getLists();
};
getLists();
getMerchantCate();
</script>

View File

@ -75,8 +75,11 @@
<span>常用功能</span> <span>常用功能</span>
</template> </template>
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<div v-for="item in workbenchData.menu" class="md:w-[12.5%] w-1/4 flex flex-col items-center" <div
:key="item"> v-for="item in workbenchData.menu"
class="md:w-[12.5%] w-1/4 flex flex-col items-center"
:key="item"
>
<router-link :to="item.url" class="mb-3 flex flex-col items-center"> <router-link :to="item.url" class="mb-3 flex flex-col items-center">
<image-contain width="40px" height="40px" :src="item?.image" /> <image-contain width="40px" height="40px" :src="item?.image" />
<div class="mt-2">{{ item.name }}</div> <div class="mt-2">{{ item.name }}</div>
@ -91,7 +94,11 @@
<span>本周商品销售数量</span> <span>本周商品销售数量</span>
</template> </template>
<div> <div>
<v-charts style="height: 350px" :option="merchantInfoData.echartOptionNowWeek" :autoresize="true" /> <v-charts
style="height: 350px"
:option="merchantInfoData.echartOptionNowWeek"
:autoresize="true"
/>
</div> </div>
</el-card> </el-card>
</div> </div>
@ -102,8 +109,11 @@
<span>上周周商品销售数量</span> <span>上周周商品销售数量</span>
</template> </template>
<div> <div>
<v-charts style="height: 350px" :option="merchantInfoData.echartOptionLastWeek" <v-charts
:autoresize="true" /> style="height: 350px"
:option="merchantInfoData.echartOptionLastWeek"
:autoresize="true"
/>
</div> </div>
</el-card> </el-card>
</div> </div>
@ -114,6 +124,13 @@
import { getWorkbench, merchantInfo } from '@/api/app' import { getWorkbench, merchantInfo } from '@/api/app'
import { orderBy } from 'lodash-es'; import { orderBy } from 'lodash-es';
import vCharts from 'vue-echarts'; import vCharts from 'vue-echarts';
import { getCurrentInstance,type ComponentInternalInstance ,onMounted} from 'vue';
// bell
onMounted(()=>{
const {appContext} = getCurrentInstance()as ComponentInternalInstance;
appContext.config.globalProperties.$mitt.emit("selfEvent");
})
// //
const merchantInfoData = ref({ const merchantInfoData = ref({