mirror of
https://github.com/yudaocode/yudao-ui-admin-vue3.git
synced 2026-05-10 01:03:28 +00:00
Merge remote-tracking branch 'yudao/master'
This commit is contained in:
@@ -193,10 +193,10 @@ const loginData = reactive({
|
||||
})
|
||||
|
||||
const socialList = [
|
||||
{ icon: 'ant-design:github-filled', type: 0 },
|
||||
{ icon: 'ant-design:wechat-filled', type: 30 },
|
||||
{ icon: 'ant-design:alipay-circle-filled', type: 0 },
|
||||
{ icon: 'ant-design:dingtalk-circle-filled', type: 20 }
|
||||
{ icon: 'ant-design:dingtalk-circle-filled', type: 20 },
|
||||
{ icon: 'ant-design:github-filled', type: 0 },
|
||||
{ icon: 'ant-design:alipay-circle-filled', type: 0 }
|
||||
]
|
||||
|
||||
// 获取验证码
|
||||
@@ -210,7 +210,7 @@ const getCode = async () => {
|
||||
verify.value.show()
|
||||
}
|
||||
}
|
||||
//获取租户ID
|
||||
// 获取租户 ID
|
||||
const getTenantId = async () => {
|
||||
if (loginData.tenantEnable === 'true') {
|
||||
const res = await LoginApi.getTenantIdByName(loginData.loginForm.tenantName)
|
||||
@@ -230,6 +230,15 @@ const getCookie = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 根据域名,获得租户信息
|
||||
const getTenantByWebsite = async () => {
|
||||
const website = location.host
|
||||
const res = await LoginApi.getTenantByWebsite(website)
|
||||
if (res) {
|
||||
loginData.loginForm.tenantName = res.name
|
||||
authUtil.setTenantId(res.id)
|
||||
}
|
||||
}
|
||||
const loading = ref() // ElLoading.service 返回的实例
|
||||
// 登录
|
||||
const handleLogin = async (params) => {
|
||||
@@ -278,10 +287,15 @@ const doSocialLogin = async (type: number) => {
|
||||
} else {
|
||||
loginLoading.value = true
|
||||
if (loginData.tenantEnable === 'true') {
|
||||
await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => {
|
||||
const res = await LoginApi.getTenantIdByName(value)
|
||||
authUtil.setTenantId(res)
|
||||
})
|
||||
// 尝试先通过 tenantName 获取租户
|
||||
await getTenantId()
|
||||
// 如果获取不到,则需要弹出提示,进行处理
|
||||
if (!authUtil.getTenantId()) {
|
||||
await message.prompt('请输入租户名称', t('common.reminder')).then(async ({ value }) => {
|
||||
const res = await LoginApi.getTenantIdByName(value)
|
||||
authUtil.setTenantId(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 计算 redirectUri
|
||||
// tricky: type、redirect需要先encode一次,否则钉钉回调会丢失。
|
||||
@@ -307,6 +321,7 @@ watch(
|
||||
)
|
||||
onMounted(() => {
|
||||
getCookie()
|
||||
getTenantByWebsite()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ const bindSocial = () => {
|
||||
socialBind(type, code, state).then(() => {
|
||||
message.success('绑定成功')
|
||||
emit('update:activeName', 'userSocial')
|
||||
initSocial()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<el-form-item label="线索名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入线索名称" />
|
||||
</el-form-item>
|
||||
<!-- TODO 客户选择 -->
|
||||
<!-- TODO wanwan 客户选择 -->
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请选择客户" />
|
||||
</el-form-item>
|
||||
@@ -31,7 +31,7 @@
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入地址" />
|
||||
</el-form-item>
|
||||
<!-- TODO 负责人选择 -->
|
||||
<!-- TODO wanwan 负责人选择 -->
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-input v-model="formData.ownerUserId" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
@@ -46,7 +46,6 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
|
||||
import * as ClueApi from '@/api/crm/clue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
|
||||
115
src/views/crm/components/CrmPermissionForm.vue
Normal file
115
src/views/crm/components/CrmPermissionForm.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="30%">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item v-if="formType === 'create'" label="选择人员" prop="userId">
|
||||
<el-select v-model="formData.userId">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="权限级别" prop="level">
|
||||
<el-radio-group v-model="formData.level">
|
||||
<!-- TODO @puhui999:搞个字典配置?然后这里 remove 掉负责人 -->
|
||||
<el-radio :label="CrmPermissionLevelEnum.READ">只读</el-radio>
|
||||
<el-radio :label="CrmPermissionLevelEnum.WRITE">读写</el-radio>
|
||||
</el-radio-group>
|
||||
</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 lang="ts" setup>
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as PermissionApi from '@/api/crm/permission'
|
||||
import { CrmPermissionLevelEnum } from './index'
|
||||
|
||||
defineOptions({ name: 'CrmPermissionForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const formData = ref<PermissionApi.PermissionVO & { ids?: number[] }>({
|
||||
userId: undefined, // 用户编号
|
||||
bizType: undefined, // Crm 类型
|
||||
bizId: undefined, // Crm 类型数据编号
|
||||
level: undefined // 权限级别
|
||||
})
|
||||
const formRules = reactive({
|
||||
userId: [{ required: true, message: '人员不能为空', trigger: 'blur' }],
|
||||
level: [{ required: true, message: '权限级别不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: 'create' | 'update', bizType: number, bizId: number, ids?: number[]) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type) + '团队成员'
|
||||
formType.value = type
|
||||
resetForm(bizType, bizId)
|
||||
// 修改时,设置数据
|
||||
if (ids) {
|
||||
formData.value.ids = ids
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value
|
||||
if (formType.value === 'create') {
|
||||
await PermissionApi.createPermission(unref(data))
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await PermissionApi.updatePermission(unref(data))
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = (bizType: number, bizId: number) => {
|
||||
formRef.value?.resetFields()
|
||||
formData.value = {
|
||||
userId: undefined, // 用户编号
|
||||
bizType, // Crm 类型
|
||||
bizId, // Crm 类型数据编号
|
||||
level: undefined // 权限级别
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
// 获得用户列表
|
||||
// TODO 芋艿:用户列表的选择组件
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
156
src/views/crm/components/CrmTeamList.vue
Normal file
156
src/views/crm/components/CrmTeamList.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<!-- 操作栏 -->
|
||||
<el-row justify="end">
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-button @click="handleEdit">
|
||||
<Icon class="mr-5px" icon="ep:edit" />
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button @click="handleRemove">
|
||||
<Icon class="mr-5px" icon="ep:delete" />
|
||||
移除
|
||||
</el-button>
|
||||
<el-button type="danger" @click="handleQuit"> 退出团队</el-button>
|
||||
</el-row>
|
||||
<!-- 团队成员展示 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:show-overflow-tooltip="true"
|
||||
:stripe="true"
|
||||
class="mt-20px"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column align="center" label="姓名" prop="nickname" />
|
||||
<el-table-column align="center" label="部门" prop="deptName" />
|
||||
<el-table-column align="center" label="岗位" prop="postNames" />
|
||||
<el-table-column align="center" label="权限级别" prop="level">
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ getLevelName(row.level) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :formatter="dateFormatter" align="center" label="加入时间" prop="createTime" />
|
||||
</el-table>
|
||||
<CrmPermissionForm ref="crmPermissionFormRef" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// TODO @puhui999:改成 CrmPermissionList
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { ElTable } from 'element-plus'
|
||||
import * as PermissionApi from '@/api/crm/permission'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import CrmPermissionForm from './CrmPermissionForm.vue'
|
||||
import { CrmPermissionLevelEnum } from './index'
|
||||
|
||||
defineOptions({ name: 'CrmTeam' })
|
||||
|
||||
const message = useMessage() // 消息
|
||||
|
||||
const props = defineProps<{
|
||||
bizType: number
|
||||
bizId: number
|
||||
}>()
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<PermissionApi.PermissionVO[]>([
|
||||
// TODO 测试数据
|
||||
{
|
||||
id: 1, // 数据权限编号
|
||||
userId: 1, // 用户编号
|
||||
bizType: 1, // Crm 类型
|
||||
bizId: 1, // Crm 类型数据编号
|
||||
level: 1, // 权限级别
|
||||
deptName: '研发部门', // 部门名称
|
||||
nickname: '芋道源码', // 用户昵称
|
||||
postNames: '全栈开发工程师', // 岗位名称数组
|
||||
createTime: new Date()
|
||||
}
|
||||
]) // 列表的数据
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await PermissionApi.getPermissionList({
|
||||
bizType: props.bizType,
|
||||
bizId: props.bizId
|
||||
})
|
||||
list.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @puhui999:字典格式化
|
||||
/**
|
||||
* 获得权限级别名称
|
||||
* @param level 权限级别
|
||||
*/
|
||||
const getLevelName = computed(() => (level: number) => {
|
||||
switch (level) {
|
||||
case CrmPermissionLevelEnum.OWNER:
|
||||
return '负责人'
|
||||
case CrmPermissionLevelEnum.READ:
|
||||
return '只读'
|
||||
case CrmPermissionLevelEnum.WRITE:
|
||||
return '读写'
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
// TODO @puhui999:空行稍微注意下哈;一些注释补齐下;
|
||||
const multipleSelection = ref<PermissionApi.PermissionVO[]>([])
|
||||
const handleSelectionChange = (val: PermissionApi.PermissionVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
// TODO @puhui999:一些变量命名,看看有没可能跟列表界面的 index.vue 保持他统一的风格;
|
||||
const crmPermissionFormRef = ref<InstanceType<typeof CrmPermissionForm>>()
|
||||
const handleEdit = () => {
|
||||
if (multipleSelection.value?.length === 0) {
|
||||
message.warning('请先选择团队成员后操作!')
|
||||
return
|
||||
}
|
||||
const ids = multipleSelection.value?.map((item) => item.id)
|
||||
crmPermissionFormRef.value?.open('update', props.bizType, props.bizId, ids)
|
||||
}
|
||||
const handleRemove = async () => {
|
||||
if (multipleSelection.value?.length === 0) {
|
||||
message.warning('请先选择团队成员后操作!')
|
||||
return
|
||||
}
|
||||
await message.delConfirm()
|
||||
const ids = multipleSelection.value?.map((item) => item.id)
|
||||
await PermissionApi.deletePermission({
|
||||
bizType: props.bizType,
|
||||
bizId: props.bizId,
|
||||
ids
|
||||
})
|
||||
}
|
||||
const handleAdd = () => {
|
||||
crmPermissionFormRef.value?.open('create', props.bizType, props.bizId)
|
||||
}
|
||||
|
||||
const userStore = useUserStoreWithOut()
|
||||
const handleQuit = async () => {
|
||||
const permission = list.value.find(
|
||||
(item) => item.userId === userStore.getUser.id && item.level === CrmPermissionLevelEnum.OWNER
|
||||
)
|
||||
if (permission) {
|
||||
message.warning('负责人不能退出团队!')
|
||||
return
|
||||
}
|
||||
const userPermission = list.value.find((item) => item.userId === userStore.getUser.id)
|
||||
await PermissionApi.quitTeam(userPermission?.id)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.bizId,
|
||||
() => {
|
||||
getList()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
</script>
|
||||
17
src/views/crm/components/index.ts
Normal file
17
src/views/crm/components/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import CrmTeam from './CrmTeamList.vue'
|
||||
|
||||
enum CrmBizTypeEnum {
|
||||
CRM_LEADS = 1, // 线索
|
||||
CRM_CUSTOMER = 2, // 客户
|
||||
CRM_CONTACTS = 3, // 联系人
|
||||
CRM_BUSINESS = 5, // 商机
|
||||
CRM_CONTRACT = 6 // 合同
|
||||
}
|
||||
|
||||
enum CrmPermissionLevelEnum {
|
||||
OWNER = 1, // 负责人
|
||||
READ = 2, // 读
|
||||
WRITE = 3 // 写
|
||||
}
|
||||
|
||||
export { CrmTeam, CrmBizTypeEnum, CrmPermissionLevelEnum }
|
||||
353
src/views/crm/contact/ContactForm.vue
Normal file
353
src/views/crm/contact/ContactForm.vue
Normal file
@@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" :width="800">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="130px"
|
||||
v-loading="formLoading"
|
||||
:inline="true"
|
||||
>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="ownerUserList"
|
||||
placeholder="请选择负责人"
|
||||
multiple
|
||||
value-key="id"
|
||||
lable-key="nickname"
|
||||
@click="openOwerForm('open')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in ownerUserList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- TODO 芋艿:封装成一个组件 -->
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
:teleported="false"
|
||||
:visible="showCustomer"
|
||||
:offset="10"
|
||||
>
|
||||
<template #reference>
|
||||
<el-input
|
||||
placeholder="请选择"
|
||||
@click="openCustomerSelect"
|
||||
v-model="formData.customerName"
|
||||
/>
|
||||
</template>
|
||||
<el-table :data="list" ref="multipleTableRef" @select="handleSelectionChange">
|
||||
<el-table-column label="选择" type="selection" width="55" />
|
||||
<el-table-column width="100" property="id" label="编号" />
|
||||
<el-table-column width="150" property="name" label="客户名称" />
|
||||
<el-table-column label="客户来源" align="center" prop="source" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户等级" align="center" prop="level" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
layout="sizes, prev, pager, next"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="10" :offset="13">
|
||||
<el-button @click="selectCustomer">确认</el-button>
|
||||
<el-button @click="showCustomer = false">取消</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="formData.sex" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input v-model="formData.mobile" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="座机" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入座机" style="width: 215px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="formData.qq" placeholder="请输入QQ" style="width: 215px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="微信" prop="webchat">
|
||||
<el-input v-model="formData.webchat" placeholder="请输入微信" />
|
||||
</el-form-item>
|
||||
<el-form-item label="下次联系时间" prop="nextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.nextTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择下次联系时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="直属上级" prop="parentId">
|
||||
<el-select v-model="formData.parentId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in allContactList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:disabled="item.id == formData.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="职位" prop="post">
|
||||
<el-input v-model="formData.post" placeholder="请输入职位" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否关键决策人" prop="policyMakers" style="width: 400px">
|
||||
<el-radio-group v-model="formData.policyMakers">
|
||||
<el-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<OwerSelect
|
||||
ref="owerRef"
|
||||
@confirmOwerSelect="owerSelectValue"
|
||||
:initOwerUser="formData.ownerUserId"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import { DICT_TYPE, getIntDictOptions, getBoolDictOptions } from '@/utils/dict'
|
||||
import OwerSelect from './OwerSelect.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { ElTable } from 'element-plus'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
nextTime: undefined,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
customerName: undefined,
|
||||
address: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
post: undefined,
|
||||
qq: undefined,
|
||||
webchat: undefined,
|
||||
sex: undefined,
|
||||
policyMakers: undefined
|
||||
})
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
mobile: null,
|
||||
industryId: null,
|
||||
level: null,
|
||||
source: null
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
||||
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const ownerUserList = ref<any[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
allContactList.value = await ContactApi.simpleAlllist()
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ContactApi.getContact(id)
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
await gotOwnerUser(formData.value.ownerUserId)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerApi.getCustomerPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const gotOwnerUser = (owerUserId: any) => {
|
||||
if (owerUserId !== null) {
|
||||
owerUserId.split(',').forEach((item: string) => {
|
||||
userList.value.find((user: { id: any }) => {
|
||||
if (user.id == item) {
|
||||
ownerUserList.value.push(user)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
owerSelectValue(ownerUserList)
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ContactApi.ContactVO
|
||||
if (formType.value === 'create') {
|
||||
await ContactApi.createContact(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ContactApi.updateContact(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
nextTime: undefined,
|
||||
mobile: undefined,
|
||||
telephone: undefined,
|
||||
email: undefined,
|
||||
customerId: undefined,
|
||||
address: undefined,
|
||||
remark: undefined,
|
||||
ownerUserId: undefined,
|
||||
lastTime: undefined,
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
name: undefined,
|
||||
post: undefined,
|
||||
qq: undefined,
|
||||
webchat: undefined,
|
||||
sex: undefined,
|
||||
policyMakers: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
ownerUserList.value = []
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
// TODO @zyna:owner?拼写要注意哈;
|
||||
const owerRef = ref()
|
||||
const openOwerForm = (type: string) => {
|
||||
owerRef.value.open(type, ownerUserList.value)
|
||||
}
|
||||
const owerSelectValue = (value) => {
|
||||
ownerUserList.value = value.value
|
||||
formData.value.ownerUserId = undefined
|
||||
value.value.forEach((item, index) => {
|
||||
if (index != 0) {
|
||||
formData.value.ownerUserId = formData.value.ownerUserId + ',' + item.id
|
||||
} else {
|
||||
formData.value.ownerUserId = item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
// 选择客户
|
||||
const showCustomer = ref(false)
|
||||
const openCustomerSelect = () => {
|
||||
showCustomer.value = !showCustomer.value
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||
const multipleSelection = ref()
|
||||
const handleSelectionChange = ({}, row) => {
|
||||
multipleSelection.value = row
|
||||
multipleTableRef.value!.clearSelection()
|
||||
multipleTableRef.value!.toggleRowSelection(row, undefined)
|
||||
}
|
||||
const selectCustomer = () => {
|
||||
formData.value.customerId = multipleSelection.value.id
|
||||
formData.value.customerName = multipleSelection.value.name
|
||||
showCustomer.value = !showCustomer.value
|
||||
}
|
||||
const allContactList = ref([]) // 所有联系人列表
|
||||
onMounted(async () => {
|
||||
allContactList.value = await ContactApi.simpleAlllist()
|
||||
})
|
||||
</script>
|
||||
72
src/views/crm/contact/OwerSelect.vue
Normal file
72
src/views/crm/contact/OwerSelect.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
|
||||
<el-transfer
|
||||
v-model="value"
|
||||
:data="data"
|
||||
:titles="transferTitles"
|
||||
:props="transferDataProp"
|
||||
:right-default-checked="[1]"
|
||||
/>
|
||||
<el-row justify="end">
|
||||
<el-col :span="4">
|
||||
<el-button type="primary" @click="confirmOwerSelect">确认</el-button>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="primary" @click="confirmOwerSelect">取消</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO 芋艿:统一选择框。
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { parseBigInt } from 'jsencrypt/lib/lib/jsbn/jsbn'
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('选择') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('')
|
||||
const transferTitles = ref(['待选择', '已选择'])
|
||||
const transferDataProp = ref({
|
||||
key: 'id',
|
||||
label: 'nickname'
|
||||
})
|
||||
const emit = defineEmits(['confirmOwerSelect'])
|
||||
const data = ref<UserApi.UserVO[]>([])
|
||||
const value = ref<any[]>([])
|
||||
const rightDefaultChecked = ref<any[]>([])
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, ownerUserList: any[]) => {
|
||||
dialogVisible.value = true
|
||||
formType.value = type
|
||||
// 修改时,设置数据
|
||||
if (ownerUserList) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
ownerUserList.forEach((item) => {
|
||||
value.value.push(item.id)
|
||||
})
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
rightDefaultChecked.value = []
|
||||
data.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
const confirmOwerSelect = () => {
|
||||
const returnData = ref<any[]>([])
|
||||
data.value.forEach((item) => {
|
||||
if (value.value.indexOf(item.id) > -1) {
|
||||
returnData.value.push(item)
|
||||
}
|
||||
})
|
||||
emit('confirmOwerSelect', returnData)
|
||||
dialogVisible.value = false
|
||||
value.value = []
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.el-row {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
24
src/views/crm/contact/detail/ContactBasicInfo.vue
Normal file
24
src/views/crm/contact/detail/ContactBasicInfo.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
* @Author: zyna
|
||||
* @Date: 2023-11-11 14:50:11
|
||||
* @LastEditTime: 2023-11-11 14:52:47
|
||||
* @FilePath: \yudao-ui-admin-vue3\src\views\crm\contact\detail\ContactBasicInfo.vue
|
||||
* @Description:
|
||||
-->
|
||||
<template>
|
||||
<el-col>
|
||||
<el-row>
|
||||
<span class="text-xl font-bold">{{ contact.name }}</span>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col class="mt-10px">
|
||||
<!-- TODO 标签 -->
|
||||
<!-- <Icon icon="ant-design:tag-filled" />-->
|
||||
</el-col>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO 芋艿:后面在 review 么?
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
|
||||
const { contact } = defineProps<{ contact: ContactApi.ContactVO }>()
|
||||
</script>
|
||||
94
src/views/crm/contact/detail/ContactDetails.vue
Normal file
94
src/views/crm/contact/detail/ContactDetails.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="姓名">
|
||||
{{ contact.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ contact.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">
|
||||
{{ contact.mobile }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="座机">
|
||||
{{ contact.telephone }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">
|
||||
{{ contact.email }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">
|
||||
{{ contact.qq }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">
|
||||
{{ contact.webchat }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="详细地址">
|
||||
{{ contact.address }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{ contact.nextTime ? formatDate(contact.nextTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="contact.sex" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">
|
||||
{{ contact.remark }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ gotOwnerUser(contact.ownerUserId) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ contact.creatorName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ contact.createTime ? formatDate(contact.createTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ contact.updateTime ? formatDate(contact.updateTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// TODO 芋艿:后面在 review 么?
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
const { contact } = defineProps<{ contact: ContactApi.ContactVO }>()
|
||||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||
const gotOwnerUser = (owerUserId: string) => {
|
||||
let ownerName = ''
|
||||
if (owerUserId !== null && owerUserId != undefined) {
|
||||
owerUserId.split(',').forEach((item: string, index: number) => {
|
||||
if (index != 0) {
|
||||
ownerName =
|
||||
ownerName + ',' + userList.value.find((user: { id: any }) => user.id == item)?.nickname
|
||||
} else {
|
||||
ownerName = userList.value.find((user: { id: any }) => user.id == item)?.nickname || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
return ownerName
|
||||
}
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
149
src/views/crm/contact/detail/index.vue
Normal file
149
src/views/crm/contact/detail/index.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<!-- TODO 芋艿:要不要把 3 到 62 合并成一个组件 -->
|
||||
<div v-loading="loading">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<!-- 左上:客户基本信息 -->
|
||||
<ContactBasicInfo :contact="contact" />
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<el-button @click="openForm('update', contact.id)" v-hasPermi="['crm:contact:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-row class="mt-10px">
|
||||
<el-button>
|
||||
<Icon icon="ph:calendar-fill" class="mr-5px" />
|
||||
创建任务
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="carbon:email" class="mr-5px" />
|
||||
发送邮件
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="system-uicons:contacts" class="mr-5px" />
|
||||
创建联系人
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="ep:opportunity" class="mr-5px" />
|
||||
创建商机
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="clarity:contract-line" class="mr-5px" />
|
||||
创建合同
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="icon-park:income-one" class="mr-5px" />
|
||||
创建回款
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="fluent:people-team-add-20-filled" class="mr-5px" />
|
||||
添加团队成员
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ contact.customerName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">
|
||||
{{ contact.post }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">
|
||||
{{ contact.mobile }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ contact.createTime ? formatDate(contact.createTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
<!-- TODO wanwan:这个 tab 拉满哈,可以更好看; -->
|
||||
<el-col :span="18">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="详细资料">
|
||||
<!-- TODO wanwan:这个 ml-2 是不是可以优化下,不要整个左移,而是里面的内容有个几 px 的偏移,不顶在框里 -->
|
||||
<ContactDetails class="ml-2" :contact="contact" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="活动" lazy> 活动</el-tab-pane>
|
||||
<el-tab-pane label="邮件" lazy> 邮件</el-tab-pane>
|
||||
<el-tab-pane label="工商信息" lazy> 工商信息</el-tab-pane>
|
||||
<!-- TODO wanwan 以下标签上的数量需要接口统计返回 -->
|
||||
<el-tab-pane label="客户" lazy>
|
||||
<template #label> 客户<el-badge :value="12" class="item" type="primary" /> </template>
|
||||
客户
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="团队成员" lazy>
|
||||
<template #label> 团队成员<el-badge :value="2" class="item" type="primary" /> </template>
|
||||
团队成员
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="商机" lazy> 商机</el-tab-pane>
|
||||
<el-tab-pane label="合同" lazy>
|
||||
<template #label> 合同<el-badge :value="3" class="item" type="primary" /> </template>
|
||||
合同
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回款" lazy>
|
||||
<template #label> 回款<el-badge :value="4" class="item" type="primary" /> </template>
|
||||
回款
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回访" lazy> 回访</el-tab-pane>
|
||||
<el-tab-pane label="发票" lazy> 发票</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ContactForm ref="formRef" @success="getContactData(id)" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactBasicInfo from '@/views/crm/contact/detail/ContactBasicInfo.vue'
|
||||
import ContactDetails from '@/views/crm/contact/detail/ContactDetails.vue'
|
||||
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
// TODO 芋艿:后面在 review 么?
|
||||
|
||||
defineOptions({ name: 'ContactDetail' })
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const route = useRoute()
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const id = Number(route.params.id)
|
||||
const loading = ref(true) // 加载中
|
||||
// 联系人详情
|
||||
const contact = ref<ContactApi.ContactVO>({} as ContactApi.ContactVO)
|
||||
/**
|
||||
* 获取详情
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
const getContactData = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
contact.value = await ContactApi.getContact(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
onMounted(async () => {
|
||||
if (!id) {
|
||||
ElMessage.warning('参数错误,联系人不能为空!')
|
||||
delView(unref(currentRoute))
|
||||
return
|
||||
}
|
||||
await getContactData(id)
|
||||
})
|
||||
</script>
|
||||
339
src/views/crm/contact/index.vue
Normal file
339
src/views/crm/contact/index.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<!-- TODO zyna:筛选项,按照需求简化下 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户编号" prop="customerId">
|
||||
<el-input
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="座机" prop="telephone">
|
||||
<el-input
|
||||
v-model="queryParams.telephone"
|
||||
placeholder="请输入电话"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input
|
||||
v-model="queryParams.qq"
|
||||
placeholder="请输入QQ"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信" prop="webchat">
|
||||
<el-input
|
||||
v-model="queryParams.webchat"
|
||||
placeholder="请输入微信"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电子邮箱" prop="email">
|
||||
<el-input
|
||||
v-model="queryParams.email"
|
||||
placeholder="请输入电子邮箱"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:contact:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:contact:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||
<template #default="scope">
|
||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">{{
|
||||
scope.row.name
|
||||
}}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户名称" fixed="left" align="center" prop="customerName" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="职位" align="center" prop="post" />
|
||||
<el-table-column label="是否关键决策人" align="center" prop="policyMakers">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.policyMakers" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="直属上级" align="center" prop="parentId">
|
||||
<template #default="scope">
|
||||
{{ allContactList.find((contact) => contact.id === scope.row.parentId)?.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||
<el-table-column label="座机" align="center" prop="telephone" />
|
||||
<el-table-column label="QQ" align="center" prop="qq" />
|
||||
<el-table-column label="微信" align="center" prop="webchat" />
|
||||
<el-table-column label="邮箱" align="center" prop="email" />
|
||||
<el-table-column label="地址" align="center" prop="address" />
|
||||
<el-table-column
|
||||
label="下次联系时间"
|
||||
align="center"
|
||||
prop="nextTime"
|
||||
width="180px"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="最后跟进时间"
|
||||
align="center"
|
||||
prop="lastTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId">
|
||||
<template #default="scope">
|
||||
{{ gotOwnerUser(scope.row.ownerUserId) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="所属部门" align="center" prop="ownerUserId" /> -->
|
||||
<el-table-column
|
||||
label="更新时间"
|
||||
align="center"
|
||||
prop="updateTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<!-- <el-table-column
|
||||
label="创建人"
|
||||
align="center"
|
||||
prop="creator"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ userList.find((user) => user.id === scope.row.creator)?.nickname }}
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
plain
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:contact:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:contact:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ContactForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ContactApi from '@/api/crm/contact'
|
||||
import ContactForm from './ContactForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
|
||||
defineOptions({ name: 'CrmContact' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
nextTime: [],
|
||||
mobile: null,
|
||||
telephone: null,
|
||||
email: null,
|
||||
customerId: null,
|
||||
address: null,
|
||||
remark: null,
|
||||
ownerUserId: null,
|
||||
createTime: [],
|
||||
lastTime: [],
|
||||
parentId: null,
|
||||
name: null,
|
||||
post: null,
|
||||
qq: null,
|
||||
webchat: null,
|
||||
sex: null,
|
||||
policyMakers: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ContactApi.getContactPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ContactApi.deleteContact(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await ContactApi.exportContact(queryParams)
|
||||
download.excel(data, '联系人.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @zyna:这个负责人的读取,放在后端好点
|
||||
const gotOwnerUser = (owerUserId: string) => {
|
||||
let ownerName = ''
|
||||
if (owerUserId !== null) {
|
||||
owerUserId.split(',').forEach((item: string, index: number) => {
|
||||
if (index != 0) {
|
||||
ownerName =
|
||||
ownerName + ',' + userList.value.find((user: { id: any }) => user.id == item)?.nickname
|
||||
} else {
|
||||
ownerName = userList.value.find((user: { id: any }) => user.id == item)?.nickname || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
return ownerName
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmContactDetail', params: { id } })
|
||||
}
|
||||
|
||||
// TODO @zyna:这个上级的读取,放在后端读取,更合适;因为可能数据量比较大
|
||||
const allContactList = ref([]) //所有联系人列表
|
||||
const allCustomerList = ref([]) //客户列表
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
allContactList.value = await ContactApi.simpleAlllist()
|
||||
})
|
||||
</script>
|
||||
@@ -166,7 +166,7 @@ import download from '@/utils/download'
|
||||
import * as ContractApi from '@/api/crm/contract'
|
||||
import ContractForm from './ContractForm.vue'
|
||||
|
||||
defineOptions({ name: 'Contract' })
|
||||
defineOptions({ name: 'CrmContract' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
@@ -8,91 +8,135 @@
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-row>
|
||||
<el-form-item label="客户名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业" prop="industryId">
|
||||
<el-select v-model="formData.industryId" placeholder="请选择所属行业">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="industryId">
|
||||
<el-select v-model="formData.industryId" placeholder="请选择所属行业">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户来源" prop="source">
|
||||
<el-select v-model="formData.source" placeholder="请选择客户来源">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="客户等级" prop="level">
|
||||
<el-select v-model="formData.level" placeholder="请选择客户等级">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="电话" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="formData.qq" placeholder="请输入QQ" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="网址" prop="website">
|
||||
<el-input v-model="formData.website" placeholder="请输入网址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所在地" prop="areaId">
|
||||
<el-tree-select
|
||||
v-model="formData.areaId"
|
||||
:data="areaList"
|
||||
:props="defaultProps"
|
||||
:render-after-expand="true"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="详细地址" prop="detailAddress">
|
||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="客户来源" prop="source">
|
||||
<el-select v-model="formData.source" placeholder="请选择客户来源">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户等级" prop="level">
|
||||
<el-select v-model="formData.level" placeholder="请选择客户等级">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input v-model="formData.mobile" placeholder="请输入手机" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话" prop="telephone">
|
||||
<el-input v-model="formData.telephone" placeholder="请输入电话" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="QQ" prop="qq">
|
||||
<el-input v-model="formData.qq" placeholder="请输入QQ" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="微信" prop="wechat">
|
||||
<el-input v-model="formData.wechat" placeholder="请输入微信" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网址" prop="website">
|
||||
<el-input v-model="formData.website" placeholder="请输入网址" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-form-item label="地区编号" prop="areaId">
|
||||
<el-input v-model="formData.areaId" placeholder="请输入地区编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" prop="detailAddress">
|
||||
<el-input v-model="formData.detailAddress" placeholder="请输入详细地址" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<!-- TODO @Wanwan 少一个负责人字段,默认先选中自己 -->
|
||||
<el-row>
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.contactNextTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择下次联系时间"
|
||||
<el-form-item v-if="formType === 'create'" label="负责人" prop="userIds" span="24">
|
||||
<el-select v-model="formData.ownerUserId">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="parseInt(item.id)"
|
||||
:label="item.nickname"
|
||||
:value="parseInt(item.id)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||
<el-date-picker
|
||||
v-model="formData.contactNextTime"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择下次联系时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="客户描述" prop="description">
|
||||
<el-input v-model="formData.description" placeholder="请输入客户描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="客户描述" prop="description">
|
||||
<el-input v-model="formData.description" placeholder="请输入客户描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
@@ -103,6 +147,10 @@
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import * as AreaApi from '@/api/system/area'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -111,6 +159,8 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const areaList = ref([]) // 地区列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
@@ -127,10 +177,12 @@ const formData = ref({
|
||||
remark: undefined,
|
||||
areaId: undefined,
|
||||
detailAddress: undefined,
|
||||
contactNextTime: undefined
|
||||
contactNextTime: undefined,
|
||||
ownerUserId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ require: true, message: '客户名称不能为空', trigger: 'blur' }]
|
||||
name: [{ required: true, message: '客户名称不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
@@ -149,6 +201,16 @@ const open = async (type: string, id?: number) => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
// 获得地区列表
|
||||
areaList.value = await AreaApi.getAreaTree()
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList()
|
||||
// 默认新建时选中自己
|
||||
if (formType.value === 'create') {
|
||||
const { wsCache } = useCache()
|
||||
const user = wsCache.get(CACHE_KEY.USER).user
|
||||
formData.value.ownerUserId = user.id
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@@ -196,7 +258,8 @@ const resetForm = () => {
|
||||
remark: undefined,
|
||||
areaId: undefined,
|
||||
detailAddress: undefined,
|
||||
contactNextTime: undefined
|
||||
contactNextTime: undefined,
|
||||
ownerUserId: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
16
src/views/crm/customer/detail/CustomerBasicInfo.vue
Normal file
16
src/views/crm/customer/detail/CustomerBasicInfo.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<el-col>
|
||||
<el-row>
|
||||
<span class="text-xl font-bold">{{ customer.name }}</span>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col class="mt-10px">
|
||||
<!-- TODO 标签 -->
|
||||
<!-- <Icon icon="ant-design:tag-filled" />-->
|
||||
</el-col>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
|
||||
const { customer } = defineProps<{ customer: CustomerApi.CustomerVO }>()
|
||||
</script>
|
||||
95
src/views/crm/customer/detail/CustomerDetails.vue
Normal file
95
src/views/crm/customer/detail/CustomerDetails.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-collapse class="" v-model="activeNames">
|
||||
<el-collapse-item name="basicInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">基本信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="4">
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ customer.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属行业">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="customer.industryId" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户来源">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户等级">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机">
|
||||
{{ customer.mobile }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="电话">
|
||||
{{ customer.telephone }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">
|
||||
{{ customer.email }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="QQ">
|
||||
{{ customer.qq }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信">
|
||||
{{ customer.wechat }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="网址">
|
||||
{{ customer.website }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所在地">
|
||||
{{ customer.areaName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="详细地址">
|
||||
{{ customer.detailAddress }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="下次联系时间">
|
||||
{{
|
||||
customer.contactNextTime ? formatDate(customer.contactNextTime, 'YYYY-MM-DD') : '空'
|
||||
}}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="最后跟进时间">
|
||||
{{ customer.contactLastTime ? formatDate(customer.contactLastTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="客户描述">
|
||||
{{ customer.description }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">
|
||||
{{ customer.remark }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="systemInfo">
|
||||
<template #title>
|
||||
<span class="text-base font-bold">系统信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ customer.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">
|
||||
{{ customer.creatorName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ customer.createTime ? formatDate(customer.createTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">
|
||||
{{ customer.updateTime ? formatDate(customer.updateTime) : '空' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
const { customer } = defineProps<{ customer: CustomerApi.CustomerVO }>()
|
||||
|
||||
// 展示的折叠面板
|
||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
151
src/views/crm/customer/detail/index.vue
Normal file
151
src/views/crm/customer/detail/index.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<!-- TODO @wanwan:要不要把上面这一整块,搞成一个组件,就是把 下面 + Details + BasitcInfo 合并成一个 -->
|
||||
<div v-loading="loading">
|
||||
<div class="flex items-start justify-between">
|
||||
<div>
|
||||
<!-- 左上:客户基本信息 -->
|
||||
<CustomerBasicInfo :customer="customer" />
|
||||
</div>
|
||||
<div>
|
||||
<!-- 右上:按钮 -->
|
||||
<el-button @click="openForm('update', customer.id)" v-hasPermi="['crm:customer:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button>更改成交状态</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-row class="mt-10px">
|
||||
<el-button>
|
||||
<Icon icon="ph:calendar-fill" class="mr-5px" />
|
||||
创建任务
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="carbon:email" class="mr-5px" />
|
||||
发送邮件
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="system-uicons:contacts" class="mr-5px" />
|
||||
创建联系人
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="ep:opportunity" class="mr-5px" />
|
||||
创建商机
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="clarity:contract-line" class="mr-5px" />
|
||||
创建合同
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="icon-park:income-one" class="mr-5px" />
|
||||
创建回款
|
||||
</el-button>
|
||||
<el-button>
|
||||
<Icon icon="fluent:people-team-add-20-filled" class="mr-5px" />
|
||||
添加团队成员
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
<ContentWrap class="mt-10px">
|
||||
<el-descriptions :column="5" direction="vertical">
|
||||
<el-descriptions-item label="客户级别">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="成交状态">
|
||||
{{ customer.dealStatus ? '已成交' : '未成交' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ customer.ownerUserName }}
|
||||
</el-descriptions-item>
|
||||
<!-- TODO wanwan 首要联系人? -->
|
||||
<el-descriptions-item label="首要联系人" />
|
||||
<!-- TODO wanwan 首要联系人电话? -->
|
||||
<el-descriptions-item label="首要联系人电话">
|
||||
{{ customer.mobile }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</ContentWrap>
|
||||
<el-col>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="详细资料">
|
||||
<CustomerDetails :customer="customer" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="活动" lazy> 活动</el-tab-pane>
|
||||
<el-tab-pane label="邮件" lazy> 邮件</el-tab-pane>
|
||||
<el-tab-pane label="工商信息" lazy> 工商信息</el-tab-pane>
|
||||
<el-tab-pane label="客户关系" lazy> 客户关系</el-tab-pane>
|
||||
<!-- TODO wanwan 以下标签上的数量需要接口统计返回 -->
|
||||
<el-tab-pane label="联系人" lazy>
|
||||
<template #label> 联系人<el-badge class="item" type="primary" /> </template>
|
||||
联系人
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="团队成员" lazy>
|
||||
<template #label> 团队成员<el-badge class="item" type="primary" /> </template>
|
||||
团队成员
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="商机" lazy> 商机</el-tab-pane>
|
||||
<el-tab-pane label="合同" lazy>
|
||||
<template #label> 合同<el-badge class="item" type="primary" /> </template>
|
||||
合同
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回款" lazy>
|
||||
<template #label> 回款<el-badge class="item" type="primary" /> </template>
|
||||
回款
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="回访" lazy> 回访</el-tab-pane>
|
||||
<el-tab-pane label="发票" lazy> 发票</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-col>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerForm ref="formRef" @success="getCustomerData(id)" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import CustomerBasicInfo from '@/views/crm/customer/detail/CustomerBasicInfo.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import CustomerDetails from '@/views/crm/customer/detail/CustomerDetails.vue'
|
||||
import CustomerForm from '@/views/crm/customer/CustomerForm.vue'
|
||||
|
||||
defineOptions({ name: 'CustomerDetail' })
|
||||
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const route = useRoute()
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const id = Number(route.params.id)
|
||||
const loading = ref(true) // 加载中
|
||||
|
||||
/**
|
||||
* 获取详情
|
||||
*
|
||||
* @param id
|
||||
*/
|
||||
const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
|
||||
const getCustomerData = async (id: number) => {
|
||||
loading.value = true
|
||||
try {
|
||||
customer.value = await CustomerApi.getCustomer(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
onMounted(() => {
|
||||
if (!id) {
|
||||
ElMessage.warning('参数错误,客户不能为空!')
|
||||
delView(unref(currentRoute))
|
||||
return
|
||||
}
|
||||
getCustomerData(id)
|
||||
})
|
||||
</script>
|
||||
@@ -2,44 +2,97 @@
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
:model="queryParams"
|
||||
class="-mb-15px"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="客户名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入客户名称"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="mobile">
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请输入手机"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属行业" prop="industryId">
|
||||
<el-select
|
||||
v-model="queryParams.industryId"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请选择所属行业"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户等级" prop="level">
|
||||
<el-select
|
||||
v-model="queryParams.level"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请选择客户等级"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户来源" prop="source">
|
||||
<el-select
|
||||
v-model="queryParams.source"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
placeholder="请选择客户来源"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:customer:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<el-button @click="handleQuery">
|
||||
<Icon class="mr-5px" icon="ep:search" />
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon class="mr-5px" icon="ep:refresh" />
|
||||
重置
|
||||
</el-button>
|
||||
<el-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')">
|
||||
<Icon class="mr-5px" icon="ep:plus" />
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:customer:export']"
|
||||
:loading="exportLoading"
|
||||
plain
|
||||
type="success"
|
||||
@click="handleExport"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
<Icon class="mr-5px" icon="ep:download" />
|
||||
导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -47,75 +100,77 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="客户名称" align="center" prop="name" width="160" />
|
||||
<el-table-column label="所属行业" align="center" prop="industryId" width="120">
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||
<el-table-column align="center" label="编号" prop="id" />
|
||||
<el-table-column align="center" label="客户名称" prop="name" width="160" />
|
||||
<el-table-column align="center" label="所属行业" prop="industryId" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="scope.row.industryId" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户来源" align="center" prop="source" width="100">
|
||||
<el-table-column align="center" label="客户来源" prop="source" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户等级" align="center" prop="level" width="120">
|
||||
<el-table-column align="center" label="客户等级" prop="level" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手机" align="center" prop="mobile" width="120" />
|
||||
<el-table-column label="详细地址" align="center" prop="detailAddress" width="200" />
|
||||
<!-- TODO @Wanwan 负责人回显,所属部门,创建人 -->
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId" />
|
||||
<el-table-column align="center" label="手机" prop="mobile" width="120" />
|
||||
<el-table-column align="center" label="详细地址" prop="detailAddress" width="200" />
|
||||
<el-table-column align="center" label="负责人" prop="ownerUserName" />
|
||||
<el-table-column align="center" label="所属部门" prop="ownerUserDept" />
|
||||
<el-table-column align="center" label="创建人" prop="creatorName" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="成交状态" align="center" prop="dealStatus">
|
||||
<el-table-column align="center" label="成交状态" prop="dealStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="下次联系时间"
|
||||
align="center"
|
||||
prop="contactNextTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="下次联系时间"
|
||||
prop="contactNextTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="最后跟进时间"
|
||||
align="center"
|
||||
prop="contactLastTime"
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="最后跟进时间"
|
||||
prop="contactLastTime"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="锁定状态" align="center" prop="lockStatus">
|
||||
<el-table-column align="center" label="锁定状态" prop="lockStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.lockStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- TODO @Wanwan 距进入公海天数 -->
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<!-- TODO @wanwan 距进入公海天数 -->
|
||||
<el-table-column align="center" fixed="right" label="操作" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openDetail(scope.row.id)">详情</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:customer:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:customer:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['crm:customer:delete']"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:customer:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
@@ -124,23 +179,26 @@
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
v-model:page="queryParams.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- TODO 方便查看效果 TODO 芋艿:先注释了,避免演示环境报错 -->
|
||||
<!-- <CrmTeam :biz-id="1" :biz-type="CrmBizTypeEnum.CRM_CUSTOMER" />-->
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as CustomerApi from '@/api/crm/customer'
|
||||
import CustomerForm from './CustomerForm.vue'
|
||||
import { CrmBizTypeEnum, CrmTeam } from '@/views/crm/components'
|
||||
|
||||
defineOptions({ name: 'CrmCustomer' })
|
||||
|
||||
@@ -154,7 +212,10 @@ const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
mobile: null
|
||||
mobile: null,
|
||||
industryId: null,
|
||||
level: null,
|
||||
source: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
@@ -183,6 +244,12 @@ const resetQuery = () => {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
const { push } = useRouter()
|
||||
const openDetail = (id: number) => {
|
||||
push({ name: 'CrmCustomerDetail', params: { id } })
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
|
||||
146
src/views/crm/customerLimitConfig/CustomerLimitConfDetails.vue
Normal file
146
src/views/crm/customerLimitConfig/CustomerLimitConfDetails.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<el-button type="primary" plain @click="handleQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 刷新
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['crm:customer-limit-config:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
class="mt-4"
|
||||
>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="规则类型" align="center" prop="type" />
|
||||
<el-table-column label="规则适用人群" align="center" prop="userNames" />
|
||||
<el-table-column label="规则适用部门" align="center" prop="deptNames" />
|
||||
<el-table-column
|
||||
:label="
|
||||
confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限'
|
||||
"
|
||||
align="center"
|
||||
prop="maxCount"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
|
||||
label="成交客户是否占用拥有客户数"
|
||||
align="center"
|
||||
prop="dealCountEnabled"
|
||||
>
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.dealCountEnabled" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:customer-limit-config:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:customer-limit-config:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerLimitConfigForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||
import CustomerLimitConfigForm from '@/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue'
|
||||
import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
defineOptions({ name: 'CustomerLimitConfDetails' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const { confType } = defineProps<{ confType: LimitConfType }>()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
type: confType
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerLimitConfigApi.getCustomerLimitConfigPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id, confType)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await CustomerLimitConfigApi.deleteCustomerLimitConfig(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
224
src/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue
Normal file
224
src/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="200px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="规则适用人群" prop="userIds">
|
||||
<el-tree-select
|
||||
v-model="formData.userIds"
|
||||
:data="userTree"
|
||||
:props="defaultProps"
|
||||
check-on-click-node
|
||||
multiple
|
||||
filterable
|
||||
node-key="id"
|
||||
placeholder="请选择规则适用人群"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="规则适用部门" prop="deptIds">
|
||||
<el-tree-select
|
||||
v-model="formData.deptIds"
|
||||
:data="deptTree"
|
||||
:props="defaultProps"
|
||||
multiple
|
||||
check-strictly
|
||||
filterable
|
||||
node-key="id"
|
||||
placeholder="请选择规则适用部门"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="
|
||||
formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT
|
||||
? '拥有客户数上限'
|
||||
: '锁定客户数上限'
|
||||
"
|
||||
prop="maxCount"
|
||||
>
|
||||
<el-input-number v-model="formData.maxCount" placeholder="请输入数量上限" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="成交客户是否占用拥有客户数"
|
||||
v-if="formData.type === LimitConfType.CUSTOMER_QUANTITY_LIMIT"
|
||||
prop="dealCountEnabled"
|
||||
>
|
||||
<el-switch v-model="formData.dealCountEnabled" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||
import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
type: undefined,
|
||||
userIds: undefined,
|
||||
deptIds: undefined,
|
||||
maxCount: undefined,
|
||||
dealCountEnabled: false
|
||||
})
|
||||
const formRules = reactive({
|
||||
type: [{ required: true, message: '规则类型不能为空', trigger: 'change' }],
|
||||
maxCount: [{ required: true, message: '数量上限不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
// TODO @芋艿:看看怎么搞个部门选择组件
|
||||
const deptTree = ref() // 部门树形结构
|
||||
const userTree = ref() // 用户树形结构
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, limitConfType?: LimitConfType) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 获得部门树
|
||||
await getDeptTree()
|
||||
// 获得用户
|
||||
await getUserTree()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await CustomerLimitConfigApi.getCustomerLimitConfig(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
} else {
|
||||
formData.value.type = limitConfType
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CustomerLimitConfigApi.CustomerLimitConfigVO
|
||||
if (formType.value === 'create') {
|
||||
await CustomerLimitConfigApi.createCustomerLimitConfig(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CustomerLimitConfigApi.updateCustomerLimitConfig(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
type: undefined,
|
||||
userIds: undefined,
|
||||
deptIds: undefined,
|
||||
maxCount: undefined,
|
||||
dealCountEnabled: false
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门树
|
||||
*/
|
||||
const getDeptTree = async () => {
|
||||
const res = await DeptApi.getSimpleDeptList()
|
||||
deptTree.value = []
|
||||
deptTree.value.push(...handleTree(res))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户树
|
||||
*/
|
||||
const getUserTree = async () => {
|
||||
const res = await UserApi.getAllUser()
|
||||
userTree.value = []
|
||||
userTree.value = cloneDeep(unref(deptTree))
|
||||
|
||||
const deptUserMap = {}
|
||||
res.forEach((user) => {
|
||||
if (user.dept) {
|
||||
if (!deptUserMap[user.deptId]) {
|
||||
deptUserMap[user.deptId] = []
|
||||
}
|
||||
deptUserMap[user.deptId].push(user)
|
||||
}
|
||||
})
|
||||
|
||||
handleUserData(userTree.value, deptUserMap)
|
||||
}
|
||||
|
||||
// TODO @芋艿:看看怎么搞个用户选择的组件
|
||||
/**
|
||||
* 处理用户树
|
||||
*
|
||||
* @param deptTree
|
||||
* @param deptUserMap
|
||||
*/
|
||||
const handleUserData = (deptTree, deptUserMap) => {
|
||||
for (let i = 0; i < deptTree.length; i++) {
|
||||
// 如果是用户,就不用继续找部门下的用户
|
||||
if (deptTree[i].isUser) {
|
||||
continue
|
||||
}
|
||||
const users = deptUserMap[deptTree[i].id]
|
||||
if (users) {
|
||||
if (!deptTree[i].children) {
|
||||
deptTree[i].children = []
|
||||
}
|
||||
deptTree[i].children.push(
|
||||
...users.map((user) => {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.username + '-' + user.nickname,
|
||||
isUser: true,
|
||||
// 用户状态为关闭
|
||||
disabled: user.status === 1
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (deptTree[i].children && deptTree[i].children.length !== 0) {
|
||||
handleUserData(deptTree[i].children, deptUserMap)
|
||||
}
|
||||
|
||||
// 非人员选项禁用
|
||||
deptTree[i].disabled = true
|
||||
// 将非人员的 id 置为空
|
||||
deptTree[i].id = 'null'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
14
src/views/crm/customerLimitConfig/customerLimitConf.ts
Normal file
14
src/views/crm/customerLimitConfig/customerLimitConf.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// TODO 可以挪到它对应的 api.ts 文件里哈
|
||||
/**
|
||||
* 客户限制配置类型
|
||||
*/
|
||||
export enum LimitConfType {
|
||||
/**
|
||||
* 拥有客户数限制
|
||||
*/
|
||||
CUSTOMER_QUANTITY_LIMIT = 1,
|
||||
/**
|
||||
* 锁定客户数限制
|
||||
*/
|
||||
CUSTOMER_LOCK_LIMIT = 2
|
||||
}
|
||||
20
src/views/crm/customerLimitConfig/index.vue
Normal file
20
src/views/crm/customerLimitConfig/index.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="拥有客户数限制">
|
||||
<!-- TODO @wanwan:CustomerLimitConfigList,因为它是列表哈 -->
|
||||
<CustomerLimitConfDetails :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="锁定客户数限制">
|
||||
<CustomerLimitConfDetails :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import CustomerLimitConfDetails from '@/views/crm/customerLimitConfig/CustomerLimitConfDetails.vue'
|
||||
import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
|
||||
|
||||
defineOptions({ name: 'CrmCustomerLimitConfig' })
|
||||
</script>
|
||||
135
src/views/crm/customerPoolConf/index.vue
Normal file
135
src/views/crm/customerPoolConf/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="160px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-card shadow="never">
|
||||
<!-- 操作 -->
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<CardTitle title="客户公海规则设置" />
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="onSubmit"
|
||||
v-hasPermi="['crm:customer-pool-config:update']"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 表单 -->
|
||||
<el-form-item label="客户公海规则设置" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled" class="ml-4">
|
||||
<el-radio :label="false" size="large">不启用</el-radio>
|
||||
<el-radio :label="true" size="large">启用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="formData.enabled">
|
||||
<el-form-item>
|
||||
<el-input-number class="mr-2" v-model="formData.contactExpireDays" />
|
||||
天不跟进或
|
||||
<el-input-number class="mx-2" v-model="formData.dealExpireDays" />
|
||||
天未成交
|
||||
</el-form-item>
|
||||
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
||||
<el-radio-group v-model="formData.notifyEnabled" class="ml-4">
|
||||
<el-radio :label="false" size="large">不提醒</el-radio>
|
||||
<el-radio :label="true" size="large">提醒</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div v-if="formData.notifyEnabled">
|
||||
<el-form-item>
|
||||
提前 <el-input-number class="mx-2" v-model="formData.notifyDays" /> 天提醒
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as CustomerPoolConfApi from '@/api/crm/customerPoolConf'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
// TODO @wanwan:CustomerPoolConf =》 CustomerPoolConfig;另外,我们在 crm 目录下,新建一个 config 目录,然后把 customerPoolConfig 和 customerLimitConfig 都挪进
|
||||
defineOptions({ name: 'CustomerPoolConf' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const formLoading = ref(false)
|
||||
const formData = ref({
|
||||
enabled: false,
|
||||
contactExpireDays: 0,
|
||||
dealExpireDays: 0,
|
||||
notifyEnabled: false,
|
||||
notifyDays: 0
|
||||
})
|
||||
const formRules = reactive({
|
||||
enabled: [{ required: true, message: '是否启用客户公海不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 获取配置 */
|
||||
const getConfig = async () => {
|
||||
try {
|
||||
formLoading.value = true
|
||||
const data = await CustomerPoolConfApi.getCustomerPoolConfig()
|
||||
if (data === null) {
|
||||
return
|
||||
}
|
||||
formData.value = data
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交配置 */
|
||||
const onSubmit = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CustomerPoolConfApi.CustomerPoolConfigVO
|
||||
await CustomerPoolConfApi.updateCustomerPoolConfig(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
await getConfig()
|
||||
formLoading.value = false
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO @wanwan:el-radio-group 选择后,触发会不会更好哈;
|
||||
watch(
|
||||
() => formData.value.enabled,
|
||||
(val: boolean) => {
|
||||
if (!val) {
|
||||
formData.value.contactExpireDays = undefined
|
||||
formData.value.dealExpireDays = undefined
|
||||
formData.value.notifyEnabled = false
|
||||
formData.value.notifyDays = undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
// TODO @wanwan:el-radio-group 选择后,触发会不会更好哈;
|
||||
watch(
|
||||
() => formData.value.notifyEnabled,
|
||||
(val: boolean) => {
|
||||
if (!val) {
|
||||
formData.value.notifyDays = undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
getConfig()
|
||||
})
|
||||
</script>
|
||||
70
src/views/crm/product/ProductDetail.vue
Normal file
70
src/views/crm/product/ProductDetail.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :max-height="500" :scroll="true" title="产品详情">
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="产品名称">
|
||||
{{ detailData.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(detailData.createTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="detailData.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品分类">
|
||||
{{ productCategoryList?.find((c) => c.id === detailData.categoryId)?.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品编码">
|
||||
{{ detailData.no }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品描述">
|
||||
{{ detailData.description }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人">
|
||||
{{ detailData.ownerUserId }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="单位">
|
||||
<dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="detailData.unit" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="价格">
|
||||
{{ fenToYuan(detailData.price) }}元
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as ProductCategoryApi from '@/api/crm/productCategory'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { getSimpleUserList, UserVO } from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'CrmProductDetail' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const detailLoading = ref(false) // 表单的加载中
|
||||
const detailData = ref() // 详情数据
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (data: ProductApi.ProductVO) => {
|
||||
dialogVisible.value = true
|
||||
// 设置数据
|
||||
detailLoading.value = true
|
||||
try {
|
||||
detailData.value = data
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
const productCategoryList = ref([]) // 产品分类树
|
||||
const userList = ref<UserVO[]>([]) // 系统用户
|
||||
|
||||
onMounted(async () => {
|
||||
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({})
|
||||
userList.value = await getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
185
src/views/crm/product/ProductForm.vue
Normal file
185
src/views/crm/product/ProductForm.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<!-- TODO @zange:改成每行两个哈; -->
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="产品名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入产品名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产品编码" prop="no">
|
||||
<el-input v-model="formData.no" placeholder="请输入产品编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-select v-model="formData.unit" class="w-1/1" placeholder="请选择单位">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_UNIT)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="价格" prop="price">
|
||||
<el-input type="number" v-model="formData.price" placeholder="请输入价格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品分类" prop="categoryId">
|
||||
<el-cascader
|
||||
v-model="formData.categoryId"
|
||||
:options="productCategoryList"
|
||||
:props="defaultProps"
|
||||
class="w-1/1"
|
||||
clearable
|
||||
placeholder="请选择产品分类"
|
||||
filterable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品描述" prop="description">
|
||||
<el-input v-model="formData.description" placeholder="请输入产品描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select
|
||||
v-model="formData.ownerUserId"
|
||||
placeholder="请选择负责人"
|
||||
:disabled="formData.id"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import * as ProductCategoryApi from '@/api/crm/productCategory'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
import { getSimpleUserList, UserVO } from '@/api/system/user'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
defineOptions({ name: 'CrmProductForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const userId = useUserStore().getUser.id // 当前登录的编号
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
no: undefined,
|
||||
unit: undefined,
|
||||
price: undefined,
|
||||
status: undefined,
|
||||
categoryId: undefined,
|
||||
description: undefined,
|
||||
ownerUserId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
|
||||
no: [{ required: true, message: '产品编码不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
|
||||
categoryId: [{ required: true, message: '产品分类ID不能为空', trigger: 'blur' }],
|
||||
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
|
||||
unit: [{ required: true, message: '单位不能为空', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '价格不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.ownerUserId = userId
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ProductApi.getProduct(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ProductApi.ProductVO
|
||||
if (formType.value === 'create') {
|
||||
await ProductApi.createProduct(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ProductApi.updateProduct(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
no: undefined,
|
||||
unit: undefined,
|
||||
price: undefined,
|
||||
status: undefined,
|
||||
categoryId: undefined,
|
||||
description: undefined,
|
||||
ownerUserId: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
const productCategoryList = ref<any[]>([]) // 产品分类树
|
||||
const userList = ref<UserVO[]>([]) // 系统用户
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await ProductCategoryApi.getProductCategoryList({})
|
||||
productCategoryList.value = handleTree(data, 'id', 'parentId')
|
||||
userList.value = await getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
269
src/views/crm/product/index.vue
Normal file
269
src/views/crm/product/index.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="产品名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入产品名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品编码" prop="no">
|
||||
<el-input
|
||||
v-model="queryParams.no"
|
||||
placeholder="请输入产品编码"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品分类" prop="categoryId">
|
||||
<el-input
|
||||
v-model="queryParams.categoryId"
|
||||
placeholder="请选择产品分类"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" @click="openForm('create')" v-hasPermi="['crm:product:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['crm:product:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!--<el-table-column label="主键id" align="center" prop="id" />-->
|
||||
<el-table-column label="产品名称" align="center" prop="name" />
|
||||
<el-table-column label="产品编码" align="center" prop="no" />
|
||||
<el-table-column label="单位" align="center" prop="unit">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="scope.row.unit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="价格" align="center" prop="price">
|
||||
<template #default="{ row }">
|
||||
{{ fenToYuan(row.price) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品分类" align="center" prop="categoryId">
|
||||
<template #default="{ row }">
|
||||
<span>{{ productCategoryList?.find((c) => c.id === row.categoryId)?.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品描述" align="center" prop="description" />
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId">
|
||||
<template #default="{ row }">
|
||||
<span>{{ userList?.find((c) => c.id === row.ownerUserId)?.nickname }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPermi="['crm:product:query']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openDetail(scope.row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:product:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:product:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ProductForm ref="formRef" @success="getList" />
|
||||
|
||||
<!-- 表单弹窗:详情 -->
|
||||
<ProductDetail ref="detailRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ProductApi from '@/api/crm/product'
|
||||
import ProductForm from './ProductForm.vue'
|
||||
import ProductDetail from './ProductDetail.vue'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import * as ProductCategoryApi from '@/api/crm/productCategory'
|
||||
import { getSimpleUserList, UserVO } from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'CrmProduct' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
no: null,
|
||||
unit: null,
|
||||
price: null,
|
||||
status: null,
|
||||
categoryId: null,
|
||||
description: null,
|
||||
ownerUserId: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProductApi.getProductPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
/** 详情操作 */
|
||||
const detailRef = ref()
|
||||
const openDetail = (data: ProductApi.ProductVO) => {
|
||||
detailRef.value.open(data)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ProductApi.deleteProduct(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await ProductApi.exportProduct(queryParams)
|
||||
download.excel(data, '产品.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const productCategoryList = ref([]) // 产品分类树
|
||||
const userList = ref<UserVO[]>([]) // 系统用户
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({})
|
||||
userList.value = await getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
110
src/views/crm/productCategory/ProductCategoryForm.vue
Normal file
110
src/views/crm/productCategory/ProductCategoryForm.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="父级id" prop="parentId">
|
||||
<el-select v-model="formData.parentId" placeholder="请选择上级分类">
|
||||
<el-option :key="0" label="顶级分类" :value="0" />
|
||||
<el-option
|
||||
v-for="item in productCategoryList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名称" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as ProductCategoryApi from '@/api/crm/productCategory'
|
||||
|
||||
defineOptions({ name: 'CrmProductCategoryForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
parentId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||
parentId: [{ required: true, message: '父级分类不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const productCategoryList = ref<any[]>([]) // 产品分类树
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ProductCategoryApi.getProductCategory(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
// 获得分类树
|
||||
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({ parentId: 0 })
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ProductCategoryApi.ProductCategoryVO
|
||||
if (formType.value === 'create') {
|
||||
await ProductCategoryApi.createProductCategory(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await ProductCategoryApi.updateProductCategory(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
parentId: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
138
src/views/crm/productCategory/index.vue
Normal file
138
src/views/crm/productCategory/index.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<!-- TODO @zange:挪到 product 下,建个 category 包,挪进去哈; -->
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['crm:product-category:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" row-key="id" default-expand-all>
|
||||
<el-table-column label="名称" align="center" prop="name" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['crm:product-category:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['crm:product-category:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<ProductCategoryForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ProductCategoryApi from '@/api/crm/productCategory'
|
||||
import ProductCategoryForm from './ProductCategoryForm.vue'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
|
||||
defineOptions({ name: 'CrmProductCategory' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<any[]>([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
name: null
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProductCategoryApi.getProductCategoryList(queryParams)
|
||||
list.value = handleTree(data, 'id', 'parentId')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ProductCategoryApi.deleteProductCategory(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -10,14 +10,14 @@
|
||||
<el-form-item label="回款编号" prop="no">
|
||||
<el-input v-model="formData.no" placeholder="请输入回款编号" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="回款计划ID" prop="planId">
|
||||
<el-input v-model="formData.planId" placeholder="请输入回款计划ID" />
|
||||
</el-form-item>-->
|
||||
<el-form-item label="客户ID" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户ID" />
|
||||
<el-form-item label="回款计划" prop="planId">
|
||||
<el-input v-model="formData.planId" placeholder="请输入回款计划" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同ID" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同ID" />
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同名称" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="审批状态" prop="checkStatus">
|
||||
<el-select v-model="formData.checkStatus" placeholder="请选择审批状态">
|
||||
@@ -54,15 +54,22 @@
|
||||
<el-input-number v-model="formData.price" placeholder="请输入回款金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-input v-model="formData.ownerUserId" placeholder="请输入负责人" />
|
||||
<el-select v-model="formData.ownerUserId" clearable placeholder="请输入负责人">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="批次" prop="batchId">
|
||||
<el-input v-model="formData.batchId" placeholder="请输入批次" />
|
||||
<el-input-number v-model="formData.batchId" placeholder="请输入批次" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="显示顺序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入显示顺序" />
|
||||
</el-form-item>-->
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择状态">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
@@ -71,7 +78,7 @@
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item>-->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" :rows="3" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
@@ -85,10 +92,11 @@
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
||||
import * as ReceivableApi from '@/api/crm/receivable'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
@@ -112,9 +120,9 @@ const formData = ref({
|
||||
status: undefined,
|
||||
remark: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
})
|
||||
// const formRules = reactive({
|
||||
// status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
|
||||
// })
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
@@ -132,6 +140,8 @@ const open = async (type: string, id?: number) => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
// 获得用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
|
||||
@@ -26,19 +26,19 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>-->
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同" prop="contractId">
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-input
|
||||
v-model="queryParams.contractId"
|
||||
placeholder="请输入合同"
|
||||
placeholder="请输入合同名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
@@ -103,7 +103,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>-->
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<!--<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-input
|
||||
v-model="queryParams.ownerUserId"
|
||||
placeholder="请输入负责人"
|
||||
@@ -112,7 +112,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="批次" prop="batchId">
|
||||
<el-form-item label="批次" prop="batchId">
|
||||
<el-input
|
||||
v-model="queryParams.batchId"
|
||||
placeholder="请输入批次"
|
||||
@@ -227,8 +227,12 @@
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="130px">
|
||||
<el-table-column label="操作" align="center" width="180px">
|
||||
<template #default="scope">
|
||||
<!-- todo @liuhongfeng:用路径参数哈,receivableId -->
|
||||
<!--<router-link :to="'/crm/receivable-plan?receivableId=' + scope.row.receivableId">
|
||||
<el-button link type="primary">详情</el-button>
|
||||
</router-link>-->
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
|
||||
@@ -7,8 +7,24 @@
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="期数" prop="indexNo">
|
||||
<el-input-number v-model="formData.indexNo" placeholder="请输入期数" />
|
||||
<el-form-item label="客户名称" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同名称" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-select v-model="formData.ownerUserId" clearable placeholder="请输入负责人">
|
||||
<el-option
|
||||
v-for="item in userList"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="期数" prop="period">
|
||||
<el-input-number v-model="formData.period" placeholder="请输入期数" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="回款ID" prop="receivableId">
|
||||
<el-input v-model="formData.receivableId" placeholder="请输入回款ID" />
|
||||
@@ -58,18 +74,9 @@
|
||||
placeholder="选择提醒日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户ID" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户ID" />
|
||||
<el-form-item label="显示排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" controls-position="right" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合同ID" prop="contractId">
|
||||
<el-input v-model="formData.contractId" placeholder="请输入合同ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-input v-model="formData.ownerUserId" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="显示顺序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入显示顺序" />
|
||||
</el-form-item>-->
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input type="textarea" :rows="3" v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
@@ -81,19 +88,18 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivablePlan'
|
||||
|
||||
import * as UserApi from '@/api/system/user'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
indexNo: undefined,
|
||||
period: undefined,
|
||||
receivableId: undefined,
|
||||
status: undefined,
|
||||
checkStatus: undefined,
|
||||
@@ -128,6 +134,9 @@ const open = async (type: string, id?: number) => {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获得用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@@ -161,7 +170,7 @@ const submitForm = async () => {
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
indexNo: undefined,
|
||||
period: undefined,
|
||||
receivableId: undefined,
|
||||
status: undefined,
|
||||
checkStatus: undefined,
|
||||
|
||||
@@ -8,10 +8,19 @@
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="期数" prop="indexNo">
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-input
|
||||
v-model="queryParams.indexNo"
|
||||
placeholder="请输入期数"
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同" prop="contractId">
|
||||
<el-input
|
||||
v-model="queryParams.contractId"
|
||||
placeholder="请输入合同"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
@@ -67,7 +76,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>-->
|
||||
<el-form-item label="提醒日期" prop="remindTime">
|
||||
<!--<el-form-item label="提醒日期" prop="remindTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.remindTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@@ -77,26 +86,8 @@
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户" prop="customerId">
|
||||
<el-input
|
||||
v-model="queryParams.customerId"
|
||||
placeholder="请输入客户"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="合同" prop="contractId">
|
||||
<el-input
|
||||
v-model="queryParams.contractId"
|
||||
placeholder="请输入合同"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="ownerUserId">
|
||||
</el-form-item>-->
|
||||
<!--<el-form-item label="负责人" prop="ownerUserId">
|
||||
<el-input
|
||||
v-model="queryParams.ownerUserId"
|
||||
placeholder="请输入负责人"
|
||||
@@ -105,7 +96,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!--<el-form-item label="备注" prop="remark">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="queryParams.remark"
|
||||
placeholder="请输入备注"
|
||||
@@ -152,8 +143,26 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="ID" align="center" prop="id" />
|
||||
<el-table-column label="期数" align="center" prop="indexNo" />
|
||||
<!--<el-table-column label="ID" align="center" prop="id" />-->
|
||||
<el-table-column label="客户名称" align="center" prop="customerId" width="150px" />
|
||||
<el-table-column label="合同名称" align="center" prop="contractId" width="150px" />
|
||||
<el-table-column label="期数" align="center" prop="period" />
|
||||
<el-table-column label="计划回款" align="center" prop="price" />
|
||||
<el-table-column
|
||||
label="计划回款日期"
|
||||
align="center"
|
||||
prop="returnTime"
|
||||
:formatter="dateFormatter2"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="提前几天提醒" align="center" prop="remindDays" />
|
||||
<!--<el-table-column
|
||||
label="提醒日期"
|
||||
align="center"
|
||||
prop="remindTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>-->
|
||||
<!--<el-table-column label="回款ID" align="center" prop="receivableId" />-->
|
||||
<el-table-column label="完成状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
@@ -166,26 +175,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!--<el-table-column label="工作流编号" align="center" prop="processInstanceId" />-->
|
||||
<el-table-column label="回款金额" align="center" prop="price" />
|
||||
<el-table-column
|
||||
label="回款日期"
|
||||
align="center"
|
||||
prop="returnTime"
|
||||
:formatter="dateFormatter2"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="提前几天提醒" align="center" prop="remindDays" />
|
||||
<el-table-column
|
||||
label="提醒日期"
|
||||
align="center"
|
||||
prop="remindTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="客户ID" align="center" prop="customerId" />
|
||||
<el-table-column label="合同ID" align="center" prop="contractId" />
|
||||
<el-table-column label="负责人" align="center" prop="ownerUserId" />
|
||||
<!--<el-table-column label="显示顺序" align="center" prop="sort" />-->
|
||||
<el-table-column prop="ownerUserId" label="负责人" width="120">
|
||||
<template #default="scope">
|
||||
{{ userList.find((user) => user.id === scope.row.ownerUserId)?.nickname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="显示顺序" align="center" prop="sort" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
@@ -234,6 +229,7 @@ import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as ReceivablePlanApi from '@/api/crm/receivablePlan'
|
||||
import ReceivablePlanForm from './ReceivablePlanForm.vue'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
defineOptions({ name: 'ReceivablePlan' })
|
||||
|
||||
@@ -243,10 +239,11 @@ const { t } = useI18n() // 国际化
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
indexNo: null,
|
||||
period: null,
|
||||
status: null,
|
||||
checkStatus: null,
|
||||
returnTime: [],
|
||||
@@ -320,7 +317,9 @@ const handleExport = async () => {
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
onMounted(async () => {
|
||||
await getList()
|
||||
// 获取用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<colum-info-form ref="columInfoRef" :columns="formData.columns" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="生成信息" name="generateInfo">
|
||||
<generate-info-form ref="generateInfoRef" :table="formData.table" />
|
||||
<generate-info-form ref="generateInfoRef" :table="formData.table" :columns="formData.columns" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form>
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
ref="treeRef"
|
||||
:data="preview.fileTree"
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
highlight-current
|
||||
node-key="id"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生成模板" prop="templateType">
|
||||
<el-select v-model="formData.templateType" @change="tplSelectChange">
|
||||
<el-select v-model="formData.templateType">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)"
|
||||
:key="dict.value"
|
||||
@@ -182,50 +182,33 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row v-show="formData.tplCategory === 'tree'">
|
||||
<h4 class="form-header">其他信息</h4>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span>
|
||||
树编码字段
|
||||
<el-tooltip content="树显示的编码字段名, 如:dept_id" placement="top">
|
||||
<Icon icon="ep:question-filled" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-select v-model="formData.treeCode" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in formData.columns"
|
||||
:key="index"
|
||||
:label="column.columnName + ':' + column.columnComment"
|
||||
:value="column.columnName"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 树表信息 -->
|
||||
<el-row v-if="formData.templateType == 2">
|
||||
<el-col :span="24">
|
||||
<h4 class="form-header">树表信息</h4>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-form-item prop="treeParentColumnId">
|
||||
<template #label>
|
||||
<span>
|
||||
树父编码字段
|
||||
父编号字段
|
||||
<el-tooltip content="树显示的父编码字段名, 如:parent_Id" placement="top">
|
||||
<Icon icon="ep:question-filled" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-select v-model="formData.treeParentCode" placeholder="请选择">
|
||||
<el-select v-model="formData.treeParentColumnId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in formData.columns"
|
||||
v-for="(column, index) in props.columns"
|
||||
:key="index"
|
||||
:label="column.columnName + ':' + column.columnComment"
|
||||
:value="column.columnName"
|
||||
:value="column.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-form-item prop="treeNameColumnId">
|
||||
<template #label>
|
||||
<span>
|
||||
树名称字段
|
||||
@@ -234,60 +217,79 @@
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<el-select v-model="formData.treeName" placeholder="请选择">
|
||||
<el-select v-model="formData.treeNameColumnId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in formData.columns"
|
||||
v-for="(column, index) in props.columns"
|
||||
:key="index"
|
||||
:label="column.columnName + ':' + column.columnComment"
|
||||
:value="column.columnName"
|
||||
:value="column.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show="formData.tplCategory === 'sub'">
|
||||
<h4 class="form-header">关联信息</h4>
|
||||
|
||||
<!-- 主表信息 -->
|
||||
<el-row v-if="formData.templateType == 15">
|
||||
<el-col :span="24">
|
||||
<h4 class="form-header">主表信息</h4>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-form-item prop="masterTableId">
|
||||
<template #label>
|
||||
<span>
|
||||
关联子表的表名
|
||||
<el-tooltip content="关联子表的表名, 如:sys_user" placement="top">
|
||||
关联的主表
|
||||
<el-tooltip content="关联主表(父表)的表名, 如:system_user" placement="top">
|
||||
<Icon icon="ep:question-filled" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-select v-model="formData.subTableName" placeholder="请选择" @change="subSelectChange">
|
||||
<el-select v-model="formData.masterTableId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(table0, index) in tables"
|
||||
:key="index"
|
||||
:label="table0.tableName + ':' + table0.tableComment"
|
||||
:value="table0.tableName"
|
||||
:value="table0.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-form-item prop="subJoinColumnId">
|
||||
<template #label>
|
||||
<span>
|
||||
子表关联的外键名
|
||||
<el-tooltip content="子表关联的外键名, 如:user_id" placement="top">
|
||||
子表关联的字段
|
||||
<el-tooltip content="子表关联的字段, 如:user_id" placement="top">
|
||||
<Icon icon="ep:question-filled" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-select v-model="formData.subTableFkName" placeholder="请选择">
|
||||
<el-select v-model="formData.subJoinColumnId" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="(column, index) in subColumns"
|
||||
v-for="(column, index) in props.columns"
|
||||
:key="index"
|
||||
:label="column.columnName + ':' + column.columnComment"
|
||||
:value="column.columnName"
|
||||
:value="column.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="subJoinMany">
|
||||
<template #label>
|
||||
<span>
|
||||
关联关系
|
||||
<el-tooltip content="主表与子表的关联关系" placement="top">
|
||||
<Icon icon="ep:question-filled" />
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-radio-group v-model="formData.subJoinMany" placeholder="请选择">
|
||||
<el-radio :label="true">一对多</el-radio>
|
||||
<el-radio :label="false">一对一</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -305,6 +307,10 @@ const props = defineProps({
|
||||
table: {
|
||||
type: Object as PropType<Nullable<CodegenApi.CodegenTableVO>>,
|
||||
default: () => null
|
||||
},
|
||||
columns: {
|
||||
type: Array as unknown as PropType<CodegenApi.CodegenColumnVO[]>,
|
||||
default: () => null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -319,13 +325,12 @@ const formData = ref({
|
||||
classComment: '',
|
||||
parentMenuId: null,
|
||||
genPath: '',
|
||||
treeCode: '',
|
||||
treeParentCode: '',
|
||||
treeName: '',
|
||||
tplCategory: '',
|
||||
subTableName: '',
|
||||
subTableFkName: '',
|
||||
genType: ''
|
||||
genType: '',
|
||||
masterTableId: undefined,
|
||||
subJoinColumnId: undefined,
|
||||
subJoinMany: undefined,
|
||||
treeParentColumnId: undefined,
|
||||
treeNameColumnId: undefined
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
@@ -336,41 +341,29 @@ const rules = reactive({
|
||||
businessName: [required],
|
||||
businessPackage: [required],
|
||||
className: [required],
|
||||
classComment: [required]
|
||||
classComment: [required],
|
||||
masterTableId: [required],
|
||||
subJoinColumnId: [required],
|
||||
subJoinMany: [required],
|
||||
treeParentColumnId: [required],
|
||||
treeNameColumnId: [required]
|
||||
})
|
||||
|
||||
const tables = ref([])
|
||||
const subColumns = ref([])
|
||||
const tables = ref([]) // 表定义列表
|
||||
const menus = ref<any[]>([])
|
||||
const menuTreeProps = {
|
||||
label: 'name'
|
||||
}
|
||||
|
||||
/** 选择子表名触发 */
|
||||
const subSelectChange = () => {
|
||||
formData.value.subTableFkName = ''
|
||||
}
|
||||
|
||||
/** 选择生成模板触发 */
|
||||
const tplSelectChange = (value) => {
|
||||
if (value !== 1) {
|
||||
// TODO 芋艿:暂时不考虑支持树形结构
|
||||
message.error(
|
||||
'暂时不考虑支持【树形】和【主子表】的代码生成。原因是:导致 vm 模板过于复杂,不利于胖友二次开发'
|
||||
)
|
||||
return false
|
||||
}
|
||||
if (value !== 'sub') {
|
||||
formData.value.subTableName = ''
|
||||
formData.value.subTableFkName = ''
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.table,
|
||||
(table) => {
|
||||
async (table) => {
|
||||
if (!table) return
|
||||
formData.value = table as any
|
||||
// 加载表列表
|
||||
if (table.dataSourceConfigId >= 0) {
|
||||
tables.value = await CodegenApi.getCodegenTableList(formData.value.dataSourceConfigId)
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
@@ -380,6 +373,7 @@ watch(
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 加载菜单
|
||||
const resp = await MenuApi.getSimpleMenusList()
|
||||
menus.value = handleTree(resp)
|
||||
} catch {}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成" url="https://doc.iocoder.cn/new-feature/" />
|
||||
<doc-alert title="代码生成(单表)" url="https://doc.iocoder.cn/new-feature/" />
|
||||
<doc-alert title="代码生成(树表)" url="https://doc.iocoder.cn/new-feature/tree/" />
|
||||
<doc-alert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||
<doc-alert title="单元测试" url="https://doc.iocoder.cn/unit-test/" />
|
||||
|
||||
<!-- 搜索 -->
|
||||
|
||||
126
src/views/infra/demo/demo01/Demo01ContactForm.vue
Normal file
126
src/views/infra/demo/demo01/Demo01ContactForm.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生年" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择出生年"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<Editor v-model="formData.description" height="150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="头像" prop="avatar">
|
||||
<UploadImg v-model="formData.avatar" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import * as Demo01ContactApi from '@/api/infra/demo/demo01'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined,
|
||||
avatar: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
|
||||
birthday: [{ required: true, message: '出生年不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo01ContactApi.getDemo01Contact(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as Demo01ContactApi.Demo01ContactVO
|
||||
if (formType.value === 'create') {
|
||||
await Demo01ContactApi.createDemo01Contact(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo01ContactApi.updateDemo01Contact(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined,
|
||||
avatar: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
214
src/views/infra/demo/demo01/index.vue
Normal file
214
src/views/infra/demo/demo01/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成(单表)" url="https://doc.iocoder.cn/new-feature/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo01-contact:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo01-contact:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="出生年"
|
||||
align="center"
|
||||
prop="birthday"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="简介" align="center" prop="description" />
|
||||
<el-table-column label="头像" align="center" prop="avatar" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo01-contact:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo01-contact:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo01ContactForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo01ContactApi from '@/api/infra/demo/demo01'
|
||||
import Demo01ContactForm from './Demo01ContactForm.vue'
|
||||
|
||||
defineOptions({ name: 'Demo01Contact' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
sex: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo01ContactApi.getDemo01ContactPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo01ContactApi.deleteDemo01Contact(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await Demo01ContactApi.exportDemo01Contact(queryParams)
|
||||
download.excel(data, '示例联系人.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
114
src/views/infra/demo/demo02/Demo02CategoryForm.vue
Normal file
114
src/views/infra/demo/demo02/Demo02CategoryForm.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级编号" prop="parentId">
|
||||
<el-tree-select
|
||||
v-model="formData.parentId"
|
||||
:data="demo02CategoryTree"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
default-expand-all
|
||||
placeholder="请选择父级编号"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo02CategoryApi from '@/api/infra/demo/demo02'
|
||||
import { defaultProps, handleTree } from '@/utils/tree'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
parentId: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
parentId: [{ required: true, message: '父级编号不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const demo02CategoryTree = ref() // 树形结构
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo02CategoryApi.getDemo02Category(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
await getDemo02CategoryTree()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as Demo02CategoryApi.Demo02CategoryVO
|
||||
if (formType.value === 'create') {
|
||||
await Demo02CategoryApi.createDemo02Category(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo02CategoryApi.updateDemo02Category(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
parentId: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 获得示例分类树 */
|
||||
const getDemo02CategoryTree = async () => {
|
||||
demo02CategoryTree.value = []
|
||||
const data = await Demo02CategoryApi.getDemo02CategoryList()
|
||||
const root: Tree = { id: 0, name: '顶级示例分类', children: [] }
|
||||
root.children = handleTree(data, 'id', 'parentId')
|
||||
demo02CategoryTree.value.push(root)
|
||||
}
|
||||
</script>
|
||||
207
src/views/infra/demo/demo02/index.vue
Normal file
207
src/views/infra/demo/demo02/index.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成(树表)" url="https://doc.iocoder.cn/new-feature/tree/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo02-category:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo02-category:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button type="danger" plain @click="toggleExpandAll">
|
||||
<Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
row-key="id"
|
||||
:default-expand-all="isExpandAll"
|
||||
v-if="refreshTable"
|
||||
>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo02-category:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo02-category:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo02CategoryForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo02CategoryApi from '@/api/infra/demo/demo02'
|
||||
import Demo02CategoryForm from './Demo02CategoryForm.vue'
|
||||
|
||||
defineOptions({ name: 'Demo02Category' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
name: null,
|
||||
parentId: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo02CategoryApi.getDemo02CategoryList(queryParams)
|
||||
list.value = handleTree(data, 'id', 'parentId')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo02CategoryApi.deleteDemo02Category(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await Demo02CategoryApi.exportDemo02Category(queryParams)
|
||||
download.excel(data, '示例分类.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 展开/折叠操作 */
|
||||
const isExpandAll = ref(true) // 是否展开,默认全部展开
|
||||
const refreshTable = ref(true) // 重新渲染表格状态
|
||||
const toggleExpandAll = async () => {
|
||||
refreshTable.value = false
|
||||
isExpandAll.value = !isExpandAll.value
|
||||
await nextTick()
|
||||
refreshTable.value = true
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
121
src/views/infra/demo/demo03/erp/Demo03StudentForm.vue
Normal file
121
src/views/infra/demo/demo03/erp/Demo03StudentForm.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<Editor v-model="formData.description" height="150px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo03StudentApi.getDemo03Student(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO
|
||||
if (formType.value === 'create') {
|
||||
await Demo03StudentApi.createDemo03Student(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo03StudentApi.updateDemo03Student(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分数" prop="score">
|
||||
<el-input v-model="formData.score" placeholder="请输入分数" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
score: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, studentId: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.studentId = studentId
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo03StudentApi.getDemo03Course(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value
|
||||
if (formType.value === 'create') {
|
||||
await Demo03StudentApi.createDemo03Course(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo03StudentApi.updateDemo03Course(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
score: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
126
src/views/infra/demo/demo03/erp/components/Demo03CourseList.vue
Normal file
126
src/views/infra/demo/demo03/erp/components/Demo03CourseList.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="分数" align="center" prop="score" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo03CourseForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
import Demo03CourseForm from './Demo03CourseForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
studentId: undefined
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
(val) => {
|
||||
queryParams.studentId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03CoursePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.studentId) {
|
||||
message.error('请选择一个学生')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.studentId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo03StudentApi.deleteDemo03Course(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班主任" prop="teacher">
|
||||
<el-input v-model="formData.teacher" placeholder="请输入班主任" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
teacher: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number, studentId: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
formData.value.studentId = studentId
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo03StudentApi.getDemo03Grade(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value
|
||||
if (formType.value === 'create') {
|
||||
await Demo03StudentApi.createDemo03Grade(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo03StudentApi.updateDemo03Grade(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
teacher: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
126
src/views/infra/demo/demo03/erp/components/Demo03GradeList.vue
Normal file
126
src/views/infra/demo/demo03/erp/components/Demo03GradeList.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="班主任" align="center" prop="teacher" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo03GradeForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
import Demo03GradeForm from './Demo03GradeForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
studentId: undefined
|
||||
})
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
(val) => {
|
||||
queryParams.studentId = val
|
||||
handleQuery()
|
||||
},
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03GradePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (!props.studentId) {
|
||||
message.error('请选择一个学生')
|
||||
return
|
||||
}
|
||||
formRef.value.open(type, id, props.studentId)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo03StudentApi.deleteDemo03Grade(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
</script>
|
||||
240
src/views/infra/demo/demo03/erp/index.vue
Normal file
240
src/views/infra/demo/demo03/erp/index.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo03-student:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
highlight-current-row
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="出生日期"
|
||||
align="center"
|
||||
prop="birthday"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="简介" align="center" prop="description" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo03StudentForm ref="formRef" @success="getList" />
|
||||
<!-- 子表的列表 -->
|
||||
<ContentWrap>
|
||||
<el-tabs model-value="demo03Course">
|
||||
<el-tab-pane label="学生课程" name="demo03Course">
|
||||
<Demo03CourseList :student-id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学生班级" name="demo03Grade">
|
||||
<Demo03GradeList :student-id="currentRow.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/erp'
|
||||
import Demo03StudentForm from './Demo03StudentForm.vue'
|
||||
import Demo03CourseList from './components/Demo03CourseList.vue'
|
||||
import Demo03GradeList from './components/Demo03GradeList.vue'
|
||||
|
||||
defineOptions({ name: 'Demo03Student' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
sex: null,
|
||||
description: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03StudentPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo03StudentApi.deleteDemo03Student(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await Demo03StudentApi.exportDemo03Student(queryParams)
|
||||
download.excel(data, '学生.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 选中行操作 */
|
||||
const currentRow = ref({}) // 选中行
|
||||
const handleCurrentChange = (row) => {
|
||||
currentRow.value = row
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
153
src/views/infra/demo/demo03/inner/Demo03StudentForm.vue
Normal file
153
src/views/infra/demo/demo03/inner/Demo03StudentForm.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<Editor v-model="formData.description" height="150px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane label="学生课程" name="demo03Course">
|
||||
<Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学生班级" name="demo03Grade">
|
||||
<Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
import Demo03CourseForm from './components/Demo03CourseForm.vue'
|
||||
import Demo03GradeForm from './components/Demo03GradeForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 子表的表单 */
|
||||
const subTabsName = ref('demo03Course')
|
||||
const demo03CourseFormRef = ref()
|
||||
const demo03GradeFormRef = ref()
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo03StudentApi.getDemo03Student(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 校验子表单
|
||||
try {
|
||||
await demo03CourseFormRef.value.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'demo03Course'
|
||||
return
|
||||
}
|
||||
try {
|
||||
await demo03GradeFormRef.value.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'demo03Grade'
|
||||
return
|
||||
}
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO
|
||||
// 拼接子表的数据
|
||||
data.demo03Courses = demo03CourseFormRef.value.getData()
|
||||
data.demo03Grade = demo03GradeFormRef.value.getData()
|
||||
if (formType.value === 'create') {
|
||||
await Demo03StudentApi.createDemo03Student(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo03StudentApi.updateDemo03Student(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
v-loading="formLoading"
|
||||
label-width="0px"
|
||||
:inline-message="true"
|
||||
>
|
||||
<el-table :data="formData" class="-mt-10px">
|
||||
<el-table-column label="序号" type="index" width="100" />
|
||||
<el-table-column label="名字" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
|
||||
<el-input v-model="row.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分数" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.score`" :rules="formRules.score" class="mb-0px!">
|
||||
<el-input v-model="row.score" placeholder="请输入分数" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="{ $index }">
|
||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row justify="center" class="mt-3">
|
||||
<el-button @click="handleAdd" round>+ 添加学生课程</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
// 1. 重置表单
|
||||
formData.value = []
|
||||
// 2. val 非空,则加载数据
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formLoading.value = true
|
||||
formData.value = await Demo03StudentApi.getDemo03CourseListByStudentId(val)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const row = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
score: undefined
|
||||
}
|
||||
row.studentId = props.studentId
|
||||
formData.value.push(row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
|
||||
/** 表单值 */
|
||||
const getData = () => {
|
||||
return formData.value
|
||||
}
|
||||
|
||||
defineExpose({ validate, getData })
|
||||
</script>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="分数" align="center" prop="score" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
list.value = await Demo03StudentApi.getDemo03CourseListByStudentId(props.studentId)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班主任" prop="teacher">
|
||||
<el-input v-model="formData.teacher" placeholder="请输入班主任" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
// 1. 重置表单
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
teacher: undefined,
|
||||
}
|
||||
// 2. val 非空,则加载数据
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formLoading.value = true
|
||||
const data = await Demo03StudentApi.getDemo03GradeByStudentId(val)
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
formData.value = data
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
|
||||
/** 表单值 */
|
||||
const getData = () => {
|
||||
return formData.value
|
||||
}
|
||||
|
||||
defineExpose({ validate, getData })
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="班主任" align="center" prop="teacher" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03GradeByStudentId(props.studentId)
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
list.value.push(data)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
229
src/views/infra/demo/demo03/inner/index.vue
Normal file
229
src/views/infra/demo/demo03/inner/index.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo03-student:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<!-- 子表的列表 -->
|
||||
<el-table-column type="expand">
|
||||
<template #default="scope">
|
||||
<el-tabs model-value="demo03Course">
|
||||
<el-tab-pane label="学生课程" name="demo03Course">
|
||||
<Demo03CourseList :student-id="scope.row.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学生班级" name="demo03Grade">
|
||||
<Demo03GradeList :student-id="scope.row.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="出生日期"
|
||||
align="center"
|
||||
prop="birthday"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="简介" align="center" prop="description" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo03StudentForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/inner'
|
||||
import Demo03StudentForm from './Demo03StudentForm.vue'
|
||||
import Demo03CourseList from './components/Demo03CourseList.vue'
|
||||
import Demo03GradeList from './components/Demo03GradeList.vue'
|
||||
|
||||
defineOptions({ name: 'Demo03Student' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
sex: null,
|
||||
description: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03StudentPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo03StudentApi.deleteDemo03Student(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await Demo03StudentApi.exportDemo03Student(queryParams)
|
||||
download.excel(data, '学生.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
153
src/views/infra/demo/demo03/normal/Demo03StudentForm.vue
Normal file
153
src/views/infra/demo/demo03/normal/Demo03StudentForm.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择出生日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<Editor v-model="formData.description" height="150px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane label="学生课程" name="demo03Course">
|
||||
<Demo03CourseForm ref="demo03CourseFormRef" :student-id="formData.id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="学生班级" name="demo03Grade">
|
||||
<Demo03GradeForm ref="demo03GradeFormRef" :student-id="formData.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal'
|
||||
import Demo03CourseForm from './components/Demo03CourseForm.vue'
|
||||
import Demo03GradeForm from './components/Demo03GradeForm.vue'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'blur' }],
|
||||
description: [{ required: true, message: '简介不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 子表的表单 */
|
||||
const subTabsName = ref('demo03Course')
|
||||
const demo03CourseFormRef = ref()
|
||||
const demo03GradeFormRef = ref()
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await Demo03StudentApi.getDemo03Student(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 校验子表单
|
||||
try {
|
||||
await demo03CourseFormRef.value.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'demo03Course'
|
||||
return
|
||||
}
|
||||
try {
|
||||
await demo03GradeFormRef.value.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'demo03Grade'
|
||||
return
|
||||
}
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as Demo03StudentApi.Demo03StudentVO
|
||||
// 拼接子表的数据
|
||||
data.demo03Courses = demo03CourseFormRef.value.getData()
|
||||
data.demo03Grade = demo03GradeFormRef.value.getData()
|
||||
if (formType.value === 'create') {
|
||||
await Demo03StudentApi.createDemo03Student(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await Demo03StudentApi.updateDemo03Student(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
sex: undefined,
|
||||
birthday: undefined,
|
||||
description: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
v-loading="formLoading"
|
||||
label-width="0px"
|
||||
:inline-message="true"
|
||||
>
|
||||
<el-table :data="formData" class="-mt-10px">
|
||||
<el-table-column label="序号" type="index" width="100" />
|
||||
<el-table-column label="名字" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
|
||||
<el-input v-model="row.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分数" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.score`" :rules="formRules.score" class="mb-0px!">
|
||||
<el-input v-model="row.score" placeholder="请输入分数" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="{ $index }">
|
||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row justify="center" class="mt-3">
|
||||
<el-button @click="handleAdd" round>+ 添加学生课程</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal'
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
score: [{ required: true, message: '分数不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
// 1. 重置表单
|
||||
formData.value = []
|
||||
// 2. val 非空,则加载数据
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formLoading.value = true
|
||||
formData.value = await Demo03StudentApi.getDemo03CourseListByStudentId(val)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const row = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
score: undefined
|
||||
}
|
||||
row.studentId = props.studentId
|
||||
formData.value.push(row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
|
||||
/** 表单值 */
|
||||
const getData = () => {
|
||||
return formData.value
|
||||
}
|
||||
|
||||
defineExpose({ validate, getData })
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名字" />
|
||||
</el-form-item>
|
||||
<el-form-item label="班主任" prop="teacher">
|
||||
<el-input v-model="formData.teacher" placeholder="请输入班主任" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal'
|
||||
|
||||
const props = defineProps<{
|
||||
studentId: undefined // 学生编号(主表的关联字段)
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
studentId: [{ required: true, message: '学生编号不能为空', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '名字不能为空', trigger: 'blur' }],
|
||||
teacher: [{ required: true, message: '班主任不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.studentId,
|
||||
async (val) => {
|
||||
// 1. 重置表单
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
studentId: undefined,
|
||||
name: undefined,
|
||||
teacher: undefined,
|
||||
}
|
||||
// 2. val 非空,则加载数据
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formLoading.value = true
|
||||
const data = await Demo03StudentApi.getDemo03GradeByStudentId(val)
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
formData.value = data
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
|
||||
/** 表单值 */
|
||||
const getData = () => {
|
||||
return formData.value
|
||||
}
|
||||
|
||||
defineExpose({ validate, getData })
|
||||
</script>
|
||||
214
src/views/infra/demo/demo03/normal/index.vue
Normal file
214
src/views/infra/demo/demo03/normal/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<doc-alert title="代码生成(主子表)" url="https://doc.iocoder.cn/new-feature/master-sub/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="名字" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['infra:demo03-student:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['infra:demo03-student:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="性别" align="center" prop="sex">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="出生日期"
|
||||
align="center"
|
||||
prop="birthday"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="简介" align="center" prop="description" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['infra:demo03-student:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<Demo03StudentForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as Demo03StudentApi from '@/api/infra/demo/demo03/normal'
|
||||
import Demo03StudentForm from './Demo03StudentForm.vue'
|
||||
|
||||
defineOptions({ name: 'Demo03Student' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
sex: null,
|
||||
description: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await Demo03StudentApi.getDemo03StudentPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await Demo03StudentApi.deleteDemo03Student(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await Demo03StudentApi.exportDemo03Student(queryParams)
|
||||
download.excel(data, '学生.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -1,4 +0,0 @@
|
||||
<template>
|
||||
<div>index</div>
|
||||
</template>
|
||||
<script lang="ts" setup></script>
|
||||
126
src/views/mall/product/spu/components/SpuShowcase.vue
Normal file
126
src/views/mall/product/spu/components/SpuShowcase.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="flex flex-wrap items-center gap-8px">
|
||||
<div v-for="(spu, index) in productSpus" :key="spu.id" class="select-box spu-pic">
|
||||
<el-tooltip :content="spu.name">
|
||||
<div class="relative h-full w-full">
|
||||
<el-image :src="spu.picUrl" class="h-full w-full" />
|
||||
<Icon
|
||||
v-show="!disabled"
|
||||
class="del-icon"
|
||||
icon="ep:circle-close-filled"
|
||||
@click="handleRemoveSpu(index)"
|
||||
/>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-tooltip content="选择商品">
|
||||
<div
|
||||
v-show="!disabled"
|
||||
v-if="!limit || limit <= productSpus.length"
|
||||
class="select-box"
|
||||
@click="openSpuTableSelect"
|
||||
>
|
||||
<Icon icon="ep:plus" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<!-- 商品选择对话框(表格形式) -->
|
||||
<SpuTableSelect ref="spuTableSelectRef" multiple @change="handleSpuSelected" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import SpuTableSelect from '@/views/mall/product/spu/components/SpuTableSelect.vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { array } from 'vue-types'
|
||||
|
||||
// 商品橱窗,一般用于与商品建立关系时使用
|
||||
// 提供功能:展示商品列表、添加商品、移除商品
|
||||
defineOptions({ name: 'SpuShowcase' })
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: array<number>().def([]).isRequired,
|
||||
// 限制数量:默认不限制
|
||||
limit: propTypes.number.def(0),
|
||||
disabled: propTypes.bool.def(false)
|
||||
})
|
||||
|
||||
// 商品列表
|
||||
const productSpus = ref<ProductSpuApi.Spu[]>([])
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
async () => {
|
||||
if (props.modelValue.length === 0) {
|
||||
productSpus.value = []
|
||||
return
|
||||
}
|
||||
// 只有商品发生变化之后,才去查询商品
|
||||
if (
|
||||
productSpus.value.length === 0 ||
|
||||
productSpus.value.some((spu) => !props.modelValue.includes(spu.id))
|
||||
) {
|
||||
debugger
|
||||
productSpus.value = await ProductSpuApi.getSpuDetailList(props.modelValue)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 商品表格选择对话框 */
|
||||
const spuTableSelectRef = ref()
|
||||
// 打开对话框
|
||||
const openSpuTableSelect = () => {
|
||||
spuTableSelectRef.value.open(productSpus.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择商品后触发
|
||||
* @param spus 选中的商品列表
|
||||
*/
|
||||
const handleSpuSelected = (spus: ProductSpuApi.Spu[]) => {
|
||||
productSpus.value = spus
|
||||
emitSpuChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
* @param index 商品索引
|
||||
*/
|
||||
const handleRemoveSpu = (index: number) => {
|
||||
productSpus.value.splice(index, 1)
|
||||
emitSpuChange()
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
const emitSpuChange = () => {
|
||||
emit(
|
||||
'update:modelValue',
|
||||
productSpus.value.map((spu) => spu.id)
|
||||
)
|
||||
emit('change', productSpus.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.select-box {
|
||||
display: flex;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 1px dashed var(--el-border-color-darker);
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spu-pic {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.del-icon {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
z-index: 1;
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
}
|
||||
</style>
|
||||
114
src/views/mall/promotion/diy/page/DiyPageForm.vue
Normal file
114
src/views/mall/promotion/diy/page/DiyPageForm.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="页面名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入页面名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预览图" prop="previewImageUrls">
|
||||
<UploadImgs v-model="formData.previewImageUrls" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
|
||||
/** 装修页面表单 */
|
||||
defineOptions({ name: 'DiyPageForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
remark: undefined,
|
||||
previewImageUrls: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '页面名称不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const diyPage = await DiyPageApi.getDiyPage(id) // 处理预览图
|
||||
if (diyPage?.previewImageUrls?.length > 0) {
|
||||
diyPage.previewImageUrls = diyPage.previewImageUrls.map((url: string) => {
|
||||
return { url }
|
||||
})
|
||||
}
|
||||
formData.value = diyPage
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 处理预览图
|
||||
const previewImageUrls = formData.value.previewImageUrls.map((item) => {
|
||||
return item['url'] ? item['url'] : item
|
||||
})
|
||||
const data = { ...formData.value, previewImageUrls } as unknown as DiyPageApi.DiyPageVO
|
||||
if (formType.value === 'create') {
|
||||
await DiyPageApi.createDiyPage(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await DiyPageApi.updateDiyPage(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
remark: undefined,
|
||||
previewImageUrls: []
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
76
src/views/mall/promotion/diy/page/decorate.vue
Normal file
76
src/views/mall/promotion/diy/page/decorate.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<DiyEditor
|
||||
v-if="formData && !formLoading"
|
||||
v-model="formData.property"
|
||||
:title="formData.name"
|
||||
:libs="PAGE_LIBS"
|
||||
:show-page-config="true"
|
||||
:show-navigation-bar="true"
|
||||
:show-tab-bar="false"
|
||||
@save="submitForm"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { PAGE_LIBS } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 装修页面表单 */
|
||||
defineOptions({ name: 'DiyPageDecorate' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref<DiyPageApi.DiyPageVO>()
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
// 获取详情
|
||||
const getPageDetail = async (id: any) => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await DiyPageApi.getDiyPageProperty(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await DiyPageApi.updateDiyPageProperty(unref(formData)!)
|
||||
message.success('保存成功')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
templateId: undefined,
|
||||
name: '',
|
||||
remark: '',
|
||||
previewImageUrls: [],
|
||||
property: ''
|
||||
} as DiyPageApi.DiyPageVO
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const route = useRoute()
|
||||
onMounted(() => {
|
||||
resetForm()
|
||||
if (!route.params.id) {
|
||||
message.warning('参数错误,页面编号不能为空!')
|
||||
delView(unref(currentRoute))
|
||||
return
|
||||
}
|
||||
getPageDetail(route.params.id)
|
||||
})
|
||||
</script>
|
||||
189
src/views/mall/promotion/diy/page/index.vue
Normal file
189
src/views/mall/promotion/diy/page/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="页面名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入页面名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:diy-page:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="预览图" align="center" prop="previewImageUrls">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
class="h-40px max-w-40px"
|
||||
v-for="(url, index) in scope.row.previewImageUrls"
|
||||
:key="index"
|
||||
:src="url"
|
||||
:preview-src-list="scope.row.previewImageUrls"
|
||||
:initial-index="index"
|
||||
preview-teleported
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="页面名称" align="center" prop="name" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleDecorate(scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-page:update']"
|
||||
>
|
||||
装修
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-page:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-page:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<DiyPageForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
import DiyPageForm from './DiyPageForm.vue'
|
||||
|
||||
/** 装修页面 */
|
||||
defineOptions({ name: 'DiyPage' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await DiyPageApi.getDiyPagePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await DiyPageApi.deleteDiyPage(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 打开装修页面 */
|
||||
const { push } = useRouter()
|
||||
const handleDecorate = (id: number) => {
|
||||
push({ name: 'DiyPageDecorate', params: { id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
115
src/views/mall/promotion/diy/template/DiyTemplateForm.vue
Normal file
115
src/views/mall/promotion/diy/template/DiyTemplateForm.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="模板名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入模板名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预览图" prop="previewImageUrls">
|
||||
<UploadImgs v-model="formData.previewImageUrls" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
|
||||
/** 装修模板表单 */
|
||||
defineOptions({ name: 'DiyTemplateForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
remark: undefined,
|
||||
previewImageUrls: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const diyTemplate = await DiyTemplateApi.getDiyTemplate(id)
|
||||
// 处理预览图
|
||||
if (diyTemplate?.previewImageUrls?.length > 0) {
|
||||
diyTemplate.previewImageUrls = diyTemplate.previewImageUrls.map((url: string) => {
|
||||
return { url }
|
||||
})
|
||||
}
|
||||
formData.value = diyTemplate
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
// 处理预览图
|
||||
const previewImageUrls = formData.value.previewImageUrls.map((item) => {
|
||||
return item['url'] ? item['url'] : item
|
||||
})
|
||||
const data = { ...formData.value, previewImageUrls } as unknown as DiyTemplateApi.DiyTemplateVO
|
||||
if (formType.value === 'create') {
|
||||
await DiyTemplateApi.createDiyTemplate(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await DiyTemplateApi.updateDiyTemplate(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
remark: undefined,
|
||||
previewImageUrls: []
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
131
src/views/mall/promotion/diy/template/decorate.vue
Normal file
131
src/views/mall/promotion/diy/template/decorate.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<DiyEditor
|
||||
v-if="formData && !formLoading"
|
||||
v-model="currentFormData!.property"
|
||||
:title="templateItems[selectedTemplateItem].name"
|
||||
:libs="libs"
|
||||
:show-page-config="selectedTemplateItem !== 0"
|
||||
:show-tab-bar="selectedTemplateItem === 0"
|
||||
:show-navigation-bar="selectedTemplateItem !== 0"
|
||||
@save="submitForm"
|
||||
>
|
||||
<template #toolBarLeft>
|
||||
<el-radio-group
|
||||
v-model="selectedTemplateItem"
|
||||
class="h-full!"
|
||||
@change="handleTemplateItemChange"
|
||||
>
|
||||
<el-tooltip v-for="(item, index) in templateItems" :key="index" :content="item.name">
|
||||
<el-radio-button :label="index">
|
||||
<Icon :icon="item.icon" :size="24" />
|
||||
</el-radio-button>
|
||||
</el-tooltip>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</DiyEditor>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import * as DiyPageApi from '@/api/mall/promotion/diy/page'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util'
|
||||
|
||||
/** 装修模板表单 */
|
||||
defineOptions({ name: 'DiyTemplateDecorate' })
|
||||
|
||||
// 左上角工具栏操作按钮
|
||||
const selectedTemplateItem = ref(0)
|
||||
const templateItems = reactive([
|
||||
{ name: '基础设置', icon: 'ep:iphone' },
|
||||
{ name: '首页', icon: 'ep:home-filled' },
|
||||
{ name: '我的', icon: 'ep:user-filled' }
|
||||
])
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>()
|
||||
const formRef = ref() // 表单 Ref
|
||||
// 当前编辑的属性
|
||||
const currentFormData = ref<DiyTemplateApi.DiyTemplatePropertyVO | DiyPageApi.DiyPageVO>()
|
||||
|
||||
// 获取详情
|
||||
const getPageDetail = async (id: any) => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await DiyTemplateApi.getDiyTemplateProperty(id)
|
||||
currentFormData.value = formData.value
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 模板组件库
|
||||
const templateLibs = [] as DiyComponentLibrary[]
|
||||
// 当前组件库
|
||||
const libs = ref<DiyComponentLibrary[]>(templateLibs)
|
||||
// 模板选项切换
|
||||
const handleTemplateItemChange = () => {
|
||||
// 编辑模板
|
||||
if (selectedTemplateItem.value === 0) {
|
||||
libs.value = templateLibs
|
||||
currentFormData.value = formData.value
|
||||
return
|
||||
}
|
||||
|
||||
// 编辑页面
|
||||
libs.value = PAGE_LIBS
|
||||
currentFormData.value = formData.value!.pages.find(
|
||||
(page: DiyPageApi.DiyPageVO) => page.name === templateItems[selectedTemplateItem.value].name
|
||||
)
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (selectedTemplateItem.value === 0) {
|
||||
// 提交模板属性
|
||||
await DiyTemplateApi.updateDiyTemplateProperty(unref(formData)!)
|
||||
} else {
|
||||
// 提交页面属性
|
||||
await DiyPageApi.updateDiyPageProperty(unref(currentFormData)!)
|
||||
}
|
||||
message.success('保存成功')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: '',
|
||||
used: false,
|
||||
usedTime: undefined,
|
||||
remark: '',
|
||||
previewImageUrls: [],
|
||||
property: '',
|
||||
pages: []
|
||||
} as DiyTemplateApi.DiyTemplatePropertyVO
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
const { currentRoute } = useRouter() // 路由
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
const route = useRoute()
|
||||
onMounted(() => {
|
||||
resetForm()
|
||||
if (!route.params.id) {
|
||||
message.warning('参数错误,页面编号不能为空!')
|
||||
delView(unref(currentRoute))
|
||||
return
|
||||
}
|
||||
getPageDetail(route.params.id)
|
||||
})
|
||||
</script>
|
||||
225
src/views/mall/promotion/diy/template/index.vue
Normal file
225
src/views/mall/promotion/diy/template/index.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="模板名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入模板名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['promotion:diy-template:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column label="预览图" align="center" prop="previewImageUrls">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
class="h-40px max-w-40px"
|
||||
v-for="(url, index) in scope.row.previewImageUrls"
|
||||
:key="index"
|
||||
:src="url"
|
||||
:preview-src-list="scope.row.previewImageUrls"
|
||||
:initial-index="index"
|
||||
preview-teleported
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模板名称" align="center" prop="name" />
|
||||
<el-table-column label="是否使用" align="center" prop="used">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.used" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="使用时间"
|
||||
align="center"
|
||||
prop="usedTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleDecorate(scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-template:update']"
|
||||
>
|
||||
装修
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-template:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<template v-if="!scope.row.used">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleUse(scope.row)"
|
||||
v-hasPermi="['promotion:diy-template:use']"
|
||||
>
|
||||
使用
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['promotion:diy-template:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<DiyTemplateForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
|
||||
import DiyTemplateForm from './DiyTemplateForm.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
/** 装修模板 */
|
||||
defineOptions({ name: 'DiyTemplate' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await DiyTemplateApi.getDiyTemplatePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await DiyTemplateApi.deleteDiyTemplate(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 使用模板 */
|
||||
const handleUse = async (row: DiyTemplateApi.DiyTemplateVO) => {
|
||||
try {
|
||||
// 使用模板的二次确认
|
||||
await message.confirm(`是否使用模板“${row.name}”?`)
|
||||
// 发起删除
|
||||
await DiyTemplateApi.useDiyTemplate(row.id)
|
||||
message.success('使用成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 打开装修页面 */
|
||||
const { push } = useRouter()
|
||||
const handleDecorate = (id: number) => {
|
||||
push({ name: 'DiyTemplateDecorate', params: { id } })
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -110,9 +110,11 @@ const handleTimeRangeChange = async (times: [dayjs.ConfigType, dayjs.ConfigType]
|
||||
.trapezoid1 {
|
||||
transform: perspective(5em) rotateX(-11deg);
|
||||
}
|
||||
|
||||
.trapezoid2 {
|
||||
transform: perspective(7em) rotateX(-20deg);
|
||||
}
|
||||
|
||||
.trapezoid3 {
|
||||
transform: perspective(3em) rotateX(-13deg);
|
||||
}
|
||||
|
||||
@@ -316,6 +316,7 @@ onMounted(() => {
|
||||
:deep(.order-table-col > .cell) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.summary {
|
||||
.el-col {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
96
src/views/member/user/detail/UserFavoriteList.vue
Normal file
96
src/views/member/user/detail/UserFavoriteList.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column key="id" align="center" label="商品编号" width="180" prop="id" />
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.picUrl" class="h-30px w-30px" @click="imagePreview(row.picUrl)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
|
||||
<el-table-column align="center" label="商品售价" min-width="90" prop="price">
|
||||
<template #default="{ row }"> {{ floatToFixed2(row.price) }}元</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
|
||||
<el-table-column
|
||||
:formatter="dateFormatter"
|
||||
align="center"
|
||||
label="收藏时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="状态" min-width="80">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as FavoriteApi from '@/api/mall/product/favorite'
|
||||
import { floatToFixed2 } from '@/utils'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
createTime: [],
|
||||
userId: NaN
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await FavoriteApi.getFavoritePage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const { userId } = defineProps({
|
||||
userId: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
queryParams.userId = userId
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -48,7 +48,9 @@
|
||||
<UserOrderList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="售后管理" lazy>售后管理(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收藏记录" lazy>收藏记录(WIP)</el-tab-pane>
|
||||
<el-tab-pane label="收藏记录" lazy>
|
||||
<UserFavoriteList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="优惠劵" lazy>
|
||||
<UserCouponList :user-id="id" />
|
||||
</el-tab-pane>
|
||||
@@ -76,6 +78,7 @@ import UserExperienceRecordList from './UserExperienceRecordList.vue'
|
||||
import UserOrderList from './UserOrderList.vue'
|
||||
import UserPointList from './UserPointList.vue'
|
||||
import UserSignList from './UserSignList.vue'
|
||||
import UserFavoriteList from './UserFavoriteList.vue'
|
||||
import { CardTitle } from '@/components/Card/index'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
|
||||
122
src/views/pay/demo/transfer/DemoTransferForm.vue
Normal file
122
src/views/pay/demo/transfer/DemoTransferForm.vue
Normal file
@@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="转账类型" prop="type">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PAY_TRANSFER_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
:disabled="dict.value === 2 || dict.value === 3 || dict.value === 4"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="转账金额(元)" prop="price">
|
||||
<el-input-number
|
||||
v-model="formData.price"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
placeholder="请输入转账金额"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="收款人姓名" prop="userName">
|
||||
<el-input v-model="formData.userName" placeholder="请输入收款人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="formData.type === 1" label="支付宝登录账号" prop="alipayLogonId">
|
||||
<el-input v-model="formData.alipayLogonId" placeholder="请输入支付宝登录账号" />
|
||||
</el-form-item>
|
||||
<el-form-item v-show="formData.type === 2" label="微信 openid" prop="openid">
|
||||
<el-input v-model="formData.openid" placeholder="请输入微信 openid" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import * as DemoTransferApi from '@/api/pay/demo/transfer'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { yuanToFen } from '@/utils'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
price: undefined,
|
||||
type: undefined,
|
||||
userName: undefined,
|
||||
alipayLogonId: undefined,
|
||||
openid: undefined
|
||||
})
|
||||
const formRules = reactive({
|
||||
price: [{ required: true, message: '转账金额不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '转账类型不能为空', trigger: 'change' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
}
|
||||
/** 关闭弹窗 */
|
||||
const close = async () => {
|
||||
dialogVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
defineExpose({ open, close }) // 提供 open, close 方法,用于打开, 关闭弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = { ...formData.value }
|
||||
data.price = yuanToFen(data.price)
|
||||
if (formType.value === 'create') {
|
||||
await DemoTransferApi.createDemoTransfer(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
price: undefined,
|
||||
userName: undefined,
|
||||
alipayLogonId: undefined,
|
||||
openid: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
159
src/views/pay/demo/transfer/index.vue
Normal file
159
src/views/pay/demo/transfer/index.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')"
|
||||
><Icon icon="ep:plus" />创建业务转账单
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true">
|
||||
<el-table-column label="订单编号" align="center" prop="id" />
|
||||
<el-table-column label="转账类型" align="center" prop="type" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_TRANSFER_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="转账金额" align="center" prop="price">
|
||||
<template #default="scope">
|
||||
<span>¥{{ (scope.row.price / 100.0).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款人姓名" align="center" prop="userName" width="120" />
|
||||
<el-table-column label="支付宝登录账号" align="center" prop="alipayLogonId" width="180" />
|
||||
<el-table-column label="微信 openid" align="center" prop="openid" width="120" />
|
||||
<el-table-column label="转账状态" align="center" prop="transferStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_TRANSFER_STATUS" :value="scope.row.transferStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="转账单号" align="center" prop="payTransferId" />
|
||||
<el-table-column label="支付渠道" align="center" prop="payChannelCode" />
|
||||
<el-table-column
|
||||
label="转账时间"
|
||||
align="center"
|
||||
prop="transferTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
width="100"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="handleTransfer(scope.row)"
|
||||
v-if="scope.row.transferStatus === 0"
|
||||
v-hasPermi="['pay:transfer:create']"
|
||||
>
|
||||
发起转账
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<DemoTransferForm ref="demoFormRef" @success="getList" />
|
||||
<CreatePayTransfer ref="payTransferRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as DemoTransferApi from '@/api/pay/demo/transfer'
|
||||
import * as PayTransferApi from '@/api/pay/transfer'
|
||||
import DemoTransferForm from './DemoTransferForm.vue'
|
||||
import CreatePayTransfer from '../../transfer/CreatePayTransfer.vue'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
let payTransfer = {
|
||||
appId: undefined,
|
||||
merchantTransferId: undefined,
|
||||
type: undefined,
|
||||
price: undefined,
|
||||
subject: undefined,
|
||||
userName: undefined,
|
||||
alipayLogonId: undefined,
|
||||
openid: undefined
|
||||
} as PayTransferApi.TransferVO // 传递给转账订单的数据
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await DemoTransferApi.getDemoTransferPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 创建业务转账单操作 */
|
||||
const demoFormRef = ref()
|
||||
const payTransferRef = ref()
|
||||
const openForm = (type: string) => {
|
||||
demoFormRef.value.open(type)
|
||||
}
|
||||
|
||||
/** 发起转账操作 */
|
||||
const handleTransfer = (row: any) => {
|
||||
payTransfer = { ...row }
|
||||
payTransfer.merchantTransferId = row.id.toString()
|
||||
payTransfer.subject = '示例转账'
|
||||
payTransferRef.value.showPayTransfer(payTransfer)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -62,7 +62,7 @@
|
||||
<el-divider />
|
||||
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
|
||||
<el-descriptions-item label="支付通道异步回调内容">
|
||||
{{ detailData.extension.channelNotifyData }}
|
||||
<el-text>{{ detailData.extension.channelNotifyData }}</el-text>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</Dialog>
|
||||
|
||||
135
src/views/pay/transfer/CreatePayTransfer.vue
Normal file
135
src/views/pay/transfer/CreatePayTransfer.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<Dialog title="发起转账" v-model="dialogVisible" width="800px">
|
||||
<el-card style="margin-top: 10px">
|
||||
<el-descriptions title="转账信息" :column="2" border>
|
||||
<el-descriptions-item label="转账类型">
|
||||
{{ typeName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="转账金额(元)">
|
||||
¥{{ (transfer.price / 100.0).toFixed(2) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="收款人姓名">
|
||||
{{ transfer.userName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="支付宝登录账号" v-if="transfer.type === 1">
|
||||
{{ transfer.alipayLogonId }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信 openid" v-if="transfer.type === 2">
|
||||
{{ transfer.openid }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
<el-card style="margin-top: 20px">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>选择转账渠道</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-radio-group v-model="channelCode">
|
||||
<el-radio
|
||||
label="alipay_pc"
|
||||
:disabled="transfer.type === 2 || transfer.type === 3 || transfer.type === 4"
|
||||
>
|
||||
<img :src="svg_alipay_app" />
|
||||
</el-radio>
|
||||
<el-radio
|
||||
label="wx_app"
|
||||
:disabled="transfer.type === 1 || transfer.type === 3 || transfer.type === 4"
|
||||
>
|
||||
<img :src="svg_wx_app" />
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-divider />
|
||||
<div style="text-align: right">
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as PayTransferApi from '@/api/pay/transfer'
|
||||
import { computed, PropType } from 'vue'
|
||||
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
|
||||
// 导入图标
|
||||
import svg_alipay_app from '@/assets/svgs/pay/icon/alipay_app.svg'
|
||||
import svg_wx_app from '@/assets/svgs/pay/icon/wx_app.svg'
|
||||
import { yuanToFen } from '@/utils'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const formLoading = ref(false) // 提交的按钮禁用
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
defineOptions({ name: 'CreatePayTransfer' })
|
||||
|
||||
// 提交数据
|
||||
let submitTransferData: PayTransferApi.TransferVO
|
||||
|
||||
const transfer = reactive({
|
||||
appId: undefined,
|
||||
channelCode: undefined,
|
||||
merchantTransferId: undefined,
|
||||
type: undefined,
|
||||
price: undefined,
|
||||
subject: undefined,
|
||||
userName: undefined,
|
||||
alipayLogonId: undefined,
|
||||
openid: undefined
|
||||
})
|
||||
const dialogVisible = ref(false)
|
||||
const typeName = computed(() => {
|
||||
return getDictLabel(DICT_TYPE.PAY_TRANSFER_TYPE, transfer.type)
|
||||
})
|
||||
const channelCode = computed(() => {
|
||||
let channelCode = 'alipay_pc'
|
||||
if (transfer.type === 2) {
|
||||
channelCode = 'wx_app'
|
||||
}
|
||||
// TODO 银行卡和钱包 转账待实现
|
||||
return channelCode
|
||||
})
|
||||
|
||||
/** 打开弹窗 */
|
||||
const showPayTransfer = async (payTransfer: PayTransferApi.TransferVO) => {
|
||||
dialogVisible.value = true
|
||||
submitTransferData = payTransfer
|
||||
transfer.merchantTransferId = payTransfer.merchantTransferId
|
||||
transfer.price = payTransfer.price
|
||||
transfer.userName = payTransfer.userName
|
||||
transfer.type = payTransfer.type
|
||||
transfer.appId = payTransfer.appId
|
||||
transfer.subject = payTransfer.subject
|
||||
transfer.alipayLogonId = payTransfer.alipayLogonId
|
||||
transfer.openid = payTransfer.openid
|
||||
}
|
||||
/** 关闭弹窗 */
|
||||
const close = async () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
defineExpose({ showPayTransfer, close }) // 提供 showPayTransfer, close 方法,用于打开, 关闭弹窗
|
||||
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
formLoading.value = true
|
||||
try {
|
||||
submitTransferData.channelCode = channelCode.value
|
||||
await PayTransferApi.createTransfer(submitTransferData)
|
||||
message.success('发起转账成功. 是否转账成功,以转账订单状态为准')
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
dialogVisible.value = false
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
80
src/views/pay/transfer/TransferDetail.vue
Normal file
80
src/views/pay/transfer/TransferDetail.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="转账单详情" width="700px">
|
||||
<el-descriptions :column="2" label-class-name="desc-label">
|
||||
<el-descriptions-item label="商户单号">
|
||||
<el-tag size="small">{{ detailData.merchantTransferId }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="转账单号">
|
||||
<el-tag type="warning" size="small" v-if="detailData.no">{{ detailData.no }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="应用编号">{{ detailData.appId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="转账状态">
|
||||
<dict-tag :type="DICT_TYPE.PAY_TRANSFER_STATUS" :value="detailData.status" size="small" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="转账金额">
|
||||
<el-tag type="success" size="small">¥{{ (detailData.price / 100.0).toFixed(2) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="转账时间">
|
||||
{{ formatDate(detailData.successTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
{{ formatDate(detailData.createTime) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<!-- 分割线 -->
|
||||
<el-divider />
|
||||
<el-descriptions :column="2" label-class-name="desc-label">
|
||||
<el-descriptions-item label="收款人姓名">{{ detailData.userName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="支付宝登录账号" v-if="detailData.type === 1">
|
||||
{{ detailData.alipayLogonId }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="微信 openid" v-if="detailData.type === 2">
|
||||
{{ detailData.openid }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="支付渠道">
|
||||
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="detailData.channelCode" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="支付 IP">{{ detailData.userIp }}</el-descriptions-item>
|
||||
<el-descriptions-item label="渠道单号">
|
||||
<el-tag size="mini" type="success" v-if="detailData.channelTransferNo">
|
||||
{{ detailData.channelTransferNo }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="通知 URL">{{ detailData.notifyUrl }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-divider />
|
||||
<el-descriptions :column="1" label-class-name="desc-label" direction="vertical" border>
|
||||
<el-descriptions-item label="转账渠道通知内容">
|
||||
<el-text>{{ detailData.channelNotifyData }}</el-text>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-divider />
|
||||
<div style="text-align: right">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
import * as TransferApi from '@/api/pay/transfer'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
defineOptions({ name: 'PayTransferDetail' })
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const detailLoading = ref(false) // 表单的加载中
|
||||
const detailData = ref({})
|
||||
/** 打开弹窗 */
|
||||
const open = async (id: number) => {
|
||||
dialogVisible.value = true
|
||||
// 设置数据
|
||||
detailLoading.value = true
|
||||
try {
|
||||
detailData.value = await TransferApi.getTransfer(id)
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
267
src/views/pay/transfer/index.vue
Normal file
267
src/views/pay/transfer/index.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="转账单号" prop="no">
|
||||
<el-input
|
||||
v-model="queryParams.no"
|
||||
placeholder="请输入转账单号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="转账渠道" prop="channelCode">
|
||||
<el-select
|
||||
v-model="queryParams.channelCode"
|
||||
placeholder="请选择支付渠道"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="商户单号" prop="merchantTransferId">
|
||||
<el-input
|
||||
v-model="queryParams.merchantTransferId"
|
||||
placeholder="请输入商户单号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="queryParams.type" placeholder="请选择类型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.PAY_TRANSFER_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="转账状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择转账状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.PAY_TRANSFER_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="收款人姓名" prop="userName">
|
||||
<el-input
|
||||
v-model="queryParams.userName"
|
||||
placeholder="请输入收款人姓名"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道单号" prop="channelTransferNo">
|
||||
<el-input
|
||||
v-model="queryParams.channelTransferNo"
|
||||
placeholder="渠道单号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="编号" align="center" prop="id" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="应用编号" align="center" prop="appId" />
|
||||
<el-table-column label="类型" align="center" prop="type" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_TRANSFER_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="转账金额" align="center" prop="price">
|
||||
<template #default="scope">
|
||||
<span>¥{{ (scope.row.price / 100.0).toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="转账状态" align="center" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_TRANSFER_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单号" align="left" width="300">
|
||||
<template #default="scope">
|
||||
<p class="transfer-font">
|
||||
<el-tag size="small"> 商户</el-tag>
|
||||
{{ scope.row.merchantTransferId }}
|
||||
</p>
|
||||
<p class="transfer-font" v-if="scope.row.no">
|
||||
<el-tag size="small" type="warning">转账</el-tag>
|
||||
{{ scope.row.no }}
|
||||
</p>
|
||||
<p class="transfer-font" v-if="scope.row.channelTransferNo">
|
||||
<el-tag size="small" type="success">渠道</el-tag>
|
||||
{{ scope.row.channelTransferNo }}
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收款人姓名" align="center" prop="userName" width="120" />
|
||||
<el-table-column label="收款账号" align="left" width="200">
|
||||
<template #default="scope">
|
||||
<p class="transfer-font" v-if="scope.row.alipayLogonId">
|
||||
<el-tag size="small">支付宝登录号</el-tag>
|
||||
{{ scope.row.alipayLogonId }}
|
||||
</p>
|
||||
<p class="transfer-font" v-if="scope.row.openid">
|
||||
<el-tag size="small">微信 openId</el-tag>
|
||||
{{ scope.row.openid }}
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="转账标题" align="center" prop="subject" width="120" />
|
||||
<el-table-column label="转账渠道" align="center" prop="channelCode">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE" :value="scope.row.channelCode" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="转账成功时间"
|
||||
align="center"
|
||||
prop="successTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openDetail(scope.row.id)"> 详情 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<TransferDetail ref="detailRef" />
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as TransferApi from '@/api/pay/transfer'
|
||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
||||
import TransferDetail from './TransferDetail.vue'
|
||||
defineOptions({ name: 'PayTransfer' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
no: null,
|
||||
appId: null,
|
||||
channelId: null,
|
||||
channelCode: null,
|
||||
merchantTransferId: null,
|
||||
type: null,
|
||||
status: null,
|
||||
successTime: [],
|
||||
price: null,
|
||||
subject: null,
|
||||
userName: null,
|
||||
alipayLogonId: null,
|
||||
openid: null,
|
||||
createTime: []
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await TransferApi.getTransferPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const detailRef = ref()
|
||||
const openDetail = (id: number) => {
|
||||
detailRef.value.open(id)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transfer-font {
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -90,7 +90,7 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as WalletRechargePackageApi.WalletRechargePackageVO
|
||||
const data = { ...formData.value }
|
||||
data.payPrice = yuanToFen(data.payPrice)
|
||||
data.bonusPrice = yuanToFen(data.bonusPrice)
|
||||
if (formType.value === 'create') {
|
||||
|
||||
@@ -37,9 +37,6 @@
|
||||
<el-descriptions-item label="发送时间">
|
||||
{{ formatDate(detailData.sendTime) }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="发送结果">
|
||||
{{ detailData.sendCode }} | {{ detailData.sendMsg }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="API 发送结果">
|
||||
{{ detailData.apiSendCode }} | {{ detailData.apiSendMsg }}
|
||||
</el-descriptions-item>
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
value-format="x"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="绑定域名" prop="domain">
|
||||
<el-input v-model="formData.domain" placeholder="请输入绑定域名" />
|
||||
<el-form-item label="绑定域名" prop="website">
|
||||
<el-input v-model="formData.website" placeholder="请输入绑定域名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="租户状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
@@ -97,7 +97,7 @@ const formData = ref({
|
||||
contactMobile: undefined,
|
||||
accountCount: undefined,
|
||||
expireTime: undefined,
|
||||
domain: undefined,
|
||||
website: undefined,
|
||||
status: CommonStatusEnum.ENABLE
|
||||
})
|
||||
const formRules = reactive({
|
||||
@@ -107,7 +107,7 @@ const formRules = reactive({
|
||||
status: [{ required: true, message: '租户状态不能为空', trigger: 'blur' }],
|
||||
accountCount: [{ required: true, message: '账号额度不能为空', trigger: 'blur' }],
|
||||
expireTime: [{ required: true, message: '过期时间不能为空', trigger: 'blur' }],
|
||||
domain: [{ required: true, message: '绑定域名不能为空', trigger: 'blur' }],
|
||||
website: [{ required: true, message: '绑定域名不能为空', trigger: 'blur' }],
|
||||
username: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }],
|
||||
password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }]
|
||||
})
|
||||
@@ -170,7 +170,7 @@ const resetForm = () => {
|
||||
contactMobile: undefined,
|
||||
accountCount: undefined,
|
||||
expireTime: undefined,
|
||||
domain: undefined,
|
||||
website: undefined,
|
||||
status: CommonStatusEnum.ENABLE
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column label="绑定域名" align="center" prop="domain" width="180" />
|
||||
<el-table-column label="绑定域名" align="center" prop="website" width="180" />
|
||||
<el-table-column label="租户状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
|
||||
Reference in New Issue
Block a user