Initial commit
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="880"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑企业' : '添加企业'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 7 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 24 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="企业名称" v-bind="validateInfos.customerName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="30"
|
||||
placeholder="请输入企业名称"
|
||||
v-model:value="form.customerName"
|
||||
@blur="
|
||||
validate('customerName', { trigger: 'blur' }).catch(() => {})
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="企业类型" v-bind="validateInfos.customerType">
|
||||
<DictSelect
|
||||
dict-code="CustomerType"
|
||||
placeholder="请选择企业类型"
|
||||
v-model:value="form.customerType"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="统一信用代码" v-bind="validateInfos.customerCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请输入社会统一信用代码"
|
||||
v-model:value="form.customerCode"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="企业负责人" v-bind="validateInfos.customerContacts">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请填写联系人"
|
||||
v-model:value="form.customerContacts"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="手机号码" v-bind="validateInfos.customerMobile">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请填写联系人手机号码"
|
||||
v-model:value="form.customerMobile"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="企业logo" v-bind="validateInfos.customerAvatar">
|
||||
<ele-image-upload
|
||||
v-model:value="images"
|
||||
:item-style="{ width: '90px', height: '90px' }"
|
||||
:limit="1"
|
||||
@upload="onUpload"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="12" :sm="24" :xs="24">
|
||||
<a-form-item label="企业全称" v-bind="validateInfos.customerFullName">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="30"
|
||||
placeholder="请输入企业全称"
|
||||
v-model:value="form.customerFullName"
|
||||
@blur="
|
||||
validate('customerFullName', { trigger: 'blur' }).catch(() => {})
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="公司座机" v-bind="validateInfos.customerPhone">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="请填写公司座机电话"
|
||||
v-model:value="form.customerPhone"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="选择位置" name="customerAddress">
|
||||
<a-input-group compact>
|
||||
<a-input
|
||||
disabled
|
||||
style="width: calc(100% - 32px)"
|
||||
v-model:value="form.customerAddress"
|
||||
placeholder="所属区域"
|
||||
@search="onSearch"
|
||||
/>
|
||||
<a-tooltip title="选择位置">
|
||||
<a-button @click="openMapPicker">
|
||||
<template #icon><EnvironmentOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="所属区域" name="region">
|
||||
<regions-select
|
||||
v-model:value="city"
|
||||
valueField="label"
|
||||
placeholder="请选择省市区"
|
||||
class="ele-fluid"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="详细地址"
|
||||
v-bind="validateInfos.customerAddress"
|
||||
>
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请填写联系地址"
|
||||
v-model:value="form.customerAddress"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" v-bind="validateInfos.sortNumber">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
placeholder="排序"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注" v-bind="validateInfos.comments">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- 地图位置选择弹窗 -->
|
||||
<ele-map-picker
|
||||
:need-city="true"
|
||||
:dark-mode="darkMode"
|
||||
v-model:visible="showMap"
|
||||
:center="[108.374959, 22.767024]"
|
||||
:search-type="1"
|
||||
:zoom="12"
|
||||
@done="onDone"
|
||||
/>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch, computed} from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
import { addCustomer, updateCustomer } from '@/api/oa/customer';
|
||||
import type { Customer } from '@/api/oa/customer/model';
|
||||
import { createCode } from '@/utils/common';
|
||||
import { uploadFile } from '@/api/system/file';
|
||||
import type { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FILE_SERVER } from '@/config/setting';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import DictSelect from "@/views/search/components/dict-select.vue";
|
||||
import {
|
||||
EnvironmentOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useThemeStore } from "@/store/modules/theme";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { CenterPoint } from "ele-admin-pro/es/ele-map-picker/types";
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Customer | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 省市区
|
||||
const city = ref<string[]>([]);
|
||||
// 是否显示地图选择弹窗
|
||||
const showMap = ref(false);
|
||||
const themeStore = useThemeStore();
|
||||
const { darkMode } = storeToRefs(themeStore);
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<Customer>({
|
||||
customerCode: '',
|
||||
customerName: '',
|
||||
customerFullName: '',
|
||||
customerType: undefined,
|
||||
progress: undefined,
|
||||
customerMobile: '',
|
||||
customerAvatar: '',
|
||||
customerPhone: '',
|
||||
customerSource: '',
|
||||
customerContacts: '',
|
||||
customerAddress: '',
|
||||
customerProvince: '',
|
||||
customerCity: '',
|
||||
customerRegion: '',
|
||||
comments: '',
|
||||
status: '0',
|
||||
sortNumber: 100,
|
||||
customerId: 0,
|
||||
userId: '',
|
||||
});
|
||||
|
||||
// 已上传数据, 可赋初始值用于回显
|
||||
const images = ref(<any>[]);
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
customerName: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入企业名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
customerType: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请选择企业类型',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
|
||||
});
|
||||
|
||||
const { resetFields, validate, validateInfos } = useForm(form, rules);
|
||||
|
||||
/* 打开位置选择 */
|
||||
const openMapPicker = () => {
|
||||
showMap.value = true;
|
||||
};
|
||||
|
||||
/* 地图选择后回调 */
|
||||
const onDone = (location: CenterPoint) => {
|
||||
console.log(location);
|
||||
city.value = [
|
||||
`${location.city?.province}`,
|
||||
`${location.city?.city}`,
|
||||
`${location.city?.district}`
|
||||
];
|
||||
form.customerProvince = `${location.city?.province}`;
|
||||
form.customerCity = `${location.city?.city}`;
|
||||
form.customerRegion = `${location.city?.district}`;
|
||||
form.customerAddress = `${location.address}`;
|
||||
form.latitude = `${location.lat}`;
|
||||
form.longitude = `${location.lng}`;
|
||||
showMap.value = false;
|
||||
};
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
// 去除空格
|
||||
form.customerName = form.customerName?.replace(/\s*/g, '');
|
||||
const data = {
|
||||
...form
|
||||
};
|
||||
// 转字符串
|
||||
const saveOrUpdate = isUpdate.value ? updateCustomer : addCustomer;
|
||||
saveOrUpdate(data)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 上传文件
|
||||
const onUpload = (d: ItemType) => {
|
||||
uploadFile(<File>d.file)
|
||||
.then((result) => {
|
||||
form.customerAvatar = result.path;
|
||||
message.success('上传成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
loading.value = false;
|
||||
// 头像赋值
|
||||
images.value = [];
|
||||
if(props.data.customerAvatar){
|
||||
images.value.push({ uid:1, url: FILE_SERVER + props.data.customerAvatar, status: '' });
|
||||
}
|
||||
assignObject(form, props.data);
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
@@ -0,0 +1,148 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
width="75%"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="'企业详情'"
|
||||
:maxable="true"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
:footer="null"
|
||||
>
|
||||
<a-form
|
||||
:label-col="{ md: { span: 4 }, sm: { span: 24 } }"
|
||||
:wrapper-col="{ md: { span: 19 }, sm: { span: 24 } }"
|
||||
>
|
||||
<div class="base-form" style="margin-bottom: 20px">
|
||||
<a-descriptions bordered>
|
||||
<a-descriptions-item label="企业名称">
|
||||
{{ customer.customerName }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="社会统一信用代码">
|
||||
{{ customer.customerCode }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="跟进状态">
|
||||
<div color="blue" v-for="(d, index) in progress" :key="index">
|
||||
<span v-if="d.value == customer.progress">{{ d.label }}</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="联系人">
|
||||
{{ customer.customerContacts }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="联系电话">
|
||||
{{ customer.customerMobile }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="座机电话">
|
||||
{{ customer.customerPhone }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="企业类型">
|
||||
<div color="blue" v-for="(d, index) in customerType" :key="index">
|
||||
<span v-if="d.value == customer.customerType">{{ d.value }}</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="联系地址">
|
||||
{{ customer.customerAddress }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="备注">
|
||||
{{ customer.comments }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<!-- <a-descriptions-->
|
||||
<!-- title="其他信息"-->
|
||||
<!-- :column="1"-->
|
||||
<!-- bordered-->
|
||||
<!-- style="margin-top: 30px"-->
|
||||
<!-- >-->
|
||||
<!-- <a-descriptions-item label="相关项目">-->
|
||||
<!-- {{ customer.comments }}-->
|
||||
<!-- </a-descriptions-item>-->
|
||||
<!-- </a-descriptions>-->
|
||||
</div>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form } from 'ant-design-vue';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
import type { Customer } from '@/api/oa/customer/model';
|
||||
import { FILE_SERVER } from '@/config/setting';
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Customer | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 用户信息
|
||||
const customer = reactive<Customer>({
|
||||
customerCode: '',
|
||||
customerName: '',
|
||||
customerType: undefined,
|
||||
customerMobile: '',
|
||||
customerAvatar: '',
|
||||
customerPhone: '',
|
||||
customerContacts: '',
|
||||
customerAddress: '',
|
||||
comments: '',
|
||||
progress: '',
|
||||
status: '0',
|
||||
sortNumber: 100,
|
||||
customerId: 0
|
||||
});
|
||||
|
||||
// 请求状态
|
||||
const loading = ref(true);
|
||||
|
||||
const { resetFields } = useForm(customer);
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
/* 打开外部链接 */
|
||||
// const openUrl = (record) => {
|
||||
// window.open(record.panel);
|
||||
// };
|
||||
|
||||
/* 获取字典数据 */
|
||||
const customerType = getDictionaryOptions('customerType');
|
||||
const progress = getDictionaryOptions('customerFollowStatus');
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
loading.value = false;
|
||||
assignObject(customer, props.data);
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.tab-pane {
|
||||
min-height: 100px;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,223 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加企业</span>
|
||||
</a-button>
|
||||
<a-upload
|
||||
:before-upload="importBatch"
|
||||
:show-upload-list="false"
|
||||
accept=".xls,.xlsx"
|
||||
>
|
||||
<a-button class="ele-btn-icon"> <DownloadOutlined />批量导入 </a-button>
|
||||
</a-upload>
|
||||
<a-button class="ele-btn-icon">
|
||||
<span>下载企业模板</span>
|
||||
</a-button>
|
||||
<a-upload
|
||||
:before-upload="importBatch"
|
||||
:show-upload-list="false"
|
||||
accept=".xls,.xlsx"
|
||||
>
|
||||
<a-button class="ele-btn-icon"> <UploadOutlined />导出 </a-button>
|
||||
</a-upload>
|
||||
<a-button
|
||||
danger
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
v-if="selection.length > 0"
|
||||
@click="removeBatch"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
<span>批量删除</span>
|
||||
</a-button>
|
||||
<!-- <a-button @click="batchMove" v-if="selection.length > 0">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <UserSwitchOutlined />-->
|
||||
<!-- </template>-->
|
||||
<!-- 批量转移-->
|
||||
<!-- </a-button>-->
|
||||
<a-input-search
|
||||
allow-clear
|
||||
placeholder="请输入关键词"
|
||||
v-model:value="searchText"
|
||||
@pressEnter="search"
|
||||
@search="search"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
UploadOutlined,
|
||||
DownloadOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import type { CustomerParam } from '@/api/oa/customer/model';
|
||||
import { ref, watch } from 'vue';
|
||||
import { utils, read } from 'xlsx';
|
||||
// import { assignObject } from 'ele-admin-pro';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: CustomerParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<CustomerParam>({
|
||||
customerName: '',
|
||||
customerCode: '',
|
||||
nickname: '',
|
||||
keywords: '',
|
||||
userId: undefined
|
||||
});
|
||||
// 下来选项
|
||||
// 搜索内容
|
||||
const searchText = ref('');
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
where.keywords = searchText.value;
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
// 导入数据的列
|
||||
const importTitle = ref<string[]>(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
|
||||
// 导入的数据
|
||||
const importData = ref<Record<string, any>[]>([]);
|
||||
// 导入数据二维数组形式
|
||||
const importDataAoa = ref<(string | number)[][]>([]);
|
||||
|
||||
/* 导入本地 excel 文件 */
|
||||
const importBatch = (file: File) => {
|
||||
if (
|
||||
![
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
].includes(file.type)
|
||||
) {
|
||||
message.error('只能选择 excel 文件');
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
message.error('大小不能超过 20MB');
|
||||
return false;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const data = new Uint8Array(e.target?.result as any);
|
||||
const workbook = read(data, { type: 'array' });
|
||||
const sheetNames = workbook.SheetNames;
|
||||
const worksheet = workbook.Sheets[sheetNames[0]];
|
||||
// 解析成二维数组
|
||||
const aoa = utils.sheet_to_json<string[]>(worksheet, { header: 1 });
|
||||
// 生成表格需要的数据
|
||||
let list: Record<string, any>[] = [];
|
||||
let maxCols = 0;
|
||||
let title: string[] = [];
|
||||
aoa.forEach((d) => {
|
||||
if (d.length > maxCols) {
|
||||
maxCols = d.length;
|
||||
}
|
||||
const row = {};
|
||||
for (let i = 0; i < d.length; i++) {
|
||||
const key = getCharByIndex(i);
|
||||
row[key] = d[i];
|
||||
row['__colspan__' + key] = 1;
|
||||
row['__rowspan__' + key] = 1;
|
||||
}
|
||||
list.push(row);
|
||||
});
|
||||
for (let i = 0; i < maxCols; i++) {
|
||||
title.push(getCharByIndex(i));
|
||||
}
|
||||
importTitle.value = title;
|
||||
importData.value = list;
|
||||
importDataAoa.value = aoa;
|
||||
};
|
||||
console.log(importData.value);
|
||||
console.log(importDataAoa.value);
|
||||
reader.readAsArrayBuffer(file);
|
||||
return false;
|
||||
};
|
||||
/* 生成Excel列字母序号 */
|
||||
const getCharByIndex = (index: number) => {
|
||||
const chars = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z'
|
||||
];
|
||||
if (index < chars.length) {
|
||||
return chars[index];
|
||||
}
|
||||
const n = parseInt(String(index / chars.length));
|
||||
const m = index % chars.length;
|
||||
return chars[n] + chars[m];
|
||||
};
|
||||
|
||||
// 转移
|
||||
// const batchMove = () => {
|
||||
// emit('batchMove');
|
||||
// };
|
||||
|
||||
// 批量删除
|
||||
const removeBatch = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const onClear = () => {
|
||||
where.userId = undefined;
|
||||
search();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="customerId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
v-model:selection="selection"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
:scroll="{ x: 800 }"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'customerName'">
|
||||
<a-avatar
|
||||
:size="30"
|
||||
:src="`${FILE_THUMBNAIL + record.customerAvatar}`"
|
||||
style="margin-right: 4px"
|
||||
:srcset="`${FILE_THUMBNAIL + record.customerAvatar}`"
|
||||
>
|
||||
<template #icon>
|
||||
<UserOutlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
<a-tooltip title="查看详情">
|
||||
<a href="#" @click="openEdit(record)">{{
|
||||
record.customerName
|
||||
}}</a>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="column.key === 'customerType'">
|
||||
<div v-for="(d, i) in JSON.parse(customerType)" :key="i">
|
||||
<span v-if="d.value === record.customerType">{{
|
||||
d.value
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">正常</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">待审核</a-tag>
|
||||
<a-tag v-if="record.status === 2" color="purple">已驳回</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'nickname'">
|
||||
<a-tooltip :title="`${record.nickname}`">
|
||||
<a-avatar :src="record.userAvatar" size="small" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="column.key === 'createTime'">
|
||||
<a-tooltip :title="`${toDateString(record.createTime)}`">
|
||||
{{ timeAgo(record.createTime) }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<CustomerEdit v-model:visible="showEdit" :data="current" @done="reload" />
|
||||
<!-- 企业详情弹窗 -->
|
||||
<CustomerInfo v-model:visible="showInfo" :data="current" @done="reload" />
|
||||
<!-- 批量转移弹窗 -->
|
||||
<!-- <CustomerMove v-model:visible="showMove" :data="selection" @done="batchMove" />-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import {
|
||||
ExclamationCircleOutlined,
|
||||
UserOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import { toDateString } from 'ele-admin-pro';
|
||||
import Search from './components/search.vue';
|
||||
import CustomerEdit from './components/customer-edit.vue';
|
||||
import CustomerInfo from './components/customer-info.vue';
|
||||
import {
|
||||
pageCustomer,
|
||||
removeCustomer,
|
||||
removeBatchCustomer
|
||||
} from '@/api/oa/customer';
|
||||
import { timeAgo } from 'ele-admin-pro';
|
||||
import type { Customer, CustomerParam } from '@/api/oa/customer/model';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { FILE_THUMBNAIL } from '@/config/setting';
|
||||
|
||||
const userStore = useUserStore();
|
||||
// 当前用户信息
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
const customerType = localStorage.getItem('customerType');
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<Customer[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<Customer | null>(null);
|
||||
|
||||
// 是否显示资产详情
|
||||
const showInfo = ref(false);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.progress = filters.progress;
|
||||
where.customerSource = filters.customerSource;
|
||||
where.customerType = filters.customerType;
|
||||
where.status = filters.status;
|
||||
}
|
||||
return pageCustomer({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
key: 'index',
|
||||
width: 48,
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
hideInSetting: true,
|
||||
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
|
||||
},
|
||||
{
|
||||
title: '企业名称',
|
||||
dataIndex: 'customerName',
|
||||
key: 'customerName'
|
||||
},
|
||||
{
|
||||
title: '企业类型',
|
||||
dataIndex: 'customerType',
|
||||
key: 'customerType'
|
||||
},
|
||||
{
|
||||
title: '企业负责人',
|
||||
dataIndex: 'customerContacts'
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'customerPhone'
|
||||
},
|
||||
{
|
||||
title: '是否含税',
|
||||
dataIndex: 'customerMobile'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: CustomerParam) => {
|
||||
console.log(where);
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: Customer) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
/* 打开用户详情弹窗 */
|
||||
const openInfo = (row?: Customer) => {
|
||||
current.value = row ?? null;
|
||||
showInfo.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: Customer) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeCustomer(row.customerId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量转移 */
|
||||
const batchMove = (userId) => {
|
||||
console.log(userId, '批量转移0000');
|
||||
console.log(selection.value);
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchCustomer(
|
||||
selection.value.map((d) => {
|
||||
if (loginUser.value.userId === d.userId) {
|
||||
return d.customerId;
|
||||
}
|
||||
})
|
||||
)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: Customer) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Accessory'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sys-org-table :deep(.ant-table-body) {
|
||||
overflow: auto !important;
|
||||
overflow: overlay !important;
|
||||
}
|
||||
|
||||
.sys-org-table :deep(.ant-table-pagination.ant-pagination) {
|
||||
padding: 0 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user