This commit is contained in:
DESKTOP-GMUNQ1B\k 2024-04-18 10:48:31 +08:00
parent 83b1ed2eb6
commit 470b4f540d
17 changed files with 10452 additions and 1849 deletions

View File

@ -1,4 +0,0 @@
NODE_ENV = 'development'
# Base API
VITE_APP_BASE_URL=''

View File

@ -1,3 +0,0 @@
NODE_ENV = 'production'
# Base API
VITE_APP_BASE_URL=''

10757
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,12 +5,22 @@ export function getConfig() {
return request.get({ url: '/config/getConfig' })
}
//字典数据
export function getDictData(params: any) {
return request.get({ url: '/config/dict', params })
}
// 工作台统计数据
// 首页图表统计
export function merchantInfo() {
return request.get({ url: '/workbench/merchantInfo' })
}
// 工作台主页
export function getWorkbench() {
return request.get({ url: '/workbench/index' })
}
//字典数据
export function getDictData(params: any) {
return request.get({ url: '/config/dict', params })
}

0
src/api/home/index.ts Normal file
View File

38
src/api/merchant.ts Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request';
// 商户列表
export function merchantList(params: any) {
return request.get({ url: '/merchant.merchant/lists', params })
}
// 修改商户相关信息
export function merchantUpdate(params: any) {
return request.post({ url: '/merchant.merchant/changeMerchant', params })
}
// 单商户查询
export function merchantDetail(params: any) {
return request.post({ url: '/merchant.merchant/detail', params })
}
// 获取记录人 子管理员
export function childManagement(params: any) {
return request.get({ url: '/record.record/info', params })
}
// 商户管理员绑定
export function merchantBind(params: any) {
return request.post({ url: '/merchant.merchant/merchantPermision', params })
}
// 商户分类
export function merchantCate(params: any) {
return request.get({ url: '/merchant.merchant/merchantCate', params })
}
// 督导相关
export function storeSuper(params: any) {
return request.post({ url: '/merchant.merchant/storeSuper', params })
}

View File

@ -23,4 +23,15 @@ export function apiRecordDelete(params: any) {
// 跟踪记录表详情
export function apiRecordDetail(params: any) {
return request.get({ url: '/record.record/detail', params })
}
}
// 获取记录分类
export function recordAll(params: any) {
return request.get({ url: '/record.record/all', params })
}
// 获取记录人
export function recordInfo(params: any) {
return request.get({ url: '/record.record/info', params })
}

View File

@ -2,7 +2,6 @@ import type { App } from 'vue'
import 'highlight.js/styles/github.css'
import hljs from 'highlight.js/lib/common'
import hljsVuePlugin from '@highlightjs/vue-plugin'
console.log(hljs)
export default (app: App<Element>) => {
app.use(hljsVuePlugin)
}

View File

@ -147,6 +147,7 @@ export class Axios {
requestOptions: opt
}
const { urlPrefix } = opt
// 拼接请求前缀如api
if (urlPrefix) {
axioxConfig.url = `${urlPrefix}${config.url}`

View File

@ -1,130 +1,130 @@
<template>
<div class="login flex flex-col">
<div class="flex-1 flex items-center justify-center">
<div class="login-card flex rounded-md">
<div class="flex-1 h-full hidden md:inline-block">
<image-contain :src="config.login_image" :width="400" height="100%" />
</div>
<div
class="login-form bg-body flex flex-col justify-center px-10 py-10 md:w-[400px] w-[375px] flex-none mx-auto"
>
<div class="text-center text-3xl font-medium mb-8">{{ config.web_name }}</div>
<el-form ref="formRef" :model="formData" size="large" :rules="rules">
<el-form-item prop="account">
<el-input
v-model="formData.account"
placeholder="请输入账号"
@keyup.enter="handleEnter"
>
<template #prepend>
<icon name="el-icon-User" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
ref="passwordRef"
v-model="formData.password"
show-password
placeholder="请输入密码"
@keyup.enter="handleLogin"
>
<template #prepend>
<icon name="el-icon-Lock" />
</template>
</el-input>
</el-form-item>
</el-form>
<div class="mb-5">
<el-checkbox v-model="remAccount" label="记住账号"></el-checkbox>
</div>
<el-button type="primary" size="large" :loading="isLock" @click="lockLogin">
登录
</el-button>
</div>
</div>
<div class="login flex flex-col">
<div class="flex-1 flex items-center justify-center">
<div class="login-card flex rounded-md">
<div class="flex-1 h-full hidden md:inline-block">
<image-contain :src="config.login_image" :width="400" height="100%" />
</div>
<layout-footer />
<div
class="login-form bg-body flex flex-col justify-center px-10 py-10 md:w-[400px] w-[375px] flex-none mx-auto"
>
<div class="text-center text-3xl font-medium mb-8">{{ config.web_name }}</div>
<el-form ref="formRef" :model="formData" size="large" :rules="rules">
<el-form-item prop="account">
<el-input
v-model="formData.account"
placeholder="请输入账号"
@keyup.enter="handleEnter"
>
<template #prepend>
<icon name="el-icon-User" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
ref="passwordRef"
v-model="formData.password"
show-password
placeholder="请输入密码"
@keyup.enter="handleLogin"
>
<template #prepend>
<icon name="el-icon-Lock" />
</template>
</el-input>
</el-form-item>
</el-form>
<div class="mb-5">
<el-checkbox v-model="remAccount" label="记住账号"></el-checkbox>
</div>
<el-button type="primary" size="large" :loading="isLock" @click="lockLogin">
登录
</el-button>
</div>
</div>
</div>
<layout-footer />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, shallowRef } from 'vue'
import type { InputInstance, FormInstance } from 'element-plus'
import LayoutFooter from '@/layout/components/footer.vue'
import useAppStore from '@/stores/modules/app'
import useUserStore from '@/stores/modules/user'
import cache from '@/utils/cache'
import { ACCOUNT_KEY } from '@/enums/cacheEnums'
import { PageEnum } from '@/enums/pageEnum'
import { useLockFn } from '@/hooks/useLockFn'
const passwordRef = shallowRef<InputInstance>()
const formRef = shallowRef<FormInstance>()
const appStore = useAppStore()
const userStore = useUserStore()
const route = useRoute()
const router = useRouter()
const remAccount = ref(false)
const config = computed(() => appStore.config)
import { computed, onMounted, reactive, ref, shallowRef } from "vue";
import type { InputInstance, FormInstance } from "element-plus";
import LayoutFooter from "@/layout/components/footer.vue";
import useAppStore from "@/stores/modules/app";
import useUserStore from "@/stores/modules/user";
import cache from "@/utils/cache";
import { ACCOUNT_KEY } from "@/enums/cacheEnums";
import { PageEnum } from "@/enums/pageEnum";
import { useLockFn } from "@/hooks/useLockFn";
const passwordRef = shallowRef<InputInstance>();
const formRef = shallowRef<FormInstance>();
const appStore = useAppStore();
const userStore = useUserStore();
const route = useRoute();
const router = useRouter();
const remAccount = ref(false);
const config = computed(() => appStore.config);
const formData = reactive({
account: '',
password: ''
})
account: "admin",
password: "123456",
});
const rules = {
account: [
{
required: true,
message: '请输入账号',
trigger: ['blur']
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['blur']
}
]
}
account: [
{
required: true,
message: "请输入账号",
trigger: ["blur"],
},
],
password: [
{
required: true,
message: "请输入密码",
trigger: ["blur"],
},
],
};
//
const handleEnter = () => {
if (!formData.password) {
return passwordRef.value?.focus()
}
handleLogin()
}
if (!formData.password) {
return passwordRef.value?.focus();
}
handleLogin();
};
//
const handleLogin = async () => {
await formRef.value?.validate()
//
cache.set(ACCOUNT_KEY, {
remember: remAccount.value,
account: remAccount.value ? formData.account : ''
})
await userStore.login(formData)
const {
query: { redirect }
} = route
const path = typeof redirect === 'string' ? redirect : PageEnum.INDEX
router.push(path)
}
const { isLock, lockFn: lockLogin } = useLockFn(handleLogin)
await formRef.value?.validate();
//
cache.set(ACCOUNT_KEY, {
remember: remAccount.value,
account: remAccount.value ? formData.account : "",
});
await userStore.login(formData);
const {
query: { redirect },
} = route;
const path = typeof redirect === "string" ? redirect : PageEnum.INDEX;
router.push(path);
};
const { isLock, lockFn: lockLogin } = useLockFn(handleLogin);
onMounted(() => {
const value = cache.get(ACCOUNT_KEY)
if (value?.remember) {
remAccount.value = value.remember
formData.account = value.account
}
})
const value = cache.get(ACCOUNT_KEY);
if (value?.remember) {
remAccount.value = value.remember;
formData.account = value.account;
}
});
</script>
<style lang="scss" scoped>
.login {
background-image: url('./images/login_bg.png');
@apply min-h-screen bg-no-repeat bg-center bg-cover;
.login-card {
height: 400px;
}
background-image: url("./images/login_bg.png");
@apply min-h-screen bg-no-repeat bg-center bg-cover;
.login-card {
height: 400px;
}
}
</style>

View File

@ -4,66 +4,31 @@
<el-page-header :content="$route.meta.title" @back="$router.back()" />
</el-card>
<el-card class="mt-4 !border-none" shadow="never">
<el-form
ref="formRef"
class="ls-form"
:model="formData"
label-width="85px"
:rules="rules"
>
<el-form ref="formRef" class="ls-form" :model="formData" label-width="85px" :rules="rules">
<div class="xl:flex">
<div>
<el-form-item label="文章标题" prop="title">
<div class="w-80">
<el-input
v-model="formData.title"
placeholder="请输入文章标题"
type="textarea"
:autosize="{ minRows: 3, maxRows: 3 }"
maxlength="64"
show-word-limit
clearable
/>
<el-input v-model="formData.title" placeholder="请输入文章标题" type="textarea"
:autosize="{ minRows: 3, maxRows: 3 }" maxlength="64" show-word-limit clearable />
</div>
</el-form-item>
<el-form-item label="文章栏目" prop="cid">
<el-select
class="w-80"
v-model="formData.cid"
placeholder="请选择文章栏目"
clearable
>
<el-option
v-for="item in optionsData.article_cate"
:key="item.id"
:label="item.name"
:value="item.id"
/>
<el-select class="w-80" v-model="formData.cid" placeholder="请选择文章栏目" clearable>
<el-option v-for="item in optionsData.article_cate" :key="item.id" :label="item.name"
:value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="文章简介" prop="desc">
<div class="w-80">
<el-input
v-model="formData.desc"
placeholder="请输入文章简介"
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
:maxlength="200"
show-word-limit
clearable
/>
<el-input v-model="formData.desc" placeholder="请输入文章简介" type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }" :maxlength="200" show-word-limit clearable />
</div>
</el-form-item>
<el-form-item label="摘要" prop="abstract">
<div class="w-80">
<el-input
type="textarea"
:autosize="{ minRows: 6, maxRows: 6 }"
v-model="formData.abstract"
maxlength="200"
show-word-limit
clearable
/>
<el-input type="textarea" :autosize="{ minRows: 6, maxRows: 6 }"
v-model="formData.abstract" maxlength="200" show-word-limit clearable />
</div>
</el-form-item>
<el-form-item label="文章封面" prop="image">

124
src/views/merchant/edit.vue Normal file
View File

@ -0,0 +1,124 @@
<style lang="scss">
.w-80 {
width: 500px;
}
.form-wrap {
display: flex;
flex-wrap: wrap;
justify-content: center;
flex-direction: column;
align-items: center;
}
</style>
<template>
<div class="article-edit">
<el-card class="!border-none" shadow="never">
<el-page-header :content="$route.meta.title" @back="$router.back()" />
</el-card>
<el-card class="mt-4 !border-none" shadow="never">
<el-form ref="formRef" class="ls-form" :model="formData" label-width="100px" :rules="rules">
<div class="form-wrap">
<el-form-item label="商户名称" prop="mer_name">
<el-input class="w-80" v-model="formData.mer_name" placeholder="请输入商户名称" clearable />
</el-form-item>
<el-form-item label="真实姓名" prop="real_name">
<el-input class="w-80" v-model="formData.real_name" placeholder="请输入真实姓名" />
</el-form-item>
<el-form-item label="总采购金额" prop="purchase_amount">
<el-input class="w-80" v-model="formData.purchase_amount" placeholder="请输入总采购金额" readonly />
</el-form-item>
<el-form-item label="总销售金额" prop="sale_amount">
<el-input class="w-80" v-model="formData.sale_amount" placeholder="请输入总销售金额" readonly />
</el-form-item>
<el-form-item label="手机号" prop="service_phone">
<el-input class="w-80" v-model="formData.service_phone" placeholder="请输入手机号" clearable />
</el-form-item>
<el-form-item label="评分" prop="product_score">
<el-input class="w-80" v-model="formData.product_score" placeholder="请输入评分" type="number"
maxlength="1" max="5" min="1" clearable />
</el-form-item>
<el-form-item label="店铺类型" prop="type_name">
<el-select class="w-80" 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-form-item>
<el-form-item label="地址" prop="mer_address">
<el-input class="w-80" v-model="formData.mer_address" placeholder="请输入地址" clearable />
</el-form-item>
</div>
</el-form>
</el-card>
<footer-btns>
<el-button type="primary" @click="handleSave">保存</el-button>
</footer-btns>
</div>
</template>
<script lang="ts" setup name="articleListsEdit">
import type { FormInstance } from 'element-plus'
import { merchantDetail, merchantUpdate, merchantCate } from "@/api/merchant";
import { articleDetail, articleEdit, articleAdd, articleCateAll } from '@/api/article'
const route = useRoute()
const router = useRouter()
const formData = ref({
mer_id: "",//id
mer_name: "",//
mer_avatar: "",//
real_name: "",//
mer_address: "",//
purchase_amount: "",//
sale_amount: "",//
service_phone: "",//
product_score: "",//
type_name: "",//
goods_num: "",//
});
const formRef = shallowRef<FormInstance>()
const rules = reactive({
mer_name: [{ required: true, message: '请输入商户名称', trigger: 'blur' }],
real_name: [{ required: true, message: '请输入商户姓名', trigger: 'blur' }],
mer_address: [{ required: true, message: '请输入商户地址', trigger: 'blur' }],
service_phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
product_score: [{ required: true, message: '请输入评分', trigger: 'blur' }],
type_name: [{ required: true, message: '请输入店铺类型', trigger: 'blur' }],
})
const getDetails = async () => {
const data = await merchantDetail({
mer_id: route.query.id
});
formData.value = data;
}
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
}
const handleSave = async () => {
await formRef.value?.validate()
if (route.query.id) {
await merchantUpdate(formData.value)
} else {
await articleAdd(formData.value)
}
router.back()
}
route.query.id && getDetails();
getMerchantCate();
</script>

View File

@ -0,0 +1,778 @@
<template>
<div>
<el-card class="!border-none" shadow="never">
<el-form
ref="formRef"
class="mb-[-16px] mt-[16px]"
:model="queryParams"
:inline="true"
>
<el-form-item label="商户名称">
<el-input
class="w-[280px]"
v-model="queryParams.mer_name"
placeholder="请输入商户名称"
clearable
@keyup.enter="resetPage"
/>
</el-form-item>
<el-form-item label="真实姓名">
<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
placeholder="请输入店铺类型"
v-model="queryParams.type_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>
<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 mt-4" shadow="never">
<div class="toolbar">
<el-button type="primary" @click="onBindBatch" size="small"> 批量绑定 </el-button>
</div>
<el-table
ref="tableRef"
size="large"
v-loading="pager.loading"
:data="pager.lists"
@selection-change="onSelectionChange"
>
<el-table-column type="selection" width="55" />
<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="purchase_amount" min-width="100" />
<el-table-column label="总销售金额" prop="sale_amount" min-width="100" />
<el-table-column label="手机号" prop="service_phone" min-width="120" />
<el-table-column label="评分" prop="product_score" min-width="100" />
<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="mer_address" min-width="220" />
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button
v-perms="['merchant.merchant/edit', 'merchant.merchant/add:edit']"
type="primary"
link
>
<router-link
:to="{
path: getRoutePath('merchant.merchant/add:edit'),
query: {
id: row.mer_id,
},
}"
>
编辑
</router-link>
</el-button>
<el-button
v-perms="['merchant.merchant/share']"
type="primary"
@click="onBind(row)"
link
size="small"
>
商户分配
</el-button>
<el-button type="primary" @click="onMonitor(row)" link size="small">
督导情况
</el-button>
</template>
</el-table-column>
</el-table>
<div class="flex justify-end mt-4">
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<!-- 商户绑定 -->
<Popup
ref="popupRef"
title="商户绑定"
:async="true"
width="550px"
@confirm="handleSubmit"
@close="onClose"
>
<el-form ref="formRef" class="ls-form" :model="bindForm" label-width="110px">
<el-form-item label="已选商户">
<el-select class="w-80" v-model="bindForm.merchant_ids" multiple disabled>
<el-option
v-for="item in pager.lists"
:key="item.mer_id"
:value="item.mer_id"
:label="item.mer_name"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="子管理员" prop="admin_id">
<el-select class="w-80" v-model="bindForm.admin_id" placeholder="请选择管理员">
<el-option
v-for="item in children"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
</el-form-item>
</el-form>
</Popup>
<!-- 督导情况 -->
<Popup
ref="monitorRef"
title="督导情况"
:async="true"
width="70%"
@confirm="onBindSubmit"
@close="onClose"
>
<el-form ref="formRef" :model="monitorForm" label-width="180px">
<el-row>
<el-col :span="12">
<el-form-item
label="督导师"
prop="supervisor"
:rules="{ required: true, message: '督导师不能为空', trigger: 'blur' }"
>
<el-input
v-model="monitorForm.supervisor"
placeholder="请输入督导师"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="成活率"
prop="supervisor_rate"
:rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }"
>
<el-input-number
v-model="monitorForm.supervisor_rate"
placeholder="请输入成活率"
controls-position="right"
:min="0.01"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="督导员"
prop="administrator"
:rules="{ required: true, message: '督导员不能为空', trigger: 'blur' }"
>
<el-input
v-model="monitorForm.administrator"
placeholder="请输入督导员"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="成活率"
prop="survival_rate"
:rules="{ required: true, message: '成活率不能为空', trigger: 'blur' }"
>
<el-input-number
v-model="monitorForm.survival_rate"
placeholder="请输入成活率"
controls-position="right"
:min="0.01"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="运营对接人"
prop="operate"
:rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }"
>
<el-input
v-model="monitorForm.operate"
placeholder="请输入运营对接人"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="成活率"
prop="operate_rate"
:rules="{ required: true, message: '运营对接人不能为空', trigger: 'blur' }"
>
<el-input-number
v-model="monitorForm.operate_rate"
placeholder="请输入成活率"
controls-position="right"
:min="0.01"
:max="100"
:step="1"
:precision="2"
></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
label="监督人商户总体情况"
prop="condition"
:rules="{
required: true,
message: '监督人商户总体情况不能为空',
trigger: 'blur',
}"
>
<el-input
v-model="monitorForm.condition"
placeholder="请输入监督人商户总体情况"
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商户总更新">
<el-input
v-model="monitorForm.saleGoodsNum"
placeholder="请输入商户总更新"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总销售金额">
<el-input
v-model="monitorForm.survival_rate"
placeholder="请输入总销售金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上周错售金额">
<el-input
v-model="monitorForm.lastWeekSale"
placeholder="请输入上周错售金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="本周错售金额">
<el-input
v-model="monitorForm.nowWeekSale"
placeholder="请输入本周错售金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="平台销售金额">
<el-input
v-model="monitorForm.saleMoney"
placeholder="请输入平台销售金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="平台销售占比">
<el-input
v-model="monitorForm.platformRate"
placeholder="请输入平台销售占比"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总采购金额">
<el-input
v-model="monitorForm.saleTarget"
placeholder="请输入总采购金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="上七日采购金额">
<el-input
v-model="monitorForm.lastProcurePrice"
placeholder="请输入lastProcurePrice"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="近七日采购金额">
<el-input
v-model="monitorForm.procurePrice"
placeholder="请输入近七日采购金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="近7日销售情况">
<el-table :data="monitorForm.salePaySeven">
<el-table-column label="时间" prop="time" align="center" />
<el-table-column label="金额" prop="money" align="center" />
</el-table>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="近七日采购情况">
<el-table :data="monitorForm.saleSeven">
<el-table-column label="时间" prop="time" align="center" />
<el-table-column label="金额" prop="money" align="center" />
</el-table>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="销售采购占比">
<el-input
v-model="monitorForm.procureRate"
placeholder="请输入近七日采购情况"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当日商户云仓采购">
<el-input
v-model="monitorForm.ycProcurement"
placeholder="请输入当日商户云仓采购"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="云仓采购总金额">
<el-input
v-model="monitorForm.allProcurement"
placeholder="请输入当日商户云仓采购"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="云仓采购占比">
<el-input
v-model="monitorForm.yunBuyRate"
placeholder="请输入当日商户云仓采购"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日云仓采购占比">
<el-input
v-model="monitorForm.yunRate"
placeholder="请输入当日商户云仓采购"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="30%云仓采购差额总金额">
<el-input
v-model="monitorForm.differenceTotal"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当天比百分之30的云仓比">
<el-input
v-model="monitorForm.differenceYunRate"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品总数">
<el-input
v-model="monitorForm.saleNum"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品分类">
<el-table :data="monitorForm.storeCates">
<el-table-column label="名称" prop="cate_name" align="center" />
<el-table-column label="数量" prop="cate_count" align="center" />
</el-table>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商户个人邀请总数">
<el-input
v-model="monitorForm.ownSpreadNum"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="可扩展量">
<el-input
v-model="monitorForm.merNum"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="总扩展数量">
<el-input
v-model="monitorForm.allSpreadNum"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="扩展成活占比">
<el-input
v-model="monitorForm.StoresaveRate"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商户扩展量">
<el-input
v-model="monitorForm.daySpread"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品销售金额">
<el-input
v-model="monitorForm.payPriceDay"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="店铺采购金额">
<el-input
v-model="monitorForm.procurePriceDay"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日云仓采购对比">
<el-input
v-model="monitorForm.yunBuyRateDay"
placeholder="请输入30%云仓采购差额总金额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="日云仓采购对比">
<el-input
v-model="monitorForm.goodsToday"
placeholder="商品更新"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="补贴总额">
<el-input
v-model="monitorForm.incomeAllSubsidies"
placeholder="补贴总额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="补贴余额">
<el-input
v-model="monitorForm.incomeBalanceSubsidies"
placeholder="补贴总额"
readonly
></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="当日补贴">
<el-input
v-model="monitorForm.incomeTodaySubsides"
placeholder="补贴总额"
readonly
></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</Popup>
</div>
</template>
<script lang="ts" setup name="merchantList">
import {
merchantList,
childManagement,
merchantBind,
merchantCate,
storeSuper,
merchantUpdate,
} from "@/api/merchant";
import { usePaging } from "@/hooks/usePaging";
import { getRoutePath } from "@/router";
import feedback from "@/utils/feedback";
import { ref } from "vue";
import Popup from "@/components/popup/index.vue";
const monitorRef: any = ref(null); //
const formRef: any = ref(null); //
const popupRef = shallowRef<InstanceType<typeof Popup>>(); //
const tableRef: any = ref(null); //
const emit = defineEmits(["success", "close"]);
//
const queryParams = reactive({
mer_name: "",
real_name: "",
mer_address: "",
purchase_amount: "",
sale_amount: "",
type_id: "",
product_score: "",
});
//
const bindForm: any = reactive({
id: "",
merchant_ids: [],
type: 1,
admin_id: "",
merchant_names: [],
});
//
const onBind = (e: any) => {
popupRef.value?.open();
bindForm.merchant_ids.push(e.mer_id);
};
//
const onBindBatch = async () => {
if (!selectData.value || selectData.value.length === 0)
return await feedback.notifyError("请至少选择一条记录!");
popupRef.value?.open();
selectData.value.forEach((item) => {
bindForm.merchant_ids.push(item);
});
};
//
const handleSubmit = async () => {
if (!bindForm.admin_id) return feedback.msgError("请选择子管理员!");
await merchantBind(bindForm);
popupRef.value?.close();
onClose();
};
// dialog
const onClose = () => {
bindForm.merchant_ids = [];
tableRef.value?.clearSelection();
monitorRef.value?.close();
popupRef.value?.close();
};
//
const selectData = ref<any[]>([]);
const onSelectionChange = (val: any[]) => {
selectData.value = val.map(({ mer_id }) => mer_id);
};
//
const onMonitor = async (row: any) => {
let detail = await storeSuper({ mer_id: row.mer_id });
monitorForm.value = detail;
monitorRef.value?.open();
};
//
const onBindSubmit = async () => {
await formRef.value?.validate();
await merchantUpdate(monitorForm.value);
monitorRef.value?.close();
emit("success");
};
//
const monitorForm: any = ref({
administrator: "", //
administrator_rate: "", //
supervisor: "", //
supervisor_rate: "", //
operate: "", //
operate_rate: "", //
condition: "", //
survival_rate: "", //
saleGoodsNum: "", //
totalSale: "", //
lastWeekSale: "", //
nowWeekSale: "", //
salePaySeven: "", //
saleMoney: "", //
platformRate: "", //
saleTarget: "", //
lastProcurePrice: "", //
procurePrice: "", //
saleSeven: "", //
procureRate: "", //
ycProcurement: "", //
allProcurement: "", //
yunBuyRate: "", //
yunRate: "", //
differenceTotal: "", //30%
differenceYunRate: "", //30
saleNum: "", //
storeCates: "", //
ownSpreadNum: "", //
merNum: "", //
allSpreadNum: "", //
StoresaveRate: "", //
daySpread: "", //
payPriceDay: "", //
procurePriceDay: "", //
yunBuyRateDay: "", //
goodsToday: "", //
incomeAllSubsidies: "", //
incomeBalanceSubsidies: "", //
incomeTodaySubsides: "", //
});
//
const { pager, getLists, resetPage, resetParams } = usePaging({
fetchFun: merchantList,
params: queryParams,
});
//
const merchantData: any = ref([]);
const getMerchantCate = async () => {
merchantData.value = await merchantCate("");
};
//
const children: any = ref([]);
const getChildManagement = async () => {
children.value = await childManagement("");
};
getLists();
//
getChildManagement();
//
getMerchantCate();
</script>
<style lang="scss">
.toolbar {
margin-bottom: 10px;
}
.el-row {
height: 70vh;
overflow: auto;
}
::-webkit-scrollbar {
width: 2px;
}
.el-input-number {
width: 100%;
}
</style>

View File

@ -6,19 +6,35 @@
<el-form-item label="主题" prop="theme">
<el-input v-model="formData.theme" clearable placeholder="请输入主题" />
</el-form-item>
<el-form-item label="商户名称" prop="merchant_id">
<el-select placeholder="请选择商户" v-model="formData.merchant_id" style="width:100%;">
<el-option v-for="(item, indx) in recordData" :key="item.id" :value="item.id"
:label="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="记录类型" prop="rid">
<el-input v-model="formData.rid" clearable placeholder="请输入记录类型" />
<el-select placeholder="请选择记录类型" v-model="formData.rid" style="width:100%;">
<el-option v-for="(item, indx) in recordData" :key="item.id" :value="item.id"
:label="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="记录人" prop="admin_id">
<el-input v-model="formData.admin_id" clearable placeholder="请输入记录人" />
<el-select placeholder="请选择记录人" v-model="formData.admin_id" style="width:100%;">
<el-option v-for="(item, indx) in recordInfo" :key="item.id" :value="item.id"
:label="item.name"></el-option>
</el-select>
</el-form-item>
<el-form-item label="跟进时间" prop="flow_time">
<el-date-picker v-model="formData.flow_time" type="date" placeholder="请选择跟进时间" style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" clearable placeholder="请输入备注" />
</el-form-item>
<el-form-item label="更进时间" prop="flow_time">
<el-input v-model="formData.flow_time" clearable placeholder="请输入更进时间" />
</el-form-item>
</el-form>
</popup>
</div>
@ -29,11 +45,16 @@ import type { FormInstance } from 'element-plus'
import Popup from '@/components/popup/index.vue'
import { apiRecordAdd, apiRecordEdit, apiRecordDetail } from '@/api/record'
import { timeFormat } from '@/utils/util'
import type { PropType } from 'vue'
import type { PropType } from 'vue';
import { defineProps } from 'vue'
defineProps({
dictData: {
type: Object as PropType<Record<string, any[]>>,
default: () => ({})
recordData: {
type: Array as PropType<any[]>,
default: () => []
},
recordInfo: {
type: Array as PropType<any[]>,
default: () => []
}
})
const emit = defineEmits(['success', 'close'])
@ -41,7 +62,6 @@ const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
//
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑跟踪记录表' : '新增跟踪记录表'
@ -55,10 +75,9 @@ const formData = reactive({
admin_id: '',
remark: '',
flow_time: '',
merchant_id: ''
})
//
const formRules = reactive<any>({
theme: [{
@ -68,17 +87,21 @@ const formRules = reactive<any>({
}],
rid: [{
required: true,
message: '请输入记录类型',
trigger: ['blur']
message: '请选择商户记录类型',
trigger: ['change']
}],
admin_id: [{
required: true,
message: '请输入记录人',
trigger: ['blur']
message: '请选择商户记录人',
trigger: ['change']
}],
merchant_id: [{
required: true,
message: '请选择商户',
trigger: ['change']
}],
})
//
const setFormData = async (data: Record<any, any>) => {
for (const key in formData) {
@ -87,10 +110,9 @@ const setFormData = async (data: Record<any, any>) => {
formData[key] = data[key]
}
}
}
const getDetail = async (row: Record<string, any>) => {
const data = await apiRecordDetail({
id: row.id
@ -129,3 +151,9 @@ defineExpose({
getDetail
})
</script>
<style lang="scss">
.el-input__wrapper {
width: 100%;
}
</style>

View File

@ -6,21 +6,25 @@
<el-input class="w-[280px]" v-model="queryParams.theme" clearable placeholder="请输入主题" />
</el-form-item>
<el-form-item label="记录类型" prop="rid">
<el-input class="w-[280px]" v-model="queryParams.rid" clearable placeholder="请输入记录类型" />
<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-input class="w-[280px]" v-model="queryParams.admin_id" clearable placeholder="请输入记录人" />
<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 label="商户" prop="merchant_id">
<el-input class="w-[280px]" v-model="queryParams.merchant_id" clearable placeholder="请选择商户" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input class="w-[280px]" v-model="queryParams.remark" clearable placeholder="请输入备注" />
</el-form-item>
<el-form-item label="更进时间" prop="flow_time">
<el-input class="w-[280px]" v-model="queryParams.flow_time" clearable placeholder="请输入更进时间" />
<el-form-item label="跟进时间" prop="flow_time">
<el-date-picker class="w-[280px]" v-model="queryParams.flow_time" type="date" placeholder="请选择跟进时间">
</el-date-picker>
</el-form-item>
<el-form-item>
@ -44,8 +48,8 @@
<el-table :data="pager.lists" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="主题" prop="theme" show-overflow-tooltip />
<el-table-column label="记录类型" prop="rid" show-overflow-tooltip />
<el-table-column label="记录人" prop="admin_id" 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="merchant_name" show-overflow-tooltip />
<el-table-column label="备注" prop="remark" show-overflow-tooltip />
<el-table-column label="更进时间" prop="flow_time" show-overflow-tooltip />
@ -67,22 +71,23 @@
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
<edit-popup v-if="showEdit" ref="editRef" :dict-data="dictData" @success="getLists" @close="showEdit = false" />
<edit-popup v-if="showEdit" ref="editRef" :record-info="recordInfoList" :record-data="recordList"
@success="getLists" @close="showEdit = false" />
</div>
</template>
<script lang="ts" setup name="recordLists">
import { usePaging } from '@/hooks/usePaging'
import { useDictData } from '@/hooks/useDictOptions'
import { apiRecordLists, apiRecordDelete } from '@/api/record'
import { apiRecordLists, apiRecordDelete, recordAll, recordInfo } from '@/api/record'
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 showEdit = ref(false);
//
const queryParams = reactive({
@ -102,8 +107,17 @@ const handleSelectionChange = (val: any[]) => {
selectData.value = val.map(({ id }) => id)
}
//
const { dictData } = useDictData('')
//
const recordList: any = ref([]);
recordAll("").then(res => {
recordList.value = res;
})
//
const recordInfoList: any = ref([]);
recordInfo("").then(res => {
recordInfoList.value = res;
})
//
const { pager, getLists, resetParams, resetPage } = usePaging({

View File

@ -43,6 +43,5 @@
</template>
<script lang="ts" setup>
const onConfirm = (value: string) => {
console.log(value)
}
</script>

View File

@ -20,11 +20,7 @@
<a :href="workbenchData.version.channel.website" target="_blank">
<el-button type="success" size="small">官网</el-button>
</a>
<a
class="ml-3"
:href="workbenchData.version.channel.gitee"
target="_blank"
>
<a class="ml-3" :href="workbenchData.version.channel.gitee" target="_blank">
<el-button type="danger" size="small">Gitee</el-button>
</a>
</div>
@ -43,32 +39,32 @@
<div class="flex flex-wrap">
<div class="w-1/2 md:w-1/4">
<div class="leading-10">销售额</div>
<div class="text-6xl">{{ workbenchData.today.today_sales }}</div>
<div class="text-tx-secondary text-xs">
<div class="leading-10">本周订单总数</div>
<div class="text-6xl">{{ merchantInfoData.order.nowWeekOrders }}</div>
<!-- <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_sales }}
</div>
</div> -->
</div>
<div class="w-1/2 md:w-1/4">
<div class="leading-10">成交订单</div>
<div class="text-6xl">{{ workbenchData.today.order_num }}</div>
<div class="text-tx-secondary text-xs">
<div class="leading-10">上周订单总数</div>
<div class="text-6xl">{{ merchantInfoData.order.lastWeekOrders }}</div>
<!-- <div class="text-tx-secondary text-xs">
{{ workbenchData.today.order_sum }}
</div>
</div> -->
</div>
<div class="w-1/2 md:w-1/4">
<div class="leading-10">新增用户</div>
<div class="text-6xl">{{ workbenchData.today.today_new_user }}</div>
<div class="text-tx-secondary text-xs">
<div class="leading-10">本周销售总额()</div>
<div class="text-6xl">{{ merchantInfoData.amount.nowWeekAmount }}</div>
<!-- <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_new_user }}
</div>
</div> -->
</div>
<div class="w-1/2 md:w-1/4">
<div class="leading-10">新增访问量</div>
<div class="text-6xl">{{ workbenchData.today.today_visitor }}</div>
<div class="text-tx-secondary text-xs">
<div class="leading-10">上周销售总额()</div>
<div class="text-6xl">{{ merchantInfoData.amount.lastWeekAmount }}</div>
<!-- <div class="text-tx-secondary text-xs">
{{ workbenchData.today.total_visitor }}
</div>
</div> -->
</div>
</div>
</el-card>
@ -79,11 +75,8 @@
<span>常用功能</span>
</template>
<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"
:key="item"
>
<div 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">
<image-contain width="40px" height="40px" :src="item?.image" />
<div class="mt-2">{{ item.name }}</div>
@ -95,14 +88,22 @@
<div class="md:flex">
<el-card class="flex-1 !border-none md:mr-4 mb-4" shadow="never">
<template #header>
<span>访问量趋势图</span>
<span>本周商品销售数量</span>
</template>
<div>
<v-charts
style="height: 350px"
:option="workbenchData.visitorOption"
:autoresize="true"
/>
<v-charts style="height: 350px" :option="merchantInfoData.echartOptionNowWeek" :autoresize="true" />
</div>
</el-card>
</div>
<div class="md:flex">
<el-card class="flex-1 !border-none md:mr-4 mb-4" shadow="never">
<template #header>
<span>上周周商品销售数量</span>
</template>
<div>
<v-charts style="height: 350px" :option="merchantInfoData.echartOptionLastWeek"
:autoresize="true" />
</div>
</el-card>
</div>
@ -110,8 +111,112 @@
</template>
<script lang="ts" setup name="workbench">
import { getWorkbench } from '@/api/app'
import vCharts from 'vue-echarts'
import { getWorkbench, merchantInfo } from '@/api/app'
import { orderBy } from 'lodash-es';
import vCharts from 'vue-echarts';
//
const merchantInfoData = ref({
//
order: {
nowWeekOrders: 0,
lastWeekOrders: 0
},
//
amount: {
nowWeekAmount: 0,
lastWeekAmount: 0
},
// echarts
echartOptionNowWeek: {
tooltip: { show: true },
xAxis: {
type: 'category',
data: [],
axisLabel: {
show: true,
rotate: 45
}
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'bar',
barMaxWidth: 30,
label: {
show: true,
position: 'top'
}
}
]
},
// echarts
echartOptionLastWeek: {
tooltip: { show: true },
xAxis: {
type: 'category',
data: [],
axisLabel: {
show: true,
rotate: 45
}
},
yAxis: {
type: 'value'
},
series: [
{
data: [],
type: 'bar',
barMaxWidth: 30,
label: {
show: true,
position: 'top'
}
}
]
}
});
//
const getMerchangeInfo = () => {
merchantInfo().then(res => {
//
merchantInfoData.value.order.nowWeekOrders = res.nowWeekOrderNum;
merchantInfoData.value.order.lastWeekOrders = res.lastWeekOrderNum;
//
merchantInfoData.value.amount.nowWeekAmount = res.nowWeekMoney;
merchantInfoData.value.amount.lastWeekAmount = res.lastWeekMoney;
//
let nameArr: any = [], dataArr: any = [];
res.nowWeekGoods.map((item: any) => {
nameArr.push(item.store_name ? item.store_name : '-');
dataArr.push(item.product_count);
});
merchantInfoData.value.echartOptionNowWeek.xAxis.data = nameArr;
merchantInfoData.value.echartOptionNowWeek.series[0].data = dataArr;
//
let nameArr1: any = [], dataArr1: any = [];
res.lastWeekGoods.map((item: any) => {
nameArr1.push(item.store_name || '-');
dataArr1.push(item.product_count);
});
merchantInfoData.value.echartOptionLastWeek.xAxis.data = nameArr1;
merchantInfoData.value.echartOptionLastWeek.series[0].data = dataArr1;
})
}
//
const workbenchData: any = reactive({
version: {
@ -186,7 +291,8 @@ const getData = () => {
}
onMounted(() => {
getData()
getData();
getMerchangeInfo();
})
</script>