【线索管理】 新增# 线索管理
parent
4221095b2c
commit
3793f8d8ab
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<el-drawer v-model="drawerVisible" :title="parameter.title" :size="parameter.width">
|
||||
<slot />
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确认</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
export interface IParams {
|
||||
title: string
|
||||
width?: string | number
|
||||
data: { [key: string]: any }
|
||||
}
|
||||
const emit = defineEmits(['handleCancel', 'handleConfirm'])
|
||||
|
||||
const drawerVisible = ref(false)
|
||||
|
||||
// 父组件传递过来的参数
|
||||
const parameter = ref<IParams>({
|
||||
title: '',
|
||||
width: '50%',
|
||||
data: {}
|
||||
})
|
||||
const openDrawer = (params: IParams) => {
|
||||
parameter.value = { ...parameter.value, ...params }
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('handleCancel', closeDrawer)
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
emit('handleConfirm', closeDrawer)
|
||||
}
|
||||
const closeDrawer = () => {
|
||||
drawerVisible.value = false
|
||||
}
|
||||
defineExpose({
|
||||
openDrawer,
|
||||
drawerVisible
|
||||
})
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -10,6 +10,7 @@ import directives from './directives'
|
|||
// 表格组件
|
||||
import ProTable from '@/components/ProTable/index.vue'
|
||||
import ProDialog from '@/components/ProDialog/index.vue'
|
||||
import ProDrawer from '@/components/ProDrawer/index.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(install)
|
||||
|
@ -17,4 +18,5 @@ app.use(eventBus)
|
|||
app.use(directives)
|
||||
app.component('ProTable', ProTable)
|
||||
app.component('ProDialog', ProDialog)
|
||||
app.component('ProDrawer', ProDrawer)
|
||||
app.mount('#app')
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
--el-text-color-secondary: #999999;
|
||||
--el-text-color-placeholder: #a8abb2;
|
||||
--el-text-color-disabled: #c0c4cc;
|
||||
--el-text-color-black2: #979797;
|
||||
--el-border-color: #dcdfe6;
|
||||
--el-border-color-light: #e4e7ed;
|
||||
--el-border-color-lighter: #ebeef5;
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
<template>
|
||||
<div class="edit-popup">
|
||||
<popup
|
||||
ref="popupRef"
|
||||
:title="popupTitle"
|
||||
:async="true"
|
||||
width="550px"
|
||||
@confirm="handleSubmit"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" label-width="84px" :rules="formRules">
|
||||
<el-form-item label="上级部门" prop="pid" v-if="formData.pid !== 0">
|
||||
<el-tree-select
|
||||
class="flex-1"
|
||||
v-model="formData.pid"
|
||||
:data="optionsData.dept"
|
||||
clearable
|
||||
node-key="id"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name'
|
||||
}"
|
||||
check-strictly
|
||||
:default-expand-all="true"
|
||||
placeholder="请选择上级部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入部门名称"
|
||||
clearable
|
||||
:maxlength="100"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="duty">
|
||||
<el-input
|
||||
v-model="formData.duty"
|
||||
placeholder="请输入负责人姓名"
|
||||
clearable
|
||||
:maxlength="30"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="mobile">
|
||||
<el-input v-model="formData.mobile" placeholder="请输入联系电话" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<div>
|
||||
<el-input-number v-model="formData.sort" :min="0" :max="9999" />
|
||||
<div class="form-tips">默认为0, 数值越大越排前</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门状态" prop="isStop">
|
||||
<el-switch v-model="formData.isStop" :active-value="0" :inactive-value="1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</popup>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { deptLists, deptEdit, deptAdd, deptDetail } from '@/api/org/department'
|
||||
import Popup from '@/components/popup/index.vue'
|
||||
import { useDictOptions } from '@/hooks/useDictOptions'
|
||||
import feedback from '@/utils/feedback'
|
||||
const emit = defineEmits(['success', 'close'])
|
||||
const formRef = shallowRef<FormInstance>()
|
||||
const popupRef = shallowRef<InstanceType<typeof Popup>>()
|
||||
const mode = ref('add')
|
||||
const popupTitle = computed(() => {
|
||||
return mode.value == 'edit' ? '编辑部门' : '新增部门'
|
||||
})
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
pid: '' as string | number,
|
||||
name: '',
|
||||
duty: '',
|
||||
mobile: '',
|
||||
sort: 0,
|
||||
isStop: 0
|
||||
})
|
||||
const checkMobile = (rule: any, value: any, callback: any) => {
|
||||
if (!value) {
|
||||
return callback()
|
||||
} else {
|
||||
const reg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/
|
||||
console.log(reg.test(value))
|
||||
if (reg.test(value)) {
|
||||
callback()
|
||||
} else {
|
||||
return callback(new Error('请输入正确的手机号'))
|
||||
}
|
||||
}
|
||||
}
|
||||
const formRules = {
|
||||
pid: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择上级部门',
|
||||
trigger: ['change']
|
||||
}
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入部门名称',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
duty: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入负责人',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
mobile: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入联系电话',
|
||||
trigger: ['blur']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const { optionsData } = useDictOptions<{
|
||||
dept: any[]
|
||||
}>({
|
||||
dept: {
|
||||
api: deptLists
|
||||
}
|
||||
})
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate()
|
||||
mode.value == 'edit' ? await deptEdit(formData) : await deptAdd(formData)
|
||||
popupRef.value?.close()
|
||||
feedback.msgSuccess('操作成功')
|
||||
emit('success')
|
||||
}
|
||||
|
||||
const open = (type = 'add') => {
|
||||
mode.value = type
|
||||
popupRef.value?.open()
|
||||
}
|
||||
|
||||
const setFormData = (data: Record<any, any>) => {
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getDetail = async (row: Record<string, any>) => {
|
||||
const data = await deptDetail({
|
||||
id: row.id
|
||||
})
|
||||
setFormData(data)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
setFormData,
|
||||
getDetail
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<div class="dept-lists">
|
||||
<el-card class="!border-none" shadow="never">
|
||||
<el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
|
||||
<el-form-item label="部门名称" prop="name">
|
||||
<el-input
|
||||
class="w-[280px]"
|
||||
v-model="queryParams.name"
|
||||
clearable
|
||||
@keyup.enter="getLists"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门状态" prop="isStop">
|
||||
<el-select class="w-[280px]" v-model="queryParams.isStop">
|
||||
<el-option label="全部" value />
|
||||
<el-option label="正常" value="0" />
|
||||
<el-option label="停用" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getLists">查询</el-button>
|
||||
<el-button @click="resetParams">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<div>
|
||||
<el-button v-perms="['system:dept:add']" type="primary" @click="handleAdd()">
|
||||
<template #icon>
|
||||
<icon name="el-icon-Plus" />
|
||||
</template>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button @click="handleExpand"> 展开/折叠 </el-button>
|
||||
</div>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
class="mt-4"
|
||||
size="large"
|
||||
v-loading="loading"
|
||||
:data="lists"
|
||||
row-key="id"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<el-table-column
|
||||
label="部门名称"
|
||||
prop="name"
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="部门状态" prop="isStop" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag class="ml-2" :type="row.isStop ? 'danger' : ''">
|
||||
{{ row.isStop ? '停用' : '正常' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sort" min-width="100" />
|
||||
<el-table-column label="更新时间" prop="updateTime" min-width="180" />
|
||||
<el-table-column label="操作" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-perms="['system:dept:add']"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleAdd(row.id)"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
v-perms="['system:dept:edit']"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleEdit(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.pid !== 0"
|
||||
v-perms="['system:dept:del']"
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDelete(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="department">
|
||||
import type { ElTable, FormInstance } from 'element-plus'
|
||||
import EditPopup from './edit.vue'
|
||||
import { deptDelete, deptLists } from '@/api/org/department'
|
||||
import feedback from '@/utils/feedback'
|
||||
const tableRef = shallowRef<InstanceType<typeof ElTable>>()
|
||||
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
||||
const formRef = shallowRef<FormInstance>()
|
||||
let isExpand = false
|
||||
const loading = ref(false)
|
||||
const lists = ref<any[]>([])
|
||||
const queryParams = reactive({
|
||||
isStop: '',
|
||||
name: ''
|
||||
})
|
||||
const showEdit = ref(false)
|
||||
const getLists = async () => {
|
||||
loading.value = true
|
||||
lists.value = await deptLists(queryParams)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const resetParams = () => {
|
||||
formRef.value?.resetFields()
|
||||
getLists()
|
||||
}
|
||||
|
||||
const handleAdd = async (id?: number) => {
|
||||
showEdit.value = true
|
||||
await nextTick()
|
||||
if (id) {
|
||||
editRef.value?.setFormData({
|
||||
pid: id
|
||||
})
|
||||
}
|
||||
editRef.value?.open('add')
|
||||
}
|
||||
|
||||
const handleEdit = async (data: any) => {
|
||||
showEdit.value = true
|
||||
await nextTick()
|
||||
editRef.value?.open('edit')
|
||||
editRef.value?.getDetail(data)
|
||||
}
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
await feedback.confirm('确定要删除?')
|
||||
await deptDelete({ id })
|
||||
feedback.msgSuccess('删除成功')
|
||||
getLists()
|
||||
}
|
||||
|
||||
const handleExpand = () => {
|
||||
isExpand = !isExpand
|
||||
toggleExpand(lists.value, isExpand)
|
||||
}
|
||||
|
||||
const toggleExpand = (children: any[], unfold = true) => {
|
||||
for (const key in children) {
|
||||
tableRef.value?.toggleRowExpansion(children[key], unfold)
|
||||
if (children[key].children) {
|
||||
toggleExpand(children[key].children!, unfold)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await getLists()
|
||||
nextTick(() => {
|
||||
handleExpand()
|
||||
})
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div class="edit-popup">
|
||||
<popup
|
||||
ref="popupRef"
|
||||
:title="popupTitle"
|
||||
:async="true"
|
||||
width="550px"
|
||||
@confirm="handleSubmit"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" label-width="84px" :rules="formRules">
|
||||
<el-form-item label="岗位名称" prop="name">
|
||||
<el-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入岗位名称"
|
||||
clearable
|
||||
:maxlength="100"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入岗位编码" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<div>
|
||||
<el-input-number v-model="formData.sort" :min="0" :max="9999" />
|
||||
<div class="form-tips">默认为0, 数值越大越排前</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="formData.remarks"
|
||||
placeholder="请输入备注"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位状态" prop="isStop">
|
||||
<el-switch v-model="formData.isStop" :active-value="0" :inactive-value="1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</popup>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { postEdit, postAdd, postDetail } from '@/api/org/post'
|
||||
import Popup from '@/components/popup/index.vue'
|
||||
import feedback from '@/utils/feedback'
|
||||
const emit = defineEmits(['success', 'close'])
|
||||
const formRef = shallowRef<FormInstance>()
|
||||
const popupRef = shallowRef<InstanceType<typeof Popup>>()
|
||||
const mode = ref('add')
|
||||
const popupTitle = computed(() => {
|
||||
return mode.value == 'edit' ? '编辑岗位' : '新增岗位'
|
||||
})
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
code: '',
|
||||
sort: 0,
|
||||
remarks: '',
|
||||
isStop: 0
|
||||
})
|
||||
|
||||
const formRules = {
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入岗位编码',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入岗位名称',
|
||||
trigger: ['blur']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate()
|
||||
mode.value == 'edit' ? await postEdit(formData) : await postAdd(formData)
|
||||
feedback.msgSuccess('操作成功')
|
||||
popupRef.value?.close()
|
||||
emit('success')
|
||||
}
|
||||
|
||||
const open = (type = 'add') => {
|
||||
mode.value = type
|
||||
popupRef.value?.open()
|
||||
}
|
||||
|
||||
const setFormData = (data: Record<any, any>) => {
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
//@ts-ignore
|
||||
formData[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getDetail = async (row: Record<string, any>) => {
|
||||
const data = await postDetail({
|
||||
id: row.id
|
||||
})
|
||||
setFormData(data)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
setFormData,
|
||||
getDetail
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<div class="post-lists">
|
||||
<el-card class="!border-none" shadow="never">
|
||||
<el-form ref="formRef" class="mb-[-16px]" :model="queryParams" :inline="true">
|
||||
<el-form-item label="岗位编码">
|
||||
<el-input
|
||||
class="w-[280px]"
|
||||
v-model="queryParams.code"
|
||||
clearable
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位名称">
|
||||
<el-input
|
||||
class="w-[280px]"
|
||||
v-model="queryParams.name"
|
||||
clearable
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="岗位状态">
|
||||
<el-select class="w-[280px]" v-model="queryParams.isStop">
|
||||
<el-option label="全部" value />
|
||||
<el-option label="正常" :value="0" />
|
||||
<el-option label="停用" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="resetPage">查询</el-button>
|
||||
<el-button @click="resetParams">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="!border-none mt-4" shadow="never">
|
||||
<div>
|
||||
<el-button v-perms="['system:post:add']" type="primary" @click="handleAdd()">
|
||||
<template #icon>
|
||||
<icon name="el-icon-Plus" />
|
||||
</template>
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table class="mt-4" size="large" v-loading="pager.loading" :data="pager.lists">
|
||||
<el-table-column label="岗位编码" prop="code" min-width="100" />
|
||||
<el-table-column label="岗位名称" prop="name" min-width="100" />
|
||||
<el-table-column label="排序" prop="sort" min-width="100" />
|
||||
<el-table-column
|
||||
label="备注"
|
||||
prop="remarks"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="添加时间" prop="createTime" min-width="180" />
|
||||
<el-table-column label="岗位状态" prop="isStop" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag class="ml-2" :type="row.isStop ? 'danger' : ''">
|
||||
{{ row.isStop ? '停用' : '正常' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-perms="['system:post:edit']"
|
||||
type="primary"
|
||||
link
|
||||
@click="handleEdit(row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-perms="['system:post:del']"
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDelete(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="flex justify-end mt-4">
|
||||
<pagination v-model="pager" @change="getLists" />
|
||||
</div>
|
||||
</el-card>
|
||||
<edit-popup v-if="showEdit" ref="editRef" @success="getLists" @close="showEdit = false" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="post">
|
||||
import { postDelete, postLists } from '@/api/org/post'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
import feedback from '@/utils/feedback'
|
||||
import EditPopup from './edit.vue'
|
||||
const editRef = shallowRef<InstanceType<typeof EditPopup>>()
|
||||
const showEdit = ref(false)
|
||||
const queryParams = reactive({
|
||||
code: '',
|
||||
name: '',
|
||||
isStop: ''
|
||||
})
|
||||
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: postLists,
|
||||
params: queryParams
|
||||
})
|
||||
|
||||
const handleAdd = async () => {
|
||||
showEdit.value = true
|
||||
await nextTick()
|
||||
editRef.value?.open('add')
|
||||
}
|
||||
|
||||
const handleEdit = async (data: any) => {
|
||||
showEdit.value = true
|
||||
await nextTick()
|
||||
editRef.value?.open('edit')
|
||||
editRef.value?.getDetail(data)
|
||||
}
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
await feedback.confirm('确定要删除?')
|
||||
await postDelete({ id })
|
||||
feedback.msgSuccess('删除成功')
|
||||
getLists()
|
||||
}
|
||||
|
||||
getLists()
|
||||
</script>
|
|
@ -19,11 +19,12 @@
|
|||
</el-form-item>
|
||||
<el-form-item label="岗位">
|
||||
<el-checkbox-group v-model="form.position">
|
||||
<el-checkbox v-for="option in positionOptions" :key="option.value" :value="option.value" name="type">
|
||||
<el-checkbox v-for="option in positionOptions" :key="option.value" :label="option.value" name="type">
|
||||
{{ option.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<template v-if="form.position.includes(1)">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<div class="flex items-center gap-[6px] cursor-pointer">
|
||||
|
@ -52,6 +53,7 @@
|
|||
</el-select>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="账号状态">
|
||||
<el-radio-group v-model="form.accountStatus">
|
||||
<el-radio v-for="option in accountStatusOptions" :key="option.value" :label="option.value">{{ option.label }}</el-radio>
|
||||
|
@ -65,6 +67,7 @@
|
|||
import ProDialog, { type IParams } from '@/components/ProDialog/index.vue'
|
||||
const proDialogRef = ref<InstanceType<typeof ProDialog>>()
|
||||
|
||||
const emit = defineEmits(['fetchTableList'])
|
||||
const data = ref([
|
||||
{
|
||||
id: 1,
|
||||
|
@ -127,7 +130,7 @@ const form = ref({
|
|||
organization: '',
|
||||
accountName: '',
|
||||
mobile: '',
|
||||
position: '',
|
||||
position: [],
|
||||
channel: '',
|
||||
accountStatus: '',
|
||||
teacher: ''
|
||||
|
@ -140,6 +143,7 @@ const handleCancel = (callback: () => void) => {
|
|||
}
|
||||
const handleConfirm = (callback: () => void) => {
|
||||
callback()
|
||||
emit('fetchTableList')
|
||||
}
|
||||
defineExpose({
|
||||
openDialog
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialogVisible" :title="parameter.title" :width="parameter.width">
|
||||
<el-space direction="vertical" alignment="normal">
|
||||
<el-form :model="searchForm" ref="userFormRef" :inline="true" label-width="auto" @submit.native.prevent>
|
||||
<el-form-item label="账号名称">
|
||||
<el-input class="ls-input" v-model="searchForm.accountName" placeholder="请输入账号名称" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary">搜索</el-button>
|
||||
<el-button>重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<ProTable ref="proTableRef" :columns="columns" :tableData="tableData" :loading="loading" :maxHeight="530">
|
||||
<template #radio="{ row }">
|
||||
<el-radio v-model="radio" :label="row.id"> </el-radio>
|
||||
</template>
|
||||
<template #accountName="{ row }">
|
||||
<el-space>
|
||||
<span>{{ row.accountName }}</span>
|
||||
<span
|
||||
v-if="isGroupLeader(row.groupLeader)"
|
||||
class="px-[6px] text-green border border-solid border-green rounded-[4px] text-center text-xs"
|
||||
>
|
||||
组长
|
||||
</span>
|
||||
</el-space>
|
||||
</template>
|
||||
<template #accountStatus="{ row }">
|
||||
<el-switch v-model="row.accountStatus" disabled :active-value="1" :inactive-value="0" />
|
||||
</template>
|
||||
</ProTable>
|
||||
<!-- <pagination v-model="pager" @change="getLists" layout="total, prev, pager, next, jumper" class="mb-[10px]" /> -->
|
||||
</el-space>
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface IParams {
|
||||
title: string
|
||||
width?: string | number
|
||||
data: { [key: string]: any }
|
||||
}
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
// 父组件传递过来的参数
|
||||
const parameter = ref<IParams>({
|
||||
title: '',
|
||||
width: '50%',
|
||||
data: {}
|
||||
})
|
||||
const openDialog = (params: IParams) => {
|
||||
parameter.value = { ...parameter.value, ...params }
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const emit = defineEmits(['refreshList'])
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
const searchForm = ref({
|
||||
accountName: ''
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const proTableRef = ref()
|
||||
const tableData = ref<any[]>([])
|
||||
const columns = reactive([
|
||||
{ prop: 'radio', label: '单选', width: 70 },
|
||||
{ prop: 'accountName', label: '账号名称' },
|
||||
{ prop: 'mobile', label: '联系电话' },
|
||||
{ prop: 'organization', label: '所属组织' },
|
||||
{ prop: 'position', label: '岗位' },
|
||||
{ prop: 'accountStatus', label: '账号状态' }
|
||||
])
|
||||
const radio = ref()
|
||||
const isGroupLeader = computed(() => (groupLeader: number) => groupLeader == 1)
|
||||
|
||||
watch(
|
||||
() => dialogVisible.value,
|
||||
val => {
|
||||
if (val) {
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
tableData.value = [
|
||||
{
|
||||
id: 1,
|
||||
accountName: '张三',
|
||||
mobile: '18138952909',
|
||||
organization: '广州团队-A组',
|
||||
position: '电销老师',
|
||||
accountStatus: 1,
|
||||
groupLeader: 1
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
accountName: '李四',
|
||||
mobile: '18138952909',
|
||||
organization: '广州团队-A组',
|
||||
position: '电销老师',
|
||||
accountStatus: 1,
|
||||
groupLeader: 0
|
||||
}
|
||||
]
|
||||
loading.value = false
|
||||
// 如果已设置组长,则需要默认回显
|
||||
if (tableData.value.length > 0) {
|
||||
radio.value = tableData.value.find(item => item.groupLeader == 1)?.id
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
)
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
console.log(radio.value)
|
||||
dialogVisible.value = false
|
||||
emit('refreshList')
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -9,15 +9,15 @@
|
|||
<el-button type="danger" plain :icon="Delete" :disabled="isDisabled" @click="handleBatchDelete">批量删除</el-button>
|
||||
</el-space>
|
||||
<el-space>
|
||||
<el-input :placeholder="`请输入${placeholder}`">
|
||||
<el-input :placeholder="`请输入${placeholder}`" v-model="searchForm.keyword" @input="handleInputChange">
|
||||
<template #prepend>
|
||||
<el-select v-model="selectKey" :placeholder="placeholder" class="w-[100px]">
|
||||
<el-option v-for="option in searchOptions" :key="option.field" :label="option.label" :value="option.field" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-select placeholder="请选择账号状态">
|
||||
<el-option label="Restaurant" value="1" />
|
||||
<el-select placeholder="请选择账号状态" v-model="searchForm.accountStatus" clearable @change="handleSelectChange">
|
||||
<el-option v-for="option in accountStatusOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-space>
|
||||
</div>
|
||||
|
@ -44,7 +44,7 @@
|
|||
</template>
|
||||
</ProTable>
|
||||
</div>
|
||||
<account-dialog ref="accountDialogRef" />
|
||||
<account-dialog ref="accountDialogRef" @fetch-table-list="fetchTableList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -52,6 +52,7 @@ import useHandleData from '@/hooks/useHandleData'
|
|||
import feedback from '@/utils/feedback'
|
||||
import { Plus, Delete } from '@element-plus/icons-vue'
|
||||
import accountDialog from '../components/account-list/account-dialog.vue'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
defineProps({
|
||||
curOrganization: {
|
||||
|
@ -66,7 +67,10 @@ const searchOptions = shallowRef([
|
|||
{ field: 'positionName', label: '岗位名称' }
|
||||
])
|
||||
const placeholder = computed(() => searchOptions.value.find(item => item.field == selectKey.value)?.label)
|
||||
|
||||
const accountStatusOptions = ref([
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '停用', value: 2 }
|
||||
])
|
||||
const loading = ref(false)
|
||||
const proTableRef = ref()
|
||||
const tableData = ref<any[]>([])
|
||||
|
@ -158,5 +162,28 @@ const showAccountDialog = () => {
|
|||
data: {}
|
||||
})
|
||||
}
|
||||
const searchForm = ref({
|
||||
keyword: '',
|
||||
accountStatus: ''
|
||||
})
|
||||
const handleInputChange = useDebounceFn(() => {
|
||||
const formMap: Record<string, string> = {
|
||||
accountName: '',
|
||||
mobile: '',
|
||||
position: ''
|
||||
}
|
||||
formMap[selectKey.value] = searchForm.value.keyword
|
||||
}, 500)
|
||||
const handleSelectChange = () => {
|
||||
console.log('handleSelectChange')
|
||||
}
|
||||
watch(
|
||||
() => selectKey.value,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal != oldVal && searchForm.value.keyword !== '') {
|
||||
searchForm.value.keyword = ''
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<el-space direction="vertical" alignment="normal" :size="24" v-loading="loading">
|
||||
<el-descriptions v-for="item in baseInfo" :key="item.title" :title="item.title" direction="vertical" :column="4" border>
|
||||
<el-descriptions-item v-for="(itemy, indey) in item.data" :key="indey" :label="itemy.label">{{ itemy.value }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-space>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: 'baseInfo'
|
||||
}
|
||||
})
|
||||
const loading = ref(false)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
console.log('基础信息')
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
const baseInfo = ref([
|
||||
{
|
||||
title: '线索基本信息',
|
||||
data: [
|
||||
{ label: '线索提供者', value: '张三' },
|
||||
{ label: '线索来源', value: '线下名单' },
|
||||
{ label: '创建人', value: 'admin' },
|
||||
{ label: '创建时间', value: '2025-02-12 11:30:00' },
|
||||
{ label: '客户姓名', value: '韩梅梅' },
|
||||
{ label: '联系电话', value: '18866668888' },
|
||||
{
|
||||
label: '基本情况',
|
||||
value: '学生爸爸接电话,学生高三毕业,300多分,家长不清楚学生收到录取通知,家长说学生不读书了,我让家长先问问学生对未来的规划先和学生沟通一下,家长同意我们加他微信发专业资料给他看看,可以在微信上问问学生具体情况。推荐3+2,给家长发一下学校简介和专业资料。'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '电销信息',
|
||||
data: [
|
||||
{ label: '负责人', value: '张三' },
|
||||
{ label: '所属组织', value: '广州团队-A组' },
|
||||
{ label: '线索状态', value: '已添加' },
|
||||
{ label: '跟进时间', value: '2025-02-12 11:30:00' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '招生信息',
|
||||
data: [
|
||||
{ label: '负责人', value: '张三' },
|
||||
{ label: '所属组织', value: '广州团队-A组' },
|
||||
{ label: '领取时间', value: '2025-02-12 11:19:00' },
|
||||
{ label: '状态', value: '账号已添加' },
|
||||
{ label: '是否转化成功', value: '' },
|
||||
{ label: '备注', value: '' },
|
||||
{ label: '成交时间', value: '' }
|
||||
]
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<el-timeline class="px-[6px]" v-loading="loading">
|
||||
<el-timeline-item v-for="(item, index) in recordList" :key="index" type="primary" color="#0E66FB">
|
||||
<el-space direction="vertical" alignment="normal">
|
||||
<div class="text-primary">{{ item.date }}</div>
|
||||
<div>
|
||||
{{ item.user }}
|
||||
<span class="text-black2">「{{ item.step }}」</span>
|
||||
</div>
|
||||
<div v-if="item.content" class="bg-primary-light-10 w-full p-[12px] rounded-[6px] flex flex-col gap-[6px]">
|
||||
<span v-for="(value, key) in item.content" :key="key">{{ key }}:{{ value }}</span>
|
||||
</div>
|
||||
</el-space>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: 'clueRecord'
|
||||
}
|
||||
})
|
||||
const loading = ref(false)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
console.log('线索来源')
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 500)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const recordList = ref([
|
||||
{
|
||||
date: '2025-02-12 15:47:00',
|
||||
user: '电销老师:张三',
|
||||
step: '添加跟进',
|
||||
content: {
|
||||
客户姓名: '韩梅梅',
|
||||
联系电话: '188866668888',
|
||||
基本情况:
|
||||
'学生爸爸接电话,学生高三毕业,300多分,家长不清楚学生收到录取通知,家长说学生不读书了,我让家长先问问学生对未来的规划先和学生沟通一下,家长同意我们加他微信发专业资料给他看看,可以在微信上问问学生具体情况。推荐3'
|
||||
}
|
||||
},
|
||||
{ date: '2025-02-12 15:47:00', user: '招生老师:王五', step: '领取线索', content: null },
|
||||
{
|
||||
date: '2025-02-12 15:47:00',
|
||||
user: '招生老师:王五',
|
||||
step: '添加进展',
|
||||
content: {
|
||||
状态: '账号已添加'
|
||||
}
|
||||
},
|
||||
{
|
||||
date: '2025-02-12 15:47:00',
|
||||
user: '招生老师:王五',
|
||||
step: '转化完成',
|
||||
content: {
|
||||
是否转化成功: '是',
|
||||
备注: '已交部分定位金'
|
||||
}
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-space) {
|
||||
width: 100%;
|
||||
.el-space__item {
|
||||
&:last-child {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.el-timeline-item__tail) {
|
||||
border-style: dashed;
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<search-form v-model="queryParams" @reset-page="resetPage" @reset-params="resetParams" />
|
||||
<clue-list :loading="pager.loading" :tableData="pager.lists">
|
||||
<template #operation="{ row }">
|
||||
<el-button type="primary" link @click="handleDetail(row)">详情</el-button>
|
||||
</template>
|
||||
</clue-list>
|
||||
</div>
|
||||
<clue-detail ref="clueDetailRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import searchForm from './modules/search-form.vue'
|
||||
import clueList from './modules/clue-list.vue'
|
||||
import clueDetail from './modules/clue-detail.vue'
|
||||
import { postLists } from '@/api/org/post'
|
||||
import { usePaging } from '@/hooks/usePaging'
|
||||
|
||||
const queryParams = reactive({
|
||||
name: '',
|
||||
conversionStatus: ''
|
||||
})
|
||||
|
||||
const { pager, getLists, resetPage, resetParams } = usePaging({
|
||||
fetchFun: postLists,
|
||||
params: queryParams
|
||||
})
|
||||
getLists()
|
||||
|
||||
const clueDetailRef = ref()
|
||||
const handleDetail = () => {
|
||||
clueDetailRef.value.openDrawer({
|
||||
title: '线索详情'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -0,0 +1,29 @@
|
|||
<template>
|
||||
<ProDrawer ref="proDrawerRef">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name">
|
||||
<component v-if="tab.name == activeTab" :is="tab.component" v-model="activeTab" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ProDrawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ProDrawer, { type IParams } from '@/components/ProDrawer/index.vue'
|
||||
import baseIfno from '../components/clue-detail/base-info.vue'
|
||||
import clueRecord from '../components/clue-detail/clue-record.vue'
|
||||
|
||||
const proDrawerRef = ref<InstanceType<typeof ProDrawer>>()
|
||||
const activeTab = ref('baseInfo')
|
||||
const tabs = shallowRef([
|
||||
{ label: '基础信息', name: 'baseInfo', component: baseIfno },
|
||||
{ label: '线索来源', name: 'clueRecord', component: clueRecord }
|
||||
])
|
||||
const openDrawer = (params: IParams) => {
|
||||
proDrawerRef.value?.openDrawer(params)
|
||||
}
|
||||
defineExpose({
|
||||
openDrawer
|
||||
})
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<el-card shadow="never" class="!border-none mt-4">
|
||||
<ProTable ref="proTableRef" :columns="columns" :tableData="tableData" :loading="loading" :maxHeight="530">
|
||||
<template #operation="{ row }">
|
||||
<slot name="operation" :row="row" />
|
||||
</template>
|
||||
</ProTable>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const proTableRef = ref()
|
||||
const columns = reactive([
|
||||
{ prop: 'accountName', label: '学生名字', width: 180 },
|
||||
{ prop: 'mobile', label: '联系电话', width: 180 },
|
||||
{ prop: 'organization', label: '电销老师', width: 180 },
|
||||
{ prop: 'position', label: '转化老师', width: 180 },
|
||||
{ prop: 'position', label: '名单来源', width: 180 },
|
||||
{ prop: 'position', label: '转化情况', width: 180 },
|
||||
{ prop: 'accountStatus', label: '创建人', width: 180 },
|
||||
{ prop: 'operation', label: '操作', fixed: 'right', width: 250 }
|
||||
])
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<el-card shadow="never" class="!border-none">
|
||||
<el-form ref="formRef" class="mb-[-16px]" :model="modelValue" :inline="true">
|
||||
<el-form-item label="学生名字">
|
||||
<el-input class="w-[280px]" placeholder="请输入" v-model="modelValue.name" clearable @keyup.enter="$emit('resetPage')" />
|
||||
</el-form-item>
|
||||
<el-form-item label="转化情况">
|
||||
<el-select class="w-[280px]" v-model="modelValue.conversionStatus">
|
||||
<el-option v-for="option in conversionOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="$emit('resetPage')">查询</el-button>
|
||||
<el-button @click="$emit('resetParams')">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
name: '',
|
||||
conversionStatus: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
defineEmits(['resetPage', 'resetParams'])
|
||||
const conversionOptions = ref([
|
||||
{ label: '有意向', value: 1 },
|
||||
{ label: '待领取', value: 2 },
|
||||
{ label: '转化中', value: 3 },
|
||||
{ label: '已添加', value: 4 },
|
||||
{ label: '异常待处理', value: 5 },
|
||||
{ label: '已成交', value: 6 },
|
||||
{ label: '已战败', value: 7 }
|
||||
])
|
||||
</script>
|
||||
<style scoped></style>
|
|
@ -35,7 +35,8 @@ module.exports = {
|
|||
'fill-light': 'var(--el-fill-color-light)',
|
||||
'fill-lighter': 'var(--el-fill-color-lighter)',
|
||||
mask: 'var(--el-mask-color)',
|
||||
green: 'var(--color-green)'
|
||||
green: 'var(--color-green)',
|
||||
black2: 'var(--el-text-color-black2)'
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['PingFang SC', 'Arial', 'Hiragino Sans GB', 'Microsoft YaHei', 'sans-serif']
|
||||
|
|
Loading…
Reference in New Issue