qhw-dev-1010
qiuhongwu 1 year ago
parent 1e104717cb
commit 52df6c6b76

@ -0,0 +1,687 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import { LoadingOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import type { SelectProps, UploadChangeParam, UploadProps } from 'ant-design-vue'
import type { Rule } from 'ant-design-vue/es/form'
//
interface FormState {
username: string
region: string | undefined
select: boolean
}
const formState = reactive<FormState>({
username: '',
region: undefined,
select: false,
})
const rules: Record<string, Rule[]> = {
username: [
{ required: true, message: 'Please input Activity name', trigger: 'change' },
{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
],
region: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
select: [{ required: true, message: '请选择人员', trigger: 'change' }],
}
function onFinish(values: any) {
console.log('Success:', values)
}
function onFinishFailed(errorInfo: any) {
console.log('Failed:', errorInfo)
}
//
function affiliationChange(value: string) {
console.log(`selected ${value}`)
}
const options = ref<SelectProps['options']>([
{
label: '总裁办',
options: [
{
value: 'jack',
label: 'Jack',
},
{
value: 'lucy',
label: 'Lucy',
},
],
},
{
label: '人事部',
options: [
{
value: 'yiminghe',
label: 'Yiminghe',
},
],
},
])
//
const shouldFixToBottom = ref(false) //
//
onMounted(() => {
window.addEventListener('scroll', handleScroll)
})
//
onBeforeUnmount(() => {
window.removeEventListener('scroll', handleScroll)
})
function handleScroll() {
//
if (window.scrollY > 100)
shouldFixToBottom.value = true
else
shouldFixToBottom.value = false
}
//
const value1 = ref<string>('a')
//
function getBase64(img: Blob, callback: (base64Url: string) => void) {
const reader = new FileReader()
reader.addEventListener('load', () => callback(reader.result as string))
reader.readAsDataURL(img)
}
const fileList = ref([])
const loading = ref<boolean>(false)
const imageUrl = ref<string>('')
function handleChange(info: UploadChangeParam) {
if (info.file.status === 'uploading') {
loading.value = true
return
}
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, (base64Url: string) => {
imageUrl.value = base64Url
loading.value = false
})
}
if (info.file.status === 'error') {
loading.value = false
message.error('upload error')
}
}
function beforeUpload(file: UploadProps['fileList'][number]) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
if (!isJpgOrPng)
message.error('You can only upload JPG file!')
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M)
message.error('Image must smaller than 2MB!')
return isJpgOrPng && isLt2M
}
//antd
const RabbitStyle = [{
height:'60px',
}]
</script>
<template>
<a-card
style="width: 1688px;
height: 4062px;
margin: 20px auto ;
overflow:auto; "
>
<div class="title">
新增客户
</div>
<div class="item">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 2 }"
:wrapper-col="{ span: 5 }"
autocomplete="off"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<!-- 客户分类 -->
<div class="classification">
<div class="moduletop">
<p>
<span></span>
客户分类
</p>
</div>
<div class="item">
<p class="hint">
不同的客户分类可实现不同的业务流程商品类型一旦保存不可修改请选择更匹配你需求的一个
</p>
<div class="selection">
<a-radio-group v-model:value="value1" :style="RabbitStyle" button-style="solid">
<a-radio-button :style="RabbitStyle" value="a">
<div class="ification">
<p>一级客户</p>
<span>钢厂/回收机构</span>
</div>
</a-radio-button>
<a-radio-button :style="RabbitStyle" value="d">
<div class="ification">
<p>二级客户</p>
<span>其他客户</span>
</div>
</a-radio-button>
</a-radio-group>
</div>
</div>
</div>
<!-- 基础信息 -->
<div class="information">
<div class="moduletop">
<p>
<span></span>
基础信息
</p>
</div>
<div class="item">
<div class="LOGO subdivide">
<P>公司LOGO:</P>
<div class="logo">
<a-upload
v-model:file-list="fileList"
name="avatar"
list-type="picture-card"
class="avatar-uploader"
:show-upload-list="false"
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
:before-upload="beforeUpload"
@change="handleChange"
>
<img v-if="imageUrl" :src="imageUrl" alt="avatar">
<div v-else>
<LoadingOutlined v-if="loading" />
<PlusOutlined v-else />
<div class="ant-upload-text">
Upload
</div>
</div>
</a-upload>
</div>
</div>
<div class="subdivide">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 13 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="客户名称"
name="username"
:rules="[{ required: true, message: '输入内容' }]"
>
<a-input v-model:value="formState.username" placeholder="输入内容" />
</a-form-item>
<a-form-item
label="行业分类"
name="region"
>
<a-select v-model:value="formState.region" placeholder="行业分类">
<a-select-option value="shanghai">
家用电器
</a-select-option>
<a-select-option value="beijing">
交通运输
</a-select-option>
<a-select-option value="shanghai">
商务服务
</a-select-option>
<a-select-option value="beijing">
家居用品
</a-select-option>
<a-select-option value="shanghai">
电工电气
</a-select-option>
<a-select-option value="beijing">
数码产品
</a-select-option>
<a-select-option value="shanghai">
通信产品
</a-select-option>
<a-select-option value="beijing">
办公文教
</a-select-option>
<a-select-option value="shanghai">
运动休闲
</a-select-option>
<a-select-option value="beijing">
传媒广电
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="业务进度"
name="region"
>
<a-select v-model:value="formState.region" placeholder="业务进度">
<a-select-option value="shanghai">
了解跟进
</a-select-option>
<a-select-option value="beijing">
资料收集
</a-select-option>
<a-select-option value="shanghai">
客户评审
</a-select-option>
<a-select-option value="beijing">
风控核准
</a-select-option>
<a-select-option value="shanghai">
合作洽谈
</a-select-option>
<a-select-option value="beijing">
准备签约
</a-select-option>
<a-select-option value="shanghai">
已经签约
</a-select-option>
<a-select-option value="beijing">
合作叫停
</a-select-option>
<a-select-option value="shanghai">
暂时搁置
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="客户星级"
name="region"
>
<a-select v-model:value="formState.region" placeholder="业务进度">
<a-select-option value="shanghai">
一星客户
</a-select-option>
<a-select-option value="beijing">
二星客户
</a-select-option>
<a-select-option value="shanghai">
三星客户
</a-select-option>
<a-select-option value="beijing">
四星客户
</a-select-option>
<a-select-option value="shanghai">
五星客户
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<div class="subdivide">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 13 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="企业性质"
name="region"
>
<a-select v-model:value="formState.region" placeholder="企业性质">
<a-select-option value="shanghai">
国有企业
</a-select-option>
<a-select-option value="beijing">
集体企业
</a-select-option>
<a-select-option value="shanghai">
私营企业
</a-select-option>
<a-select-option value="beijing">
股份制企业
</a-select-option>
<a-select-option value="shanghai">
外资企业
</a-select-option>
<a-select-option value="beijing">
合资企业
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="客户来源"
name="region"
>
<a-select v-model:value="formState.region" placeholder="客户来源">
<a-select-option value="shanghai">
电话营销
</a-select-option>
<a-select-option value="beijing">
主动来电
</a-select-option>
<a-select-option value="shanghai">
客户介绍
</a-select-option>
<a-select-option value="beijing">
朋友介绍
</a-select-option>
<a-select-option value="shanghai">
独立开发
</a-select-option>
<a-select-option value="beijing">
网络搜索
</a-select-option>
<a-select-option value="shanghai">
广告杂志
</a-select-option>
<a-select-option value="beijing">
展会促销
</a-select-option>
<a-select-option value="shanghai">
其他途径
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="归属人员"
name="select"
>
<a-select v-model:value="value" style="width: 200px" placeholder="选择人员" @change="affiliationChange">
<a-select-opt-group>
<template #label>
<span>
<UserOutlined />
总裁办
</span>
</template>
<a-select-option value="jack">
张总
</a-select-option>
<a-select-option value="lucy">
李总
</a-select-option>
<a-select-option value="lucy">
王总
</a-select-option>
</a-select-opt-group>
<a-select-opt-group label="Engineer">
<a-select-option value="Yiminghe">
yiminghe
</a-select-option>
<a-select-option value="Yiminghe1">
yiminghe1
</a-select-option>
</a-select-opt-group>
</a-select>
</a-form-item>
</a-form>
</div>
</div>
</div>
<!-- 详细信息 -->
<div class="detailinformation">
<div class="moduletop">
<p>
<span></span>
详细信息
</p>
</div>
<div class="item">
<div class="itemtop">
<div class="detailinitemtop">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 13 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="国家"
name="region"
>
<a-select v-model:value="formState.region" placeholder="中国">
<a-select-option value="shanghai">
中国
</a-select-option>
<a-select-option v-for="(index, nation) in 5" :key="index" value="{{ nation.value }}">
国家列表
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="公司电话"
name="username"
:rules="[{ required: true, message: '输入内容' }]"
>
<a-input v-model:value="formState.username" placeholder="输入内容" />
</a-form-item>
</a-form>
</div>
<div class="detailinitemtop">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 13 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="所在省/市"
name="region"
>
<a-select v-model:value="formState.region" placeholder="选择城市">
<a-option-group v-for="(group, letter) in groupedCities" :key="letter" :label="letter">
<a-option v-for="city in group" :key="city.id" :value="city.id">
{{ city.name }}
</a-option>
</a-option-group>
</a-select>
</a-form-item>
<a-form-item
label="公司传真"
name="username"
:rules="[{ required: true, message: '输入内容' }]"
>
<a-input v-model:value="formState.username" placeholder="输入内容" />
</a-form-item>
</a-form>
</div>
<div class="detailinitemtop">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 13 }"
autocomplete="off"
:rules="rules"
@finish="onFinish"
@finishFailed="onFinishFailed"
>
<a-form-item
label="详细地址"
name="username"
:rules="[{ required: true, message: '输入内容' }]"
>
<a-input v-model:value="formState.username" placeholder="输入内容" />
</a-form-item>
<a-form-item
label="公司网址"
name="username"
:rules="[{ required: true, message: '输入内容' }]"
>
<a-input v-model:value="formState.username" placeholder="输入内容" />
</a-form-item>
</a-form>
</div>
</div>
<div class="itembottom">
<p>公司介绍</p>
<a-textarea placeholder="输入内容" />
</div>
</div>
</div>
<!-- 代表人信息 -->
<div class="information">
<div class="moduletop">
<p>
<span></span>
代表人信息
</p>
</div>
</div>
</a-form>
</div>
</a-card>
</template>
<style scoped lang="less">
.title{
font-size: 22px;
font-weight: normal;
font-weight: 700;
line-height: 34px;
color: #262626;
}
.item{
//
.moduletop{
padding-bottom: 16px;
margin: 40px 15px ;
border-bottom: 1px solid #E5E5E5;
p{
width: 107px;
height: 24px;
margin-bottom: 15px;
font-family: "AlibabaPuHuiTi_3_55_Regular-55 Regular";
font-size:16px;
line-height: 24px;
color: #262626;
background: linear-gradient(90deg, #9EF9FF 0%, #FFF 100%);
span{
font-size:14px;
color:#409EFF;
}
}
}
.item{
padding: 0 20px;
margin: 20px;
}
//
.classification{
width: 100%;
.hint{
font-family: "AlibabaPuHuiTi_3_55_Regular-55 Regular";
font-size: 14px;
font-weight: normal;
line-height: 22px;
color: #8C8C8C;
}
.selection{
.ification{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 10px;
line-height: 10px;
}
}
}
//
.information{
width: 100%;
.item{
display: flex;
justify-content: flex-start;
.subdivide{
width: 30%;
.logo{
margin-left: 20%;
}
}
}
}
//
.detailinformation{
width: 100%;
.item{
.itemtop{
display: flex;
justify-content: flex-start;
.detailinitemtop{
width: 30%;
}
}
.itembottom{
width: 1250px;
margin-left: 2%;
}
}
}
}
:where(.css-dev-only-do-not-override-176pxz6).ant-input {
height: 38px;
}
:where(.css-dev-only-do-not-override-176pxz6).ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
height: 38px;
}
</style>

@ -1,22 +1,17 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="isUpdate ? t('action.edit') : t('action.create')" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, unref } from 'vue' import { ref, unref } from 'vue'
import { createFormSchema, updateFormSchema } from './customer.data'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form' import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal' import { BasicModal, useModalInner } from '@/components/Modal'
import { createFormSchema, updateFormSchema } from './customer.data'
import { createCustomer, getCustomer, updateCustomer } from '@/api/xxjj/customer' import { createCustomer, getCustomer, updateCustomer } from '@/api/xxjj/customer'
defineOptions({ name: 'CustomerModal' }) defineOptions({ name: 'CustomerModal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n() const { t } = useI18n()
const { createMessage } = useMessage() const { createMessage } = useMessage()
const emit = defineEmits(['success', 'register'])
const isUpdate = ref(true) const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({ const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({
@ -24,7 +19,7 @@ const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = u
baseColProps: { span: 24 }, baseColProps: { span: 24 },
schemas: createFormSchema, schemas: createFormSchema,
showActionButtonGroup: false, showActionButtonGroup: false,
actionColOptions: { span: 23 } actionColOptions: { span: 23 },
}) })
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
@ -42,16 +37,23 @@ async function handleSubmit() {
try { try {
const values = await validate() const values = await validate()
setModalProps({ confirmLoading: true }) setModalProps({ confirmLoading: true })
if (unref(isUpdate)) { if (unref(isUpdate))
await updateCustomer(values) await updateCustomer(values)
} else { else
await createCustomer(values) await createCustomer(values)
}
closeModal() closeModal()
emit('success') emit('success')
createMessage.success(t('common.saveSuccessText')) createMessage.success(t('common.saveSuccessText'))
} finally { }
finally {
setModalProps({ confirmLoading: false }) setModalProps({ confirmLoading: false })
} }
} }
</script> </script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

@ -1,47 +1,12 @@
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" v-auth="['xxjj:customer:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button type="warning" v-auth="['xxjj:customer:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'xxjj:customer:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
color: 'error',
label: t('action.delete'),
auth: 'xxjj:customer:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record)
}
}
]"
/>
</template>
</template>
</BasicTable>
<CustomerModal @register="registerModal" @success="reload()" />
</div>
</template>
<script lang="ts" setup> <script lang="ts" setup>
import CustomerModal from './CustomerModal.vue'
import { columns, searchFormSchema } from './customer.data'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal' import { useModal } from '@/components/Modal'
import CustomerModal from './CustomerModal.vue'
import { IconEnum } from '@/enums/appEnum' import { IconEnum } from '@/enums/appEnum'
import { BasicTable, useTable, TableAction } from '@/components/Table' import { BasicTable, TableAction, useTable } from '@/components/Table'
import { deleteCustomer, exportCustomer, getCustomerPage } from '@/api/xxjj/customer' import { deleteCustomer, exportCustomer, getCustomerPage } from '@/api/xxjj/customer'
import { columns, searchFormSchema } from './customer.data'
defineOptions({ name: 'Customer' }) defineOptions({ name: 'Customer' })
@ -60,8 +25,8 @@ const [registerTable, { getForm, reload }] = useTable({
width: 140, width: 140,
title: t('common.action'), title: t('common.action'),
dataIndex: 'action', dataIndex: 'action',
fixed: 'right' fixed: 'right',
} },
}) })
function handleCreate() { function handleCreate() {
@ -80,7 +45,7 @@ async function handleExport() {
async onOk() { async onOk() {
await exportCustomer(getForm().getFieldsValue()) await exportCustomer(getForm().getFieldsValue())
createMessage.success(t('common.exportSuccessText')) createMessage.success(t('common.exportSuccessText'))
} },
}) })
} }
@ -90,3 +55,39 @@ async function handleDelete(record: Recordable) {
reload() reload()
} }
</script> </script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button v-auth="['xxjj:customer:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['xxjj:customer:export']" type="warning" :pre-icon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: 'xxjj:customer:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
color: 'error',
label: t('action.delete'),
auth: 'xxjj:customer:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<CustomerModal @register="registerModal" @success="reload()" />
</div>
</template>

Loading…
Cancel
Save