Merge remote-tracking branch 'origin/main' into dev-cl

This commit is contained in:
2024-06-20 12:27:25 +08:00
32 changed files with 2191 additions and 217 deletions

35
src/api/clue/afterSale.js Normal file
View File

@@ -0,0 +1,35 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getAfterSaleList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/list', params })
}
// 查询(精简)列表
export const getAfterSalePage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/page', params })
}
// 新增
export const createAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/create', data })
}
// 审核
export const auditAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/check', data })
}
export const batchAuditAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/batch/check', data })
}
// 撤销
export const cancelApplyAfterSale = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-after-sale/revoke', params: data })
}
// 查询详情
export const getAfterSaleDetail = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-after-sale/get', params })
}

View File

@@ -49,3 +49,8 @@ export const getOpearateRecord = async (params) => {
export const getPublicClue = async (data) => { export const getPublicClue = async (data) => {
return await request.put({ url: '/admin-api/crm/sch-clue/public/save', data }) return await request.put({ url: '/admin-api/crm/sch-clue/public/save', data })
} }
// 获得线索跟进用户信息
export const getFollowUserList = async (params) => {
return await request.get({ url: '/admin-api/crm/sch-clue/get-follow-user', params })
}

35
src/api/clue/payment.js Normal file
View File

@@ -0,0 +1,35 @@
import request from '@/config/axios'
// 查询(精简)列表
export const getPaymentList = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/list', params })
}
// 查询(精简)列表
export const getPaymentPage = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/page', params })
}
// 新增
export const createPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/create', data })
}
// 审核
export const auditPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/check', data })
}
export const batchAuditPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/batch/check', data })
}
// 撤销
export const cancelApplyPayment = async (data) => {
return await request.post({ url: '/admin-api/crm/sign-pay-record/revoke', params: data })
}
// 查询详情
export const getPaymentDetail = async (params) => {
return await request.get({ url: '/admin-api/crm/sign-pay-record/get', params })
}

View File

@@ -14,3 +14,8 @@ export const getSign = async (id) => {
export const createSign = async (data) => { export const createSign = async (data) => {
return await request.post({ url: '/admin-api/crm/sign/create', data: data }) return await request.post({ url: '/admin-api/crm/sign/create', data: data })
} }
// 取消登记
export const cancelDeal = async (id) => {
return await request.delete({ url: '/admin-api/crm/sign/delete?id=' + id })
}

View File

@@ -7,5 +7,10 @@ export const getConfigByConfigKey = (params) => {
// 保存配置项 // 保存配置项
export const updateConfig = (data) => { export const updateConfig = (data) => {
return request.put({ url: '/admin-api/crm/config/update', data }) return request.put({ url: '/admin-api/crm/config/batchUpdateConfigValue', data })
}
// 根据模块获取配置列表
export const getConfigList = (params) => {
return request.get({ url: '/admin-api/crm/config/query', params })
} }

View File

@@ -27,7 +27,8 @@ const props = defineProps({
data: { data: {
type: Object as PropType<any>, type: Object as PropType<any>,
default: () => ({}) default: () => ({})
} },
defaultShow: propTypes.bool.def(true)
}) })
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
@@ -57,7 +58,7 @@ const getBindItemValue = (item: DescriptionsSchema) => {
} }
// 折叠 // 折叠
const show = ref(true) const show = ref(props.defaultShow)
const toggleClick = () => { const toggleClick = () => {
if (props.collapse) { if (props.collapse) {

View File

@@ -51,7 +51,8 @@ export default defineComponent({
// 表单label宽度 // 表单label宽度
labelWidth: propTypes.oneOfType([String, Number]).def('auto'), labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
// 是否 loading 数据中 add by 芋艿 // 是否 loading 数据中 add by 芋艿
vLoading: propTypes.bool.def(false) vLoading: propTypes.bool.def(false),
inlineBlock: propTypes.bool.def(false)
}, },
emits: ['register'], emits: ['register'],
setup(props, { slots, expose, emit }) { setup(props, { slots, expose, emit }) {
@@ -289,6 +290,7 @@ export default defineComponent({
model={props.isCustom ? props.model : formModel} model={props.isCustom ? props.model : formModel}
class={prefixCls} class={prefixCls}
v-loading={props.vLoading} v-loading={props.vLoading}
style={props.inlineBlock ? 'display: inline' : ''}
> >
{{ {{
// 如果需要自定义,就什么都不渲染,而是提供默认插槽 // 如果需要自定义,就什么都不渲染,而是提供默认插槽

View File

@@ -38,6 +38,7 @@ const props = defineProps({
// 伸缩的界限字段 // 伸缩的界限字段
expandField: propTypes.string.def(''), expandField: propTypes.string.def(''),
inline: propTypes.bool.def(true), inline: propTypes.bool.def(true),
inlineBlock: propTypes.bool.def(false),
model: { model: {
type: Object as PropType<Recordable>, type: Object as PropType<Recordable>,
default: () => ({}) default: () => ({})
@@ -154,6 +155,7 @@ initSearch()
<!-- update by 芋艿class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离避免空隙过大 --> <!-- update by 芋艿class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离避免空隙过大 -->
<Form <Form
:inline="inline" :inline="inline"
:inlineBlock="inlineBlock"
:is-col="isCol" :is-col="isCol"
:is-custom="false" :is-custom="false"
isSearch isSearch

View File

@@ -1,18 +1,39 @@
<template> <template>
<el-form :model="form" ref="formRef" label-width="auto"> <el-form :model="form" ref="formRef" label-width="auto">
<el-form-item> <el-form-item v-if="getConfig('usePhoneConfig')">
<template #label> <template #label>
<Tooltip message="请联系客服开通或关闭,客服电话:15955599959" />使用外呼 <Tooltip
v-if="getConfig('usePhoneConfig').remark"
:message="getConfig('usePhoneConfig').remark"
/>
<span>使用外呼</span>
</template> </template>
<el-radio-group v-model="form.callEnable" disabled> <el-radio-group v-model="form.usePhoneConfig">
<el-radio :label="1"> 使用 </el-radio> <el-radio
<el-radio :label="0"> 不使用 </el-radio> v-for="(item, index) in getConfig('usePhoneConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="跟进信息"> <el-form-item v-if="getConfig('showFollowConfig')">
<el-radio-group v-model="form.followType"> <template #label>
<el-radio :label="1"> 展示所有 </el-radio> <Tooltip
<el-radio :label="2"> 仅展示本人 </el-radio> v-if="getConfig('showFollowConfig').remark"
:message="getConfig('showFollowConfig').remark"
/>
<span>跟进信息</span>
</template>
<el-radio-group v-model="form.showFollowConfig">
<el-radio
v-for="(item, index) in getConfig('showFollowConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -23,23 +44,42 @@
</template> </template>
<script setup name="BasicSettingClue"> <script setup name="BasicSettingClue">
import * as ConfigApi from '@/api/system/set'
const message = useMessage() const message = useMessage()
const form = ref({ const form = ref({})
followType: 1,
callEnable: 1 const configList = ref([])
})
function getConfig(val) {
return configList.value.find((it) => it.configKey == val)
}
function getData() { function getData() {
form.value = { ConfigApi.getConfigList({ module: 100 }).then((data) => {
followType: 1, configList.value = data
callEnable: 1 // 获取所有配置项
} data.map((it) => {
form.value[it.configKey] = it.configValue
})
})
} }
function onSubmit() { function onSubmit() {
message.success('保存成功') const params = configList.value.map((it) => ({
configId: it.configId,
configKey: it.configKey,
configValue: form.value[it.configKey]
}))
ConfigApi.updateConfig(params).then(() => {
message.success('保存成功')
})
} }
onMounted(() => {
getData()
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -1,16 +1,31 @@
<template> <template>
<el-form :model="form" ref="formRef" label-width="auto"> <el-form :model="form" ref="formRef" label-width="auto">
<el-form-item label="是否关联成交率"> <el-form-item v-if="getConfig('saleCommissionRelateDealConfig')">
<el-radio-group v-model="form.withSuccess"> <template #label>
<el-radio :label="1"> </el-radio> <Tooltip
<el-radio :label="0"> </el-radio> v-if="getConfig('saleCommissionRelateDealConfig').remark"
:message="getConfig('saleCommissionRelateDealConfig').remark"
/>
<span>是否关联成交率</span>
</template>
<el-radio-group
v-model="form.saleCommissionRelateDealConfig"
@change="form.saleCommissionRelateRulesConfig = []"
>
<el-radio
v-for="(item, index) in getConfig('saleCommissionRelateDealConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="关联规则" v-if="form.withSuccess"> <el-form-item label="关联规则" v-if="form.saleCommissionRelateDealConfig == 'true'">
<div> <div>
<el-button @click="form.rules.push({})">新增规则</el-button> <el-button @click="form.saleCommissionRelateRulesConfig.push({})">新增规则</el-button>
<div <div
v-for="(item, index) in form.rules" v-for="(item, index) in form.saleCommissionRelateRulesConfig"
:key="index" :key="index"
class="mt-10px flex justify-center items-center" class="mt-10px flex justify-center items-center"
> >
@@ -48,22 +63,52 @@
</template> </template>
<script setup name="BasicSettingClue"> <script setup name="BasicSettingClue">
import * as ConfigApi from '@/api/system/set'
const message = useMessage() const message = useMessage()
const form = ref({ const form = ref({})
withSuccess: 1, const configList = ref([])
rules: []
}) const jsonArr = ['saleCommissionRelateRulesConfig']
function getConfig(val) {
return configList.value.find((it) => it.configKey == val)
}
function getData() { function getData() {
form.value = { ConfigApi.getConfigList({ module: 101 }).then((data) => {
withSuccess: 1 configList.value = data
} // 获取所有配置项
data.map((it) => {
form.value[it.configKey] = it.configValue
if (it.configKey == 'saleCommissionRelateRulesConfig') {
if (!form.value[it.configKey]) {
form.value[it.configKey] = []
} else {
form.value[it.configKey] = JSON.parse(it.configValue)
}
}
})
})
} }
function onSubmit() { function onSubmit() {
message.success('保存成功') const params = configList.value.map((it) => ({
configId: it.configId,
configKey: it.configKey,
configValue: jsonArr.includes(it.configKey)
? JSON.stringify(form.value[it.configKey])
: form.value[it.configKey]
}))
ConfigApi.updateConfig(params).then(() => {
message.success('保存成功')
})
} }
onMounted(() => {
getData()
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -0,0 +1,236 @@
<template>
<div>
<el-form :model="searchForm" label-width="0" inline>
<el-form-item>
<el-input v-model="searchForm.signId" placeholder="成交单号" clearable />
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.name" placeholder="线索名称" clearable />
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.state" placeholder="审核状态" clearable>
<el-option label="待审核" :value="1" />
<el-option label="已撤销" :value="2" />
<el-option label="已通过" :value="3" />
<el-option label="已驳回" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.signUser" placeholder="登记人" clearable filterable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.dealDate"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="登记日期"
end-placeholder="登记日期"
/>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.applyUser" placeholder="申请人" clearable filterable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.applyTime"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="申请日期"
end-placeholder="申请日期"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button @click="batchAudit">批量审核</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="tableList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" :selectable="(row) => row.state == 1" />
<el-table-column prop="signId" label="成交单号" min-width="150px" />
<el-table-column prop="name" label="线索名称" min-width="200px" />
<el-table-column prop="phone" label="联系方式" min-width="150px" />
<el-table-column prop="reason" label="售后原因" min-width="150px" />
<el-table-column prop="refundAmount" label="退款金额" min-width="90px" />
<el-table-column prop="isReturns" label="是否退货" min-width="90px" />
<el-table-column prop="solution" label="解决方案" min-width="150px" />
<el-table-column prop="signUserName" label="登记人" min-width="90px" />
<el-table-column prop="dealDate" label="登记时间" min-width="150px" />
<el-table-column prop="applyUserName" label="申请人" min-width="90px" />
<el-table-column prop="applyTime" label="申请时间" min-width="150px" />
<el-table-column prop="stateName" label="审核状态" fixed="right" min-width="90px" />
<el-table-column label="操作" width="150px" fixed="right">
<template #default="{ row }">
<el-button type="primary" style="padding: 0" text @click="handleDetail(row.id)">
详情
</el-button>
<el-button
type="primary"
style="padding: 0"
text
v-if="row.state == 1 && currentUserId == row.applyUser"
v-hasPermi="['clue:order:after-sale']"
@click="handleCancel(row.id)"
>
撤销
</el-button>
<el-button
type="primary"
style="padding: 0"
text
v-if="row.state == 1"
v-hasPermi="['clue:order:after-sale-audit']"
@click="handleAudit(row)"
>
审核
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
v-model:limit="searchForm.pageSize"
v-model:page="searchForm.pageNo"
:total="total"
@pagination="getList"
/>
<DialogAfterSaleAudit ref="afterSaleAuditDialog" @success="getList" />
<DialogAfterSaleDetail ref="afterSaleDetailDialog" />
<DialogBatchAudit ref="batchAuditDialog" @success="getList" />
</div>
</template>
<script setup name="AfterSales">
import * as AfterSaleApi from '@/api/clue/afterSale'
import { getSimpleUserList as getUserOption } from '@/api/system/user'
import { useUserStore } from '@/store/modules/user'
import DialogAfterSaleAudit from './DialogAfterSaleAudit.vue'
import DialogAfterSaleDetail from './DialogAfterSaleDetail.vue'
import DialogBatchAudit from './DialogBatchAudit.vue'
const afterSaleAuditDialog = ref()
const userStore = useUserStore()
const message = useMessage() // 消息弹窗
const currentUserId = userStore.getUser.id
const searchForm = ref({
signId: undefined,
name: undefined,
dealDate: [],
state: undefined,
dealUser: undefined,
createDate: [],
createUser: undefined,
pageNo: 1,
pageSize: 20
})
const userOptions = ref([])
const tableList = ref([])
const total = ref(0)
function handleSearch() {
searchForm.value.pageNo = 1
getList()
}
function handleReset() {
searchForm.value = {
signId: undefined,
name: undefined,
state: undefined,
dealDate: [],
dealUser: undefined,
createDate: [],
createUser: undefined,
pageNo: 1,
pageSize: 20
}
}
const loading = ref(false)
async function getList() {
loading.value = true
try {
const data = await AfterSaleApi.getAfterSalePage(searchForm.value)
tableList.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const batchIds = ref([])
function handleSelectionChange(val) {
batchIds.value = val.map((it) => it.id)
}
const batchAuditDialog = ref()
function batchAudit() {
if (batchIds.value.length) {
batchAuditDialog.value.open('aftersale', batchIds.value)
} else {
message.info('请选择表格中需要审核的数据')
}
}
const afterSaleDetailDialog = ref()
function handleDetail(id) {
afterSaleDetailDialog.value.open(id)
}
async function handleCancel(id) {
try {
// 删除的二次确认
await message.confirm('是否确认撤销申请?')
// 发起删除
await AfterSaleApi.cancelApplyAfterSale({ id })
message.success('撤销成功!')
// 刷新列表
await getList()
} catch (err) {
console.log(err)
}
}
function handleAudit(row) {
afterSaleAuditDialog.value.open(row)
}
function getOptions() {
getUserOption().then((data) => {
userOptions.value = data
})
}
onMounted(() => {
getOptions()
handleSearch()
})
</script>
<style lang="scss" scoped></style>

View File

View File

@@ -0,0 +1,122 @@
<template>
<Dialog title="售后记录" v-model="show" width="800px">
<el-table :data="aftersaleList" border stripe>
<el-table-column type="index" width="50" />
<el-table-column prop="reason" label="售后原因" />
<el-table-column prop="refundAmount" label="退款金额" />
<el-table-column prop="isReturns" label="是否退货" />
<el-table-column prop="applyTime" label="申请日期" width="180px" />
<el-table-column prop="stateName" label="审核状态" />
</el-table>
<el-divider direction="horizontal" />
<el-button v-show="showAdd" class="mb-10px" type="primary" @click="handleAdd">
新增售后
</el-button>
<el-form v-if="!showAdd" :model="form" ref="formRef" :rules="rules" label-width="80px">
<el-form-item label="售后原因" prop="reason">
<el-input v-model="form.reason" placeholder="请输入售后原因" />
</el-form-item>
<el-form-item label="退款金额" prop="refundAmount">
<el-input-number
v-model="form.refundAmount"
:min="0"
:controls="false"
placeholder="请输入金额"
/>
</el-form-item>
<el-form-item label="是否退货" prop="isReturns">
<el-radio-group v-model="form.isReturns">
<el-radio :label="true"> 退货 </el-radio>
<el-radio :label="false"> 不退货 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="解决方案" prop="solution">
<el-input
type="textarea"
:autoSize="{ minRows: 3 }"
v-model="form.solution"
placeholder="请输入退款原因"
/>
</el-form-item>
<el-form-item label="备注">
<Editor v-model:modelValue="form.remark" />
</el-form-item>
<el-form-item>
<el-button :disabled="formLoading" type="primary" @click="onSubmit">保存</el-button>
<el-button @click="showAdd = true">取消</el-button>
</el-form-item>
</el-form>
</Dialog>
</template>
<script setup name="DialogAfterSale">
import { getAfterSalePage, createAfterSale } from '@/api/clue/afterSale'
const message = useMessage() // 消息弹窗
const show = ref(false)
const aftersaleList = ref([])
const orderId = ref('')
function open(signId) {
show.value = true
orderId.value = signId
try {
getList()
} catch (error) {
console.log(error)
}
}
const showAdd = ref(true)
const form = ref({})
const rules = {
refundAmount: { required: true, message: '回款金额不可为空', trigger: 'blur' },
reason: { required: true, message: '售后原因不可为空', trigger: 'blur' },
solution: { required: true, message: '解决方案不可为空', trigger: 'blur' }
}
function getList() {
getAfterSalePage({ signId: orderId.value, pageNo: 1, pageSize: 100 }).then((data) => {
aftersaleList.value = data.list
})
}
function handleAdd() {
showAdd.value = false
form.value = {
signId: orderId.value,
reason: undefined,
solution: undefined,
refundAmount: undefined,
isReturns: false,
remark: undefined
}
}
const formRef = ref()
const formLoading = ref(false)
async function onSubmit() {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await createAfterSale(form.value)
message.success('申请成功!')
getList()
showAdd.value = true
} finally {
formLoading.value = false
}
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,135 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
<Descriptions :data="orderInfo" :schema="schema" :columns="2" labelWidth="130px" />
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<el-form-item label="状态" prop="state">
<el-radio-group v-model="formData.state">
<el-radio :label="3"> 通过 </el-radio>
<el-radio :label="4"> 驳回 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<Editor v-model:modelValue="formData.remark" />
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script name="DialogAfterSaleAudit" setup>
import { auditAfterSale } from '@/api/clue/afterSale'
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const schema = [
{
field: 'name',
label: '线索名称',
span: 1
},
{
field: 'phone',
label: '联系方式',
span: 1
},
{
field: 'signUserName',
label: '登记人',
span: 1
},
{
field: 'dealDate',
label: '登记时间',
span: 1
},
{
field: 'reason',
label: '售后原因',
span: 1
},
{
field: 'solution',
label: '解决方案',
span: 1
},
{
field: 'refundAmount',
label: '退款金额',
span: 1
},
{
field: 'isReturns',
label: '是否退货',
span: 1
},
{
field: 'applyUserName',
label: '申请人',
span: 1
},
{
field: 'applyTime',
label: '申请时间',
span: 1
},
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formData = ref({
state: 3,
remark: ''
})
const formRef = ref() // 表单 Ref
const orderInfo = ref({})
/** 打开弹窗 */
const open = async (row) => {
dialogVisible.value = true
dialogTitle.value = '售后审核'
resetForm(row.id)
// 修改时,设置数据
orderInfo.value = { ...row }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await auditAfterSale(formData.value)
message.success('审核完成!')
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = (signId) => {
formData.value = {
saleId: signId,
state: 3,
remark: ''
}
formRef.value?.resetFields()
}
</script>

View File

@@ -0,0 +1,137 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="900px">
<Descriptions
title="申请详情"
:data="orderInfo"
:schema="applySchema"
:columns="2"
labelWidth="130px"
/>
<Descriptions
title="审核详情"
:data="orderInfo"
:schema="orderInfo.state == 2 ? cancelSchema : auditSchema"
:columns="orderInfo.state == 2 ? 2 : 3"
labelWidth="100px"
/>
</Dialog>
</template>
<script name="DialogAfterSaleDetail" setup>
import { getAfterSaleDetail } from '@/api/clue/afterSale'
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const applySchema = [
{
field: 'name',
label: '线索名称',
span: 1
},
{
field: 'phone',
label: '联系方式',
span: 1
},
{
field: 'signUserName',
label: '登记人',
span: 1
},
{
field: 'dealDate',
label: '登记时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'reason',
label: '售后原因',
span: 1
},
{
field: 'refundAmount',
label: '退款金额',
span: 1
},
{
field: 'isReturns',
label: '是否退货',
span: 1
},
{
field: 'solution',
label: '解决方案',
span: 1
},
{
field: 'applyUserName',
label: '申请人',
span: 1
},
{
field: 'applyTime',
label: '申请时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
const auditSchema = [
{
field: 'stateName',
label: '审核状态',
span: 1
},
{
field: 'checkUser',
label: '审核人',
span: 1
},
{
field: 'checkTime',
label: '审核时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'checkRemark',
label: '备注',
span: 3,
isEditor: true
}
]
const cancelSchema = [
{
field: 'stateName',
label: '审核状态',
span: 1
},
{
field: 'revokeTime',
label: '撤销时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
}
]
const orderInfo = ref({})
/** 打开弹窗 */
const open = async (id) => {
dialogVisible.value = true
dialogTitle.value = '售后申请详情'
try {
orderInfo.value = await getAfterSaleDetail({ id })
} catch (error) {
console.log(error)
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
</script>

View File

@@ -0,0 +1,87 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<el-form-item label="状态" prop="state">
<el-radio-group v-model="formData.state">
<el-radio :label="3"> 通过 </el-radio>
<el-radio :label="4"> 驳回 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<Editor v-model:modelValue="formData.remark" />
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script name="DialogAfterSaleAudit" setup>
import { batchAuditAfterSale } from '@/api/clue/afterSale'
import { batchAuditPayment } from '@/api/clue/payment'
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formData = ref({
state: 3,
remark: ''
})
const formRef = ref() // 表单 Ref
const formType = ref('aftersale')
const titleMap = {
aftersale: '批量售后审核',
feeback: '批量回款审核'
}
/** 打开弹窗 */
const open = (type, ids) => {
dialogVisible.value = true
formType.value = type
dialogTitle.value = titleMap[type] || '批量审核'
resetForm(ids)
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
if (formType.value == 'aftersale') {
await batchAuditAfterSale(formData.value)
message.success('审核完成!')
} else if (formType.value == 'feeback') {
await batchAuditPayment(formData.value)
message.success('审核完成!')
} else {
return
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = (ids) => {
formData.value = {
payIds: ids,
saleIds: ids,
state: 3,
remark: ''
}
formRef.value?.resetFields()
}
</script>

View File

@@ -0,0 +1,101 @@
<template>
<Dialog title="回款记录" v-model="show" width="800px">
<el-table :data="feebackList" border stripe>
<el-table-column type="index" width="50" />
<el-table-column prop="money" label="回款金额" />
<el-table-column prop="applyTime" label="申请日期" />
<el-table-column prop="isPayoff" label="是否结清" />
<el-table-column prop="stateName" label="审核状态" />
</el-table>
<el-divider direction="horizontal" />
<el-button v-show="showAdd" class="mb-10px" type="primary" @click="handleAdd">
新增回款
</el-button>
<el-form v-if="!showAdd" :model="form" ref="formRef" :rules="rules" label-width="80px">
<el-form-item label="回款金额" prop="money">
<el-input-number v-model="form.money" :min="0" :controls="false" placeholder="请输入金额" />
</el-form-item>
<el-form-item label="是否结清" prop="isPayoff">
<el-radio-group v-model="form.isPayoff">
<el-radio :label="true"> 结清 </el-radio>
<el-radio :label="false"> 未结清 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注">
<Editor v-model:modelValue="form.remark" />
</el-form-item>
<el-form-item>
<el-button :disabled="formLoading" type="primary" @click="onSubmit">保存</el-button>
<el-button @click="showAdd = true">取消</el-button>
</el-form-item>
</el-form>
</Dialog>
</template>
<script setup name="DialogFeeback">
import { getPaymentPage, createPayment } from '@/api/clue/payment'
const message = useMessage() // 消息弹窗
const show = ref(false)
const feebackList = ref([])
const orderId = ref('')
function open(signId) {
show.value = true
orderId.value = signId
try {
getList()
} catch (error) {
console.log(error)
}
}
const showAdd = ref(true)
const form = ref({})
const rules = {
money: { required: true, message: '回款金额不可为空', trigger: 'blur' }
}
function getList() {
getPaymentPage({ signId: orderId.value, pageNo: 1, pageSize: 100 }).then((data) => {
feebackList.value = data.list
})
}
function handleAdd() {
showAdd.value = false
form.value = {
signId: orderId.value,
money: undefined,
isPayoff: true,
remark: undefined
}
}
const formRef = ref()
const formLoading = ref(false)
async function onSubmit() {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await createPayment(form.value)
message.success('申请成功!')
getList()
showAdd.value = true
} finally {
formLoading.value = false
}
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,125 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
<Descriptions :data="orderInfo" :schema="schema" :columns="2" labelWidth="130px" />
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
<el-form-item label="状态" prop="state">
<el-radio-group v-model="formData.state">
<el-radio :label="3"> 通过 </el-radio>
<el-radio :label="4"> 驳回 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<Editor v-model:modelValue="formData.remark" />
</el-form-item>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script name="DialogFeebackAudit" setup>
import { auditPayment } from '@/api/clue/payment'
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const schema = [
{
field: 'name',
label: '线索名称',
span: 1
},
{
field: 'phone',
label: '联系方式',
span: 1
},
{
field: 'signUserName',
label: '登记人',
span: 1
},
{
field: 'dealDate',
label: '登记时间',
span: 1
},
{
field: 'money',
label: '回款金额',
span: 1
},
{
field: 'isPayoff',
label: '是否结清',
span: 1
},
{
field: 'applyUserName',
label: '申请人',
span: 1
},
{
field: 'applyTime',
label: '申请时间',
span: 1
},
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formData = ref({
state: 3,
remark: ''
})
const formRef = ref() // 表单 Ref
const orderInfo = ref({})
/** 打开弹窗 */
const open = async (row) => {
dialogVisible.value = true
dialogTitle.value = '回款审核'
resetForm(row.id)
// 修改时,设置数据
orderInfo.value = { ...row }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
await auditPayment(formData.value)
message.success('审核完成!')
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = (signId) => {
formData.value = {
payId: signId,
state: 3,
remark: ''
}
formRef.value?.resetFields()
}
</script>

View File

@@ -0,0 +1,127 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="900px">
<Descriptions
title="申请详情"
:data="orderInfo"
:schema="applySchema"
:columns="2"
labelWidth="130px"
/>
<Descriptions
title="审核详情"
:data="orderInfo"
:schema="orderInfo.state == 2 ? cancelSchema : auditSchema"
:columns="orderInfo.state == 2 ? 2 : 3"
labelWidth="100px"
/>
</Dialog>
</template>
<script name="DialogFeebackDetail" setup>
import { getPaymentDetail } from '@/api/clue/payment'
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const applySchema = [
{
field: 'name',
label: '线索名称',
span: 1
},
{
field: 'phone',
label: '联系方式',
span: 1
},
{
field: 'signUserName',
label: '登记人',
span: 1
},
{
field: 'dealDate',
label: '登记时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'money',
label: '回款金额',
span: 1
},
{
field: 'isPayoff',
label: '是否结清',
span: 1
},
{
field: 'applyUserName',
label: '申请人',
span: 1
},
{
field: 'applyTime',
label: '申请时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
const auditSchema = [
{
field: 'stateName',
label: '审核状态',
span: 1
},
{
field: 'checkUser',
label: '审核人',
span: 1
},
{
field: 'checkTime',
label: '审核时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
},
{
field: 'checkRemark',
label: '备注',
span: 3,
isEditor: true
}
]
const cancelSchema = [
{
field: 'stateName',
label: '审核状态',
span: 1
},
{
field: 'revokeTime',
label: '撤销时间',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
span: 1
}
]
const orderInfo = ref({})
/** 打开弹窗 */
const open = async (id) => {
dialogVisible.value = true
dialogTitle.value = '回款申请详情'
try {
orderInfo.value = await getPaymentDetail({ id })
} catch (error) {
console.log(error)
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
</script>

View File

@@ -0,0 +1,121 @@
<template>
<Dialog title="成交详情" v-model="show" width="800px">
<el-tabs v-model="tabName">
<el-tab-pane label="线索信息" name="clueInfo">
<Descriptions :data="clueInfo" :schema="clueSchema" :columns="2" labelWidth="130px" />
</el-tab-pane>
<el-tab-pane label="成交信息" name="orderInfo">
<Descriptions :data="orderInfo" :schema="orderSchema" :columns="2" labelWidth="130px" />
<el-divider direction="horizontal" content-position="left">额外支出</el-divider>
<el-table :data="orderInfo.extraPay" border stripe>
<el-table-column type="index" width="50" />
<el-table-column prop="extraPayType" label="额外支出项" />
<el-table-column prop="extraPayMoney" label="支出金额" />
<el-table-column prop="remark" label="备注" />
</el-table>
</el-tab-pane>
<el-tab-pane label="回款记录" name="returnRecord">
<el-table :data="returnRecordList" border stripe>
<el-table-column type="index" width="50" />
<el-table-column prop="money" label="回款金额" />
<el-table-column prop="applyTime" label="申请日期" />
<el-table-column prop="isPayoff" label="是否结清" />
<el-table-column prop="stateName" label="审核状态" />
</el-table>
</el-tab-pane>
<el-tab-pane label="售后记录" name="afterSale">
<el-table :data="aftersaleList" border stripe>
<el-table-column type="index" width="50" />
<el-table-column prop="reason" label="售后原因" />
<el-table-column prop="refundAmount" label="退款金额" />
<el-table-column prop="isReturns" label="是否退货" />
<el-table-column prop="applyTime" label="申请日期" width="180px" />
<el-table-column prop="stateName" label="审核状态" />
</el-table>
</el-tab-pane>
</el-tabs>
<div class="mb-15px"></div>
</Dialog>
</template>
<script setup name="DialogOrder">
import * as ClueApi from '@/api/clue'
import * as OrderApi from '@/api/clue/sign'
import { getSimpleFieldList as getClueFieldList } from '@/api/clue/clueField'
import { getSimpleFieldList as getOrderFieldList } from '@/api/clue/orderField'
import { getPaymentPage } from '@/api/clue/payment'
import { getAfterSalePage } from '@/api/clue/afterSale'
import { formatDate } from '@/utils/formatTime'
const tabName = ref('clueInfo')
const show = ref(false)
const clueInfo = ref({})
const orderInfo = ref({})
const returnRecordList = ref([])
const aftersaleList = ref([])
function open(clueId, orderId) {
try {
show.value = true
tabName.value = 'clueInfo'
getFields()
ClueApi.getClue(clueId).then((data) => {
clueInfo.value = { ...data, ...data.diyParams }
})
OrderApi.getSign(orderId).then((data) => {
orderInfo.value = { ...data, ...data.diyParams }
orderInfo.value.dealDate = formatDate(orderInfo.value.dealDate, 'YYYY-MM-DD HH:mm')
})
getPaymentPage({ signId: orderId, pageNo: 1, pageSize: 100 }).then((data) => {
returnRecordList.value = data.list
})
getAfterSalePage({ signId: orderId, pageNo: 1, pageSize: 100 }).then((data) => {
aftersaleList.value = data.list
})
} catch (error) {
console.log(error)
}
}
const clueSchema = ref([])
const orderSchema = ref([])
function getFields() {
getClueFieldList().then((data) => {
const arr = useCrudSchemas(data).allSchemas.detailSchema
clueSchema.value = [
...arr,
{
field: 'requirement',
label: '诉求',
span: 2
},
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
})
getOrderFieldList().then((data) => {
const arr = useCrudSchemas(data).allSchemas.detailSchema
orderSchema.value = [
...arr,
{
field: 'remark',
label: '备注',
span: 2,
isEditor: true
}
]
})
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,367 @@
<template>
<div>
<!-- 搜索工作栏 -->
<div>
<el-form
:model="searchForm"
ref="moreSearchRef"
inline
label-width="0"
style="display: inline"
>
<template v-if="appStore.getAppInfo?.instanceType == 1">
<el-form-item style="margin-bottom: 10px">
<el-select
v-model="searchForm.signSchool"
placeholder="选择驾校"
filterable
clearable
@change="changeSchool"
>
<el-option
v-for="item in schoolOptions"
:key="item.schoolId"
:label="item.schoolName"
:value="item.schoolId"
/>
</el-select>
</el-form-item>
<el-form-item style="margin-bottom: 10px">
<el-select
v-model="searchForm.signPlace"
placeholder="选择场地"
filterable
clearable
:disabled="!searchForm.signSchool"
@change="changePlace"
>
<el-option
v-for="item in placeOptions"
:key="item.placeId"
:label="item.name"
:value="item.placeId"
/>
</el-select>
</el-form-item>
<el-form-item style="margin-bottom: 10px">
<el-select
v-model="searchForm.signClass"
:disabled="!searchForm.signPlace"
placeholder="选择班型"
filterable
clearable
>
<el-option
v-for="item in classOptions"
:key="item.typeId"
:label="item.typeName"
:value="item.typeId"
/>
</el-select>
</el-form-item>
</template>
<template v-else-if="appStore.getAppInfo?.instanceType == 2">
<el-form-item>
<el-select
v-model="searchForm.signProduct"
placeholder="选择成交产品"
filterable
@change="searchForm.specsId = undefined"
>
<el-option
v-for="item in prodOptions"
:key="item.productId"
:label="item.productName"
:value="item.productId"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-select
v-model="searchForm.specsId"
placeholder="选择规格"
filterable
:disabled="!searchForm.signProduct"
>
<el-option
v-for="item in specsOptions(searchForm.signProduct)"
:key="item.specsId"
:label="item.specsName"
:value="item.specsId"
/>
</el-select>
</el-form-item>
</template>
</el-form>
<Search
v-if="!loading"
ref="searchRef"
:schema="allSchemas.searchSchema"
inlineBlock
labelWidth="0"
>
<template #actionMore>
<el-button @click="getTableList" v-hasPermi="['clue:order:search']"> 搜索 </el-button>
<el-button @click="resetQuery" v-hasPermi="['clue:order:reset']"> 重置 </el-button>
</template>
</Search>
</div>
<!-- 列表 -->
<SSTable
v-if="!loading"
class="mt-10px"
v-model:tableObject="tableObject"
:tableColumns="allSchemas.tableColumns"
@get-list="getTableList"
>
<el-table-column
v-for="item in allSchemas.tableColumns"
:key="item.field"
:prop="item.field"
:label="item.label"
min-width="120px"
/>
<el-table-column label="操作" width="240px" fixed="right">
<template #default="scope">
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:detail']"
@click="handleDetail(scope.row)"
>
详情
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:after-sale']"
@click="sellAfter(scope.row)"
>
售后
</el-button>
<el-button
type="primary"
class="mr-10px"
link
v-if="appStore.getAppInfo?.instanceType == 2"
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:send']"
>
发货
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-if="scope.row.isPayoff == false"
v-hasPermi="['clue:order:return']"
@click="feeBack(scope.row)"
>
回款
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:pool:enroll']"
@click="cancelDeal(scope.row)"
>
取消登记
</el-button>
</template>
</el-table-column>
</SSTable>
<!-- 详情 -->
<DialogOrder ref="orderDetailDialog" />
<DialogFeeback ref="feedbackDialog" />
<DialogAfterSale ref="afterSaleDialog" />
</div>
</template>
<script setup name="ClueOrderList">
import { getSimpleFieldList } from '@/api/clue/orderField'
import * as SignApi from '@/api/clue/sign'
import { getSimpleUserList as getUserOption } from '@/api/system/user'
import { getPlaceList } from '@/api/school/place'
import { getClassTypePage } from '@/api/school/class'
import { getSimpleProductList } from '@/api/mall/product'
import DialogOrder from './DialogOrder.vue'
import DialogFeeback from './DialogFeeback.vue'
import DialogAfterSale from './DialogAfterSale.vue'
import { removeNullField } from '@/utils'
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const message = useMessage() // 消息弹窗
const allSchemas = ref({})
const orderDetailDialog = ref()
const searchRef = ref()
const schoolOptions = ref([])
const allPlaceOptions = ref([])
const prodOptions = ref([])
const specsOptions = computed({
get() {
return (prodId) => {
if (prodId) {
return prodOptions.value.find((it) => it.productId == prodId).productSpecList
}
return []
}
}
})
const searchForm = ref({
signSchool: undefined,
signPlace: undefined,
signClass: undefined,
signProduct: undefined
})
const tableObject = ref({
tableList: [],
loading: false,
total: 1,
pageSize: 20,
currentPage: 1
})
const placeOptions = computed(() => {
return allPlaceOptions.value.filter((it) => it.schoolId == searchForm.value.signSchool)
})
function resetQuery() {
searchForm.value = {
signSchool: undefined,
signPlace: undefined,
signClass: undefined,
signProduct: undefined
}
searchRef.value.reset()
tableObject.value.currentPage = 1
getTableList()
}
// 查询
async function getTableList() {
// 查询
tableObject.value.loading = true
try {
const queryParams = await searchRef.value.getFormModel()
const params = {
...queryParams,
...searchForm.value,
pageNo: tableObject.value.currentPage,
pageSize: tableObject.value.pageSize
}
const data = await SignApi.getSignPage(removeNullField(params))
tableObject.value.tableList = data.list.map((it) => ({ ...it, ...it.diyParams }))
tableObject.value.total = data.total
} finally {
tableObject.value.loading = false
}
}
const loading = ref(true)
async function getCurdSchemas() {
loading.value = true
try {
const data = await getSimpleFieldList()
data.forEach((elem) => {
if (elem.field == 'createUser') {
elem.search.options = userOptions.value
}
})
allSchemas.value = useCrudSchemas(data).allSchemas
} finally {
loading.value = false
nextTick(() => {
getTableList()
})
}
}
// 详情
function handleDetail(row) {
orderDetailDialog.value.open(row.clueId, row.signId)
}
const feedbackDialog = ref()
const afterSaleDialog = ref()
// 售后
function sellAfter(row) {
afterSaleDialog.value.open(row.signId)
}
// 回款
function feeBack(row) {
feedbackDialog.value.open(row.signId)
}
// 取消登记
async function cancelDeal(row) {
try {
// 二次确认
await message.confirm('是否确认取消登记该线索?')
// 发起删除
await SignApi.cancelDeal(row.signId)
message.success('取消登记成功!')
// 刷新列表
await getTableList()
} catch (err) {
console.log(err)
}
}
function changeSchool() {
searchForm.value.signPlace = undefined
searchForm.value.signClass = undefined
}
function changePlace() {
searchForm.value.signClass = undefined
getClassTypeOptions()
}
const classOptions = ref([])
async function getClassTypeOptions() {
const data = await getClassTypePage({ placeId: searchForm.value.signPlace })
classOptions.value = data.list
}
function getOptions() {
if (appStore.getAppInfo?.instanceType == 1) {
// 驾校
getPlaceList().then((data) => {
schoolOptions.value = data.schoolList
allPlaceOptions.value = data.placeList
})
} else {
// 产品
getSimpleProductList().then((data) => {
prodOptions.value = data
})
}
}
const userOptions = ref([])
onMounted(() => {
getUserOption().then((data) => {
userOptions.value = data
getCurdSchemas()
})
getOptions()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,236 @@
<template>
<div>
<el-form :model="searchForm" label-width="0" inline>
<el-form-item>
<el-input v-model="searchForm.signId" placeholder="成交单号" clearable />
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.name" placeholder="线索名称" clearable />
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.state" placeholder="审核状态" clearable>
<el-option label="待审核" :value="1" />
<el-option label="已撤销" :value="2" />
<el-option label="已通过" :value="3" />
<el-option label="已驳回" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.signUser" placeholder="登记人" clearable filterable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.dealDate"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="登记日期"
end-placeholder="登记日期"
/>
</el-form-item>
<el-form-item>
<el-select v-model="searchForm.applyUser" placeholder="申请人" clearable filterable>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.applyTime"
type="daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
range-separator="-"
start-placeholder="申请日期"
end-placeholder="申请日期"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button @click="batchAudit">批量审核</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="loading"
:data="tableList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" :selectable="(row) => row.state == 1" />
<el-table-column prop="signId" label="成交单号" min-width="150px" />
<el-table-column prop="name" label="线索名称" min-width="200px" />
<el-table-column prop="phone" label="联系方式" min-width="150px" />
<el-table-column prop="money" label="回款金额" min-width="90" />
<el-table-column prop="isPayoff" label="是否结清" min-width="90" />
<el-table-column prop="signUserName" label="登记人" min-width="90" />
<el-table-column prop="dealDate" label="登记时间" min-width="150px" />
<el-table-column prop="applyUserName" label="申请人" min-width="90" />
<el-table-column prop="applyTime" label="申请时间" min-width="150px" />
<el-table-column prop="stateName" label="审核状态" fixed="right" min-width="90" />
<el-table-column label="操作" width="150px" fixed="right">
<template #default="{ row }">
<el-button type="primary" style="padding: 0" text @click="handleDetail(row.id)">
详情
</el-button>
<el-button
type="primary"
style="padding: 0"
text
v-if="row.state == 1 && currentUserId == row.applyUser"
v-hasPermi="['clue:order:return']"
@click="handleCancel(row.id)"
>
撤销
</el-button>
<el-button
type="primary"
style="padding: 0"
text
v-if="row.state == 1"
v-hasPermi="['clue:order:return-audit']"
@click="handleAudit(row)"
>
审核
</el-button>
</template>
</el-table-column>
</el-table>
<Pagination
v-model:limit="searchForm.pageSize"
v-model:page="searchForm.pageNo"
:total="total"
@pagination="getList"
/>
<DialogFeebackAudit ref="feebackDialog" @success="getList" />
<DialogFeebackDetail ref="feebackDetailDialog" />
<DialogBatchAudit ref="batchAuditDialog" @success="getList" />
</div>
</template>
<script setup name="Reback">
import * as FeebackApi from '@/api/clue/payment'
import { getSimpleUserList as getUserOption } from '@/api/system/user'
import { useUserStore } from '@/store/modules/user'
import DialogFeebackAudit from './DialogFeebackAudit.vue'
import DialogFeebackDetail from './DialogFeebackDetail.vue'
import DialogBatchAudit from './DialogBatchAudit.vue'
const userStore = useUserStore()
const message = useMessage() // 消息弹窗
const feebackDialog = ref()
const currentUserId = userStore.getUser.id
const searchForm = ref({
signId: undefined,
name: undefined,
state: undefined,
dealDate: [],
signUser: undefined,
applyTime: [],
applyUser: undefined,
pageNo: 1,
pageSize: 20
})
const userOptions = ref([])
const tableList = ref([])
const total = ref(0)
function handleSearch() {
searchForm.value.pageNo = 1
getList()
}
function handleReset() {
searchForm.value = {
signId: undefined,
name: undefined,
dealDate: [],
state: undefined,
signUser: undefined,
applyTime: [],
applyUser: undefined,
pageNo: 1,
pageSize: 20
}
}
const loading = ref(false)
async function getList() {
loading.value = true
try {
const data = await FeebackApi.getPaymentPage(searchForm.value)
tableList.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const batchIds = ref([])
function handleSelectionChange(val) {
batchIds.value = val.map((it) => it.id)
}
const batchAuditDialog = ref()
function batchAudit() {
if (batchIds.value.length) {
batchAuditDialog.value.open('aftersale', batchIds.value)
} else {
message.info('请选择表格中需要审核的数据')
}
}
const feebackDetailDialog = ref()
function handleDetail(id) {
feebackDetailDialog.value.open(id)
}
async function handleCancel(id) {
try {
// 删除的二次确认
await message.confirm('是否确认撤销申请?')
// 发起删除
await FeebackApi.cancelApplyPayment({ id })
message.success('撤销成功!')
// 刷新列表
await getList()
} catch (err) {
console.log(err)
}
}
function handleAudit(row) {
feebackDialog.value.open(row)
}
function getOptions() {
getUserOption().then((data) => {
userOptions.value = data
})
}
onMounted(() => {
getOptions()
handleSearch()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -1,104 +1,28 @@
<template> <template>
<div> <el-tabs v-model="tabName">
<!-- 搜索工作栏 --> <el-tab-pane label="成交列表" name="list">
<Search :schema="allSchemas.searchSchema" labelWidth="0"> <OrderList v-if="tabName == 'list'" />
<template #actionMore> </el-tab-pane>
<el-button @click="getTableList" v-hasPermi="['clue:order:search']"> 搜索 </el-button> <el-tab-pane label="回款申请" name="commission">
<el-button @click="resetQuery" v-hasPermi="['clue:order:reset']"> 重置 </el-button> <Reback v-if="tabName == 'commission'" />
</template> </el-tab-pane>
</Search> <el-tab-pane label="售后申请" name="aftersale">
<!-- 列表 --> <AfterSales v-if="tabName == 'aftersale'" />
<SSTable </el-tab-pane>
class="mt-20px" <el-tab-pane label="发货列表" name="delivery" v-if="appStore.getAppInfo?.instanceType == 2">
v-model:tableObject="tableObject" <OrderList v-if="tabName == 'delivery'" />
:tableColumns="allSchemas.tableColumns" </el-tab-pane>
@get-list="getTableList" </el-tabs>
>
<el-table-column
v-for="item in allSchemas.tableColumns"
:key="item.field"
:prop="item.field"
:label="item.label"
min-width="120px"
/>
<el-table-column label="操作" width="200px" fixed="right">
<template #default="scope">
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:after-sale']"
@click="sellAfter(scope.row)"
>
售后
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:after-sale-audit']"
>
售后审核
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:send']"
>
发货(进销存)
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:return']"
@click="feeBack(scope.row)"
>
回款
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:return-audit']"
>
回款确认
</el-button>
</template>
</el-table-column>
</SSTable>
</div>
</template> </template>
<script setup name="ClueOrder"> <script setup name="ClueOrder">
import { allSchemas } from './order.data' import { useAppStore } from '@/store/modules/app'
import OrderList from './Comp/OrderList.vue'
import Reback from './Comp/Reback.vue'
import AfterSales from './Comp/AfterSales.vue'
const tableObject = ref({ const appStore = useAppStore()
tableList: [{ name: '测试', contact: '18888888888' }], const tabName = ref('list')
loading: false,
total: 1,
pageSize: 20,
currentPage: 1
})
function resetQuery() {
// 方法体
}
// 查询
function getTableList() {
// 方法体
}
// 售后
function sellAfter() {
// 方法体
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -1,11 +1,12 @@
<template> <template>
<Dialog title="成交登记" v-model="show" width="820px"> <Dialog title="成交登记" v-model="show" width="820px">
<Descriptions <Descriptions
title="线索信息" :title="`线索信息-${info.name}`"
:data="info" :data="info"
:schema="showSchema" :schema="showSchema"
:columns="2" :columns="2"
labelWidth="130px" labelWidth="130px"
:defaultShow="false"
/> />
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px" class="mt-20px"> <el-form :model="form" ref="formRef" :rules="rules" label-width="80px" class="mt-20px">
<el-row :gutter="20"> <el-row :gutter="20">
@@ -128,11 +129,13 @@
class="mb-5px" class="mb-5px"
type="primary" type="primary"
size="small" size="small"
@click="form.signProduct.push({ productId: undefined, specsId: undefined, signNum: 0 })" @click="
form.signProducts.push({ productId: undefined, specsId: undefined, signNum: 0 })
"
> >
添加成交产品 添加成交产品
</el-button> </el-button>
<el-table :data="form.signProduct" border size="small"> <el-table :data="form.signProducts" border size="small">
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
<el-table-column prop="productId" label="产品"> <el-table-column prop="productId" label="产品">
<template #default="{ row }"> <template #default="{ row }">
@@ -189,7 +192,7 @@
<Icon <Icon
icon="ep:remove-filled" icon="ep:remove-filled"
class="text-red-500" class="text-red-500"
@click="handleRemove('signProduct', $index)" @click="handleRemove('signProducts', $index)"
/> />
</template> </template>
</el-table-column> </el-table-column>
@@ -255,7 +258,7 @@
<template #footer> <template #footer>
<span> <span>
<el-button @click="show = false"> </el-button> <el-button @click="show = false"> </el-button>
<el-button type="primary" @click="handleSave"> </el-button> <el-button :disabled="formLoading" type="primary" @click="handleSave"> </el-button>
</span> </span>
</template> </template>
</Dialog> </Dialog>
@@ -339,7 +342,7 @@ function resetForm(id) {
payAmount: 0, payAmount: 0,
remark: undefined, remark: undefined,
extraPay: [], extraPay: [],
signProduct: [] signProducts: []
} }
} }
@@ -355,7 +358,7 @@ async function handleSave() {
return return
} }
if (form.value.signProduct.some((it) => !it.schoolId || !item.specsId || !it.signNum)) { if (form.value.signProducts.some((it) => !it.productId || !item.specsId || !it.signNum)) {
message.info('请将成交产品信息填写完整!') message.info('请将成交产品信息填写完整!')
return return
} }
@@ -363,9 +366,19 @@ async function handleSave() {
// 提交请求 // 提交请求
formLoading.value = true formLoading.value = true
try { try {
await createSign(form.value) const params = { ...form.value }
params.diyParams = {}
diyFieldList.value.map((it) => {
params.diyParams[it.field] = undefined
})
for (const key in params.diyParams) {
if (Object.hasOwnProperty.call(params, key)) {
params.diyParams[key] = params[key]
}
}
await createSign(params)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
dialogVisible.value = false show.value = false
// 发送操作成功的事件 // 发送操作成功的事件
emit('success') emit('success')
} finally { } finally {

View File

@@ -31,10 +31,10 @@
<!-- 基础信息 --> <!-- 基础信息 -->
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-table :data="followList" size="small" border class="mt-10px"> <el-table :data="followList" size="small" border class="mt-10px">
<el-table-column prop="name" label="跟进人" /> <el-table-column prop="userName" label="跟进人" />
<el-table-column prop="latestFollowTime" label="最新跟进时间" /> <el-table-column prop="followTime" label="最新跟进时间" :formatter="dateFormatter" />
<el-table-column prop="nextFollowTime" label="下次跟进时间" /> <el-table-column prop="nextFollowTime" label="下次跟进时间" />
<el-table-column prop="saleStatus" label="成交状态" /> <el-table-column prop="signSate" label="成交状态" />
</el-table> </el-table>
</el-skeleton> </el-skeleton>
<el-divider direction="horizontal" /> <el-divider direction="horizontal" />
@@ -99,7 +99,8 @@
<span>操作人{{ item.operateUserName }}</span> <span>操作人{{ item.operateUserName }}</span>
</div> </div>
<div class="pt-5px pb-5px"> <div class="pt-5px pb-5px">
<span>{{ item.content }}</span> <!-- <span>{{ item.content }}</span> -->
<span v-dompurify-html="item.content"></span>
</div> </div>
<div class="flex" style="align-items: center"> <div class="flex" style="align-items: center">
<Icon icon="ep:clock" class="mr-5px" /> <Icon icon="ep:clock" class="mr-5px" />
@@ -124,7 +125,7 @@ import { getPlaceList } from '@/api/school/place'
import DialogFollow from './DialogFollow.vue' import DialogFollow from './DialogFollow.vue'
import AMapLoader from '@amap/amap-jsapi-loader' import AMapLoader from '@amap/amap-jsapi-loader'
import { formatDate } from '@/utils/formatTime' import { formatDate, dateFormatter } from '@/utils/formatTime'
import ImgPostion from '@/assets/imgs/flag/position_blue.png' import ImgPostion from '@/assets/imgs/flag/position_blue.png'
import FlagRed from '@/assets/imgs/flag/flag_red.png' import FlagRed from '@/assets/imgs/flag/flag_red.png'
@@ -165,20 +166,7 @@ const showSchema = computed(() => {
return [...props.schema, ...arr] return [...props.schema, ...arr]
}) })
const followList = ref([ const followList = ref([])
{
name: '李四',
latestFollowTime: '2024-02-01',
nextFollowTime: '2024-04-01',
saleStatus: '未成交'
},
{
name: '王二',
latestFollowTime: '2024-03-01',
nextFollowTime: '2024-04-11',
saleStatus: '已成交'
}
])
const followRecordList = ref([]) const followRecordList = ref([])
const operateRecordList = ref([]) const operateRecordList = ref([])
@@ -201,9 +189,16 @@ function getFollowList() {
}) })
} }
function getFollowUsers(id) {
ClueApi.getFollowUserList({ id }).then((data) => {
followList.value = data
})
}
async function open(id) { async function open(id) {
clueId.value = id clueId.value = id
try { try {
getFollowUsers(id)
getFollowList() getFollowList()
ClueApi.getOpearateRecord({ clueId: id }).then((data) => { ClueApi.getOpearateRecord({ clueId: id }).then((data) => {
operateRecordList.value = data.map((item) => ({ operateRecordList.value = data.map((item) => ({

View File

@@ -109,6 +109,7 @@
<el-button <el-button
type="primary" type="primary"
link link
:disabled="scope.row.state"
@click="handleSuccess(scope.row)" @click="handleSuccess(scope.row)"
v-hasPermi="['clue:pool:enroll']" v-hasPermi="['clue:pool:enroll']"
> >
@@ -147,11 +148,11 @@
<DrawerClue <DrawerClue
v-if="!loading" v-if="!loading"
ref="drawerRef" ref="drawerRef"
:schema="allSchemas.formSchema" :schema="allSchemas.detailSchema"
@get-list="getTableList" @get-list="getTableList"
@update="handleEdit" @update="handleEdit"
/> />
<DialogSuccess ref="successRef" :schema="allSchemas.formSchema" @success="getTableList" /> <DialogSuccess ref="successRef" :schema="allSchemas.detailSchema" @success="getTableList" />
<DialogFollow ref="followRef" @success="getTableList" /> <DialogFollow ref="followRef" @success="getTableList" />
</div> </div>
</template> </template>
@@ -187,6 +188,11 @@ async function getCurdSchemas() {
loading.value = true loading.value = true
try { try {
const data = await getSimpleFieldList() const data = await getSimpleFieldList()
data.forEach((elem) => {
if (elem.field == 'followUser') {
elem.search.options = userOptions.value
}
})
allSchemas.value = useCrudSchemas(data).allSchemas allSchemas.value = useCrudSchemas(data).allSchemas
} finally { } finally {
loading.value = false loading.value = false
@@ -314,8 +320,8 @@ const userOptions = ref([])
onMounted(() => { onMounted(() => {
getUserOption().then((data) => { getUserOption().then((data) => {
userOptions.value = data userOptions.value = data
getCurdSchemas()
}) })
getCurdSchemas()
}) })
</script> </script>

View File

@@ -93,7 +93,7 @@ const loading = ref(false)
const userList = ref([]) const userList = ref([])
function setRowClass({ row }) { function setRowClass({ row }) {
return row.field == currentRowId.value ? 'current-row' : '' return row.id == currentRowId.value ? 'current-row' : ''
} }
const currentRowId = ref('') const currentRowId = ref('')
@@ -144,7 +144,7 @@ function resourceCheckedChange(val) {
} }
function handleRowClick(row) { function handleRowClick(row) {
currentRowId.value = row.ruleId currentRowId.value = row.id
form.value = { ...row } form.value = { ...row }
} }

View File

@@ -1,21 +1,57 @@
<template> <template>
<el-form :model="form" ref="formRef" label-width="auto"> <el-form :model="form" ref="formRef" label-width="auto">
<el-form-item label="售后申请自动通过"> <el-form-item v-if="getConfig('afterSalesAuditAutoCompleteConfig')">
<el-radio-group v-model="form.autoAudiAfterSale"> <template #label>
<el-radio :label="0"> </el-radio> <Tooltip
<el-radio :label="1"> </el-radio> v-if="getConfig('afterSalesAuditAutoCompleteConfig').remark"
:message="getConfig('afterSalesAuditAutoCompleteConfig').remark"
/>
<span>售后申请自动通过</span>
</template>
<el-radio-group v-model="form.afterSalesAuditAutoCompleteConfig">
<el-radio
v-for="(item, index) in getConfig('afterSalesAuditAutoCompleteConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="发货自动发起采购"> <el-form-item v-if="getConfig('deliveryAutoStartPurchaseConfig')">
<el-radio-group v-model="form.autoSend"> <template #label>
<el-radio :label="0"> </el-radio> <Tooltip
<el-radio :label="1"> </el-radio> v-if="getConfig('deliveryAutoStartPurchaseConfig').remark"
:message="getConfig('deliveryAutoStartPurchaseConfig').remark"
/>
<span>发货自动发起采购</span>
</template>
<el-radio-group v-model="form.deliveryAutoStartPurchaseConfig">
<el-radio
v-for="(item, index) in getConfig('deliveryAutoStartPurchaseConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="回款申请自动通过"> <el-form-item v-if="getConfig('repaymentAuditAutoCompleteConfig')">
<el-radio-group v-model="form.autoAuditReturn"> <template #label>
<el-radio :label="0"> </el-radio> <Tooltip
<el-radio :label="1"> </el-radio> v-if="getConfig('repaymentAuditAutoCompleteConfig').remark"
:message="getConfig('repaymentAuditAutoCompleteConfig').remark"
/>
<span>回款申请自动通过</span>
</template>
<el-radio-group v-model="form.repaymentAuditAutoCompleteConfig">
<el-radio
v-for="(item, index) in getConfig('repaymentAuditAutoCompleteConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -26,25 +62,42 @@
</template> </template>
<script setup name="GeneralClue"> <script setup name="GeneralClue">
import * as ConfigApi from '@/api/system/set'
const message = useMessage() const message = useMessage()
const form = ref({ const form = ref({})
autoAudiAfterSale: 0,
autoSend: 0, const configList = ref([])
autoAuditReturn: 0
}) function getConfig(val) {
return configList.value.find((it) => it.configKey == val)
}
function getData() { function getData() {
form.value = { ConfigApi.getConfigList({ module: 1 }).then((data) => {
autoAudiAfterSale: 0, configList.value = data
autoSend: 0, // 获取所有配置项
autoAuditReturn: 0 data.map((it) => {
} form.value[it.configKey] = it.configValue
})
})
} }
function onSubmit() { function onSubmit() {
message.success('保存成功') const params = configList.value.map((it) => ({
configId: it.configId,
configKey: it.configKey,
configValue: form.value[it.configKey]
}))
ConfigApi.updateConfig(params).then(() => {
message.success('保存成功')
})
} }
onMounted(() => {
getData()
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -10,20 +10,20 @@
<el-tab-pane label="线索来源" :name="15" v-if="checkPermi(['clue:setting:clue-resource'])"> <el-tab-pane label="线索来源" :name="15" v-if="checkPermi(['clue:setting:clue-resource'])">
<ClueSource v-if="tabIndex == 15" /> <ClueSource v-if="tabIndex == 15" />
</el-tab-pane> </el-tab-pane>
<!-- <el-tab-pane <el-tab-pane
label="线索获取规则" label="线索获取规则"
:name="20" :name="20"
v-if="checkPermi(['clue:setting:clue-get-rules'])" v-if="checkPermi(['clue:setting:clue-get-rules'])"
> >
<ClueGet v-if="tabIndex == 20" /> <ClueGet v-if="tabIndex == 20" />
</el-tab-pane> --> </el-tab-pane>
<!-- <el-tab-pane <el-tab-pane
label="线索分配规则" label="线索分配规则"
:name="30" :name="30"
v-if="checkPermi(['clue:setting:clue-send-rules'])" v-if="checkPermi(['clue:setting:clue-send-rules'])"
> >
<ClueSend v-if="tabIndex == 30" /> <ClueSend v-if="tabIndex == 30" />
</el-tab-pane> --> </el-tab-pane>
<el-tab-pane label="意向状态" :name="35" v-if="checkPermi(['clue:setting:intention-status'])"> <el-tab-pane label="意向状态" :name="35" v-if="checkPermi(['clue:setting:intention-status'])">
<IntentionStatus v-if="tabIndex == 35" /> <IntentionStatus v-if="tabIndex == 35" />
</el-tab-pane> </el-tab-pane>
@@ -44,8 +44,8 @@
import FieldClue from './Comp/FieldClue.vue' import FieldClue from './Comp/FieldClue.vue'
import FieldOrder from './Comp/FieldOrder.vue' import FieldOrder from './Comp/FieldOrder.vue'
import ClueSource from './Comp/ClueSource.vue' import ClueSource from './Comp/ClueSource.vue'
// import ClueGet from './Comp/ClueGet.vue' import ClueGet from './Comp/ClueGet.vue'
// import ClueSend from './Comp/ClueSend.vue' import ClueSend from './Comp/ClueSend.vue'
// import MsgSend from './Comp/MsgSend.vue' // import MsgSend from './Comp/MsgSend.vue'
import IntentionStatus from './Comp/IntentionStatus.vue' import IntentionStatus from './Comp/IntentionStatus.vue'
import ExtraFeeType from './Comp/ExtraFeeType.vue' import ExtraFeeType from './Comp/ExtraFeeType.vue'

View File

@@ -11,9 +11,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleQuery" v-hasPermi="['clue:skill:search']"> <el-button @click="handleQuery" v-hasPermi="['clue:skill:search']">搜索</el-button>
搜索
</el-button>
<el-button @click="resetQuery" v-hasPermi="['clue:skill:reset']">重置</el-button> <el-button @click="resetQuery" v-hasPermi="['clue:skill:reset']">重置</el-button>
<el-button type="primary" plain @click="handleAdd" v-hasPermi="['clue:skill:add']"> <el-button type="primary" plain @click="handleAdd" v-hasPermi="['clue:skill:add']">
新增 新增

View File

@@ -203,7 +203,7 @@ const getMonthlySaleRate = async () => {
) )
set(lineOptionsData, 'series', [ set(lineOptionsData, 'series', [
{ {
name: t('analysis.estimate'), name: '个人成交率',
smooth: true, smooth: true,
type: 'line', type: 'line',
data: data.map((v) => v.estimate), data: data.map((v) => v.estimate),
@@ -211,7 +211,7 @@ const getMonthlySaleRate = async () => {
animationEasing: 'cubicInOut' animationEasing: 'cubicInOut'
}, },
{ {
name: t('analysis.actual'), name: '平均成交率',
smooth: true, smooth: true,
type: 'line', type: 'line',
itemStyle: {}, itemStyle: {},

View File

@@ -1,9 +1,21 @@
<template> <template>
<el-form :model="form" ref="formRef" label-width="auto"> <el-form :model="form" ref="formRef" label-width="auto">
<el-form-item label="采购申请自动通过"> <el-form-item v-if="getConfig('purchaseAuditAutoCompleteConfig')">
<template #label>
<Tooltip
v-if="getConfig('purchaseAuditAutoCompleteConfig').remark"
:message="getConfig('purchaseAuditAutoCompleteConfig').remark"
/>
<span>采购申请自动通过</span>
</template>
<el-radio-group v-model="form.purchaseAuditAutoCompleteConfig"> <el-radio-group v-model="form.purchaseAuditAutoCompleteConfig">
<el-radio label="true"> </el-radio> <el-radio
<el-radio label="false"> </el-radio> v-for="(item, index) in getConfig('purchaseAuditAutoCompleteConfig').options"
:key="index"
:label="item.id"
>
{{ item.name }}
</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -17,27 +29,31 @@ import * as ConfigApi from '@/api/system/set'
const message = useMessage() const message = useMessage()
const form = ref({ const form = ref({})
purchaseAuditAutoCompleteConfig: 'true'
})
const info = ref({}) const configList = ref([])
function getConfig(val) {
return configList.value.find((it) => it.configKey == val)
}
function getData() { function getData() {
ConfigApi.getConfigByConfigKey({ configKey: 'purchaseAuditAutoCompleteConfig' }).then((data) => { ConfigApi.getConfigList({ module: 3 }).then((data) => {
info.value = data configList.value = data
form.value = { // 获取所有配置项
purchaseAuditAutoCompleteConfig: data.configValue data.map((it) => {
} form.value[it.configKey] = it.configValue
})
}) })
} }
function onSubmit() { function onSubmit() {
const data = { const params = configList.value.map((it) => ({
...info, configId: it.configId,
purchaseAuditAutoCompleteConfig: form.value.purchaseAuditAutoCompleteConfig configKey: it.configKey,
} configValue: form.value[it.configKey]
ConfigApi.updateConfig(data).then(() => { }))
ConfigApi.updateConfig(params).then(() => {
message.success('保存成功') message.success('保存成功')
}) })
} }