Merge remote-tracking branch 'refs/remotes/yudao/dev' into develop
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { SystemUserProfileApi } from '#/api/system/user/profile';
|
||||
|
||||
import { Card, Tabs } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import ProfileUser from './modules/profile-user.vue';
|
||||
import BaseInfo from './modules/base-info.vue';
|
||||
import ResetPwd from './modules/reset-pwd.vue';
|
||||
import UserSocial from './modules/user-social.vue';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Card, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { getUserProfile } from '#/api/system/user/profile';
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import BaseInfo from './modules/base-info.vue';
|
||||
import ProfileUser from './modules/profile-user.vue';
|
||||
import ResetPwd from './modules/reset-pwd.vue';
|
||||
import UserSocial from './modules/user-social.vue';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const activeName = ref('basicInfo');
|
||||
|
||||
@@ -46,13 +49,13 @@ onMounted(loadProfile);
|
||||
<Card class="ml-3 w-3/5">
|
||||
<Tabs v-model:active-key="activeName" class="-mt-4">
|
||||
<Tabs.TabPane key="basicInfo" tab="基本设置">
|
||||
<BaseInfo :profile="profile" @success="refreshProfile" />
|
||||
<BaseInfo :profile="profile" @success="refreshProfile" />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="resetPwd" tab="密码设置">
|
||||
<ResetPwd />
|
||||
<ResetPwd />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="userSocial" tab="社交绑定" force-render>
|
||||
<UserSocial @update:active-name="activeName = $event" />
|
||||
<UserSocial @update:active-name="activeName = $event" />
|
||||
</Tabs.TabPane>
|
||||
<!-- TODO @芋艿:在线设备 -->
|
||||
</Tabs>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { SystemUserProfileApi } from '#/api/system/user/profile';
|
||||
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { watch } from 'vue';
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
import { updateUserProfile } from '#/api/system/user/profile';
|
||||
import { $t } from '@vben/locales';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const props = defineProps<{ profile?: SystemUserProfileApi.UserProfileRespVO }>();
|
||||
const props = defineProps<{
|
||||
profile?: SystemUserProfileApi.UserProfileRespVO;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'success'): void;
|
||||
}>();
|
||||
@@ -87,11 +92,15 @@ async function handleSubmit(values: Recordable<any>) {
|
||||
}
|
||||
|
||||
/** 监听 profile 变化 */
|
||||
watch(() => props.profile, (newProfile) => {
|
||||
if (newProfile) {
|
||||
formApi.setValues(newProfile);
|
||||
}
|
||||
}, { immediate: true });
|
||||
watch(
|
||||
() => props.profile,
|
||||
(newProfile) => {
|
||||
if (newProfile) {
|
||||
formApi.setValues(newProfile);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { updateUserPassword } from '#/api/system/user/profile';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
|
||||
@@ -2,26 +2,27 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { SystemSocialUserApi } from '#/api/system/social/user';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { Button, Card, Image, message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { computed, ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { $t } from '#/locales';
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { socialAuthRedirect } from '#/api/core/auth';
|
||||
import {
|
||||
getBindSocialUserList,
|
||||
socialUnbind,
|
||||
socialBind,
|
||||
socialUnbind,
|
||||
} from '#/api/system/social/user';
|
||||
import { socialAuthRedirect } from '#/api/core/auth';
|
||||
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
|
||||
import { $t } from '#/locales';
|
||||
import { SystemUserSocialTypeEnum } from '#/utils/constants';
|
||||
import { DICT_TYPE, getDictLabel } from '#/utils/dict';
|
||||
|
||||
const route = useRoute();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:activeName', v: string): void;
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
/** 已经绑定的平台 */
|
||||
const bindList = ref<SystemSocialUserApi.SocialUser[]>([]);
|
||||
const allBindList = computed<any[]>(() => {
|
||||
@@ -126,8 +127,7 @@ async function onBind(bind: any) {
|
||||
try {
|
||||
// 计算 redirectUri
|
||||
// tricky: type 需要先 encode 一次,否则钉钉回调会丢失。配合 getUrlValue() 使用
|
||||
const redirectUri =
|
||||
location.origin + '/profile?' + encodeURIComponent(`type=${type}`);
|
||||
const redirectUri = `${location.origin}/profile?${encodeURIComponent(`type=${type}`)}`;
|
||||
|
||||
// 进行跳转
|
||||
window.location.href = await socialAuthRedirect(type, redirectUri);
|
||||
|
||||
@@ -63,7 +63,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入分类排序',
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -69,7 +69,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="审批接入(流程表单)" url="https://doc.iocoder.cn/bpm/use-bpm-form/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<DocAlert
|
||||
title="审批接入(流程表单)"
|
||||
url="https://doc.iocoder.cn/bpm/use-bpm-form/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/form/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/form/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/form/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/form/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/group/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/group/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/group/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/group/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,28 +1,242 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import type { BpmModelApi } from '#/api/bpm/model';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { Plus, Search, Settings } from '@vben/icons';
|
||||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import { refAutoReset } from '@vueuse/core';
|
||||
import { useSortable } from '@vueuse/integrations/useSortable';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
Menu,
|
||||
message,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
getCategorySimpleList,
|
||||
updateCategorySortBatch,
|
||||
} from '#/api/bpm/category';
|
||||
import { getModelList } from '#/api/bpm/model';
|
||||
|
||||
import CategoryDraggableModel from './modules/category-draggable-model.vue';
|
||||
// 模型列表加载状态
|
||||
const modelListSpinning = refAutoReset(false, 3000);
|
||||
// 保存排序状态
|
||||
const saveSortLoading = ref(false);
|
||||
// 按照 category 分组的数据
|
||||
const categoryGroup = ref<BpmModelApi.ModelCategoryInfo[]>([]);
|
||||
// 未排序前的原始数据
|
||||
const originalData = ref<BpmModelApi.ModelCategoryInfo[]>([]);
|
||||
// 可以排序元素的容器
|
||||
const sortable = useTemplateRef<HTMLElement>('categoryGroupRef');
|
||||
// 排序引用,以便后续启用或禁用排序
|
||||
const sortableInstance = ref<any>(null);
|
||||
// 分类排序状态
|
||||
const isCategorySorting = ref(false);
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
// 监听分类排序模式切换
|
||||
watch(
|
||||
() => isCategorySorting.value,
|
||||
(newValue) => {
|
||||
if (sortableInstance.value) {
|
||||
if (newValue) {
|
||||
// 启用排序功能
|
||||
sortableInstance.value.option('disabled', false);
|
||||
} else {
|
||||
// 禁用排序功能
|
||||
sortableInstance.value.option('disabled', true);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/** 加载数据 */
|
||||
const getList = async () => {
|
||||
modelListSpinning.value = true;
|
||||
try {
|
||||
const modelList = await getModelList(queryParams.name);
|
||||
const categoryList = await getCategorySimpleList();
|
||||
// 按照 category 聚合
|
||||
categoryGroup.value = categoryList.map((category: any) => ({
|
||||
...category,
|
||||
modelList: modelList.filter(
|
||||
(model: any) => model.categoryName === category.name,
|
||||
),
|
||||
}));
|
||||
// 重置排序实例
|
||||
sortableInstance.value = null;
|
||||
} finally {
|
||||
modelListSpinning.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 初始化 */
|
||||
onActivated(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
/** 查询方法 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 新增模型 */
|
||||
const createModel = () => {
|
||||
// TODO 新增模型
|
||||
};
|
||||
|
||||
/** 处理下拉菜单命令 */
|
||||
const handleCommand = (command: string) => {
|
||||
if (command === 'handleCategoryAdd') {
|
||||
// TODO 新建分类逻辑
|
||||
} else if (command === 'handleCategorySort') {
|
||||
originalData.value = cloneDeep(categoryGroup.value);
|
||||
isCategorySorting.value = true;
|
||||
// 如果排序实例不存在,则初始化
|
||||
if (sortableInstance.value) {
|
||||
// 已存在实例,则启用排序功能
|
||||
sortableInstance.value.option('disabled', false);
|
||||
} else {
|
||||
sortableInstance.value = useSortable(sortable, categoryGroup, {
|
||||
disabled: false, // 启用排序
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消分类排序 */
|
||||
const handleCategorySortCancel = () => {
|
||||
// 恢复初始数据
|
||||
categoryGroup.value = cloneDeep(originalData.value);
|
||||
isCategorySorting.value = false;
|
||||
// 直接禁用排序功能
|
||||
if (sortableInstance.value) {
|
||||
sortableInstance.value.option('disabled', true);
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交分类排序 */
|
||||
const handleCategorySortSubmit = async () => {
|
||||
saveSortLoading.value = true;
|
||||
try {
|
||||
// 保存排序逻辑
|
||||
const ids = categoryGroup.value.map((item: any) => item.id);
|
||||
await updateCategorySortBatch(ids);
|
||||
} finally {
|
||||
saveSortLoading.value = false;
|
||||
}
|
||||
message.success('分类排序成功');
|
||||
isCategorySorting.value = false;
|
||||
// 刷新列表
|
||||
await getList();
|
||||
// 禁用排序功能
|
||||
if (sortableInstance.value) {
|
||||
sortableInstance.value.option('disabled', true);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
<Page auto-content-height>
|
||||
<Card
|
||||
:body-style="{ padding: '10px' }"
|
||||
class="mb-4"
|
||||
v-spinning="modelListSpinning"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/model/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/model/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<div class="flex items-center justify-between pl-5">
|
||||
<span class="-mb-4 text-lg font-extrabold">流程模型</span>
|
||||
<!-- 搜索工作栏 -->
|
||||
<Form
|
||||
v-if="!isCategorySorting"
|
||||
class="-mb-4 mr-2.5 flex"
|
||||
:model="queryParams"
|
||||
layout="inline"
|
||||
>
|
||||
<Form.Item name="name" class="ml-auto">
|
||||
<Input
|
||||
v-model:value="queryParams.name"
|
||||
placeholder="搜索流程"
|
||||
allow-clear
|
||||
@press-enter="handleQuery"
|
||||
class="!w-60"
|
||||
>
|
||||
<template #prefix>
|
||||
<Search class="mx-2.5" />
|
||||
</template>
|
||||
</Input>
|
||||
</Form.Item>
|
||||
<!-- 右上角:新建模型、更多操作 -->
|
||||
<Form.Item>
|
||||
<Button type="primary" @click="createModel">
|
||||
<Plus class="size-5" /> 新建模型
|
||||
</Button>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Dropdown placement="bottomRight">
|
||||
<Button>
|
||||
<template #icon>
|
||||
<Settings class="size-4" />
|
||||
</template>
|
||||
</Button>
|
||||
<template #overlay>
|
||||
<Menu @click="(e) => handleCommand(e.key as string)">
|
||||
<Menu.Item key="handleCategoryAdd">
|
||||
<div class="flex items-center">
|
||||
<span
|
||||
class="icon-[ant-design--plus-outlined] mr-1.5 text-[18px]"
|
||||
></span>
|
||||
新建分类
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="handleCategorySort">
|
||||
<div class="flex items-center">
|
||||
<span class="icon-[fa--sort-amount-desc] mr-1.5"></span>
|
||||
分类排序
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div class="-mb-4 mr-6" v-else>
|
||||
<Button @click="handleCategorySortCancel" class="mr-3">
|
||||
取 消
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
:loading="saveSortLoading"
|
||||
@click="handleCategorySortSubmit"
|
||||
>
|
||||
保存排序
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<!-- 按照分类,展示其所属的模型列表 -->
|
||||
<div class="px-5" ref="categoryGroupRef">
|
||||
<CategoryDraggableModel
|
||||
v-for="element in categoryGroup"
|
||||
:class="isCategorySorting ? 'cursor-move' : ''"
|
||||
:key="element.id"
|
||||
:category-info="element"
|
||||
:is-category-sorting="isCategorySorting"
|
||||
@success="getList"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BpmCategoryApi } from '#/api/bpm/category';
|
||||
import type { BpmModelApi } from '#/api/bpm/model';
|
||||
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
|
||||
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { useSortable } from '@vueuse/integrations/useSortable';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
message,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { updateModelSortBatch } from '#/api/bpm/model';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const props = defineProps<{
|
||||
categoryInfo: BpmCategoryApi.ModelCategoryInfo;
|
||||
isCategorySorting: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const isModelSorting = ref(false);
|
||||
const originalData = ref<BpmModelApi.ModelVO[]>([]);
|
||||
const modelList = ref<BpmModelApi.ModelVO[]>([]);
|
||||
const isExpand = ref(false);
|
||||
const tableRef = ref();
|
||||
|
||||
// 排序引用,以便后续启用或禁用排序
|
||||
const sortableInstance = ref<any>(null);
|
||||
/** 解决 v-model 问题,使用计算属性 */
|
||||
const expandKeys = computed(() => (isExpand.value ? ['1'] : []));
|
||||
|
||||
// 表格列配置
|
||||
const columns = [
|
||||
{
|
||||
title: '流程名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'left' as const,
|
||||
minWidth: 250,
|
||||
},
|
||||
{
|
||||
title: '可见范围',
|
||||
dataIndex: 'startUserIds',
|
||||
key: 'startUserIds',
|
||||
align: 'center' as const,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
title: '流程类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
align: 'center' as const,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
title: '表单信息',
|
||||
dataIndex: 'formType',
|
||||
key: 'formType',
|
||||
align: 'center' as const,
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
title: '最后发布',
|
||||
dataIndex: 'deploymentTime',
|
||||
key: 'deploymentTime',
|
||||
align: 'center' as const,
|
||||
minWidth: 250,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'operation',
|
||||
align: 'center' as const,
|
||||
fixed: 'right' as const,
|
||||
width: 150,
|
||||
},
|
||||
];
|
||||
|
||||
/** 处理模型的排序 */
|
||||
const handleModelSort = () => {
|
||||
// 保存初始数据
|
||||
originalData.value = cloneDeep(props.categoryInfo.modelList);
|
||||
// 展开数据
|
||||
isExpand.value = true;
|
||||
isModelSorting.value = true;
|
||||
// 如果排序实例不存在,则初始化
|
||||
if (sortableInstance.value) {
|
||||
// 已存在实例,则启用排序功能
|
||||
sortableInstance.value.option('disabled', false);
|
||||
} else {
|
||||
const sortableClass = `.category-${props.categoryInfo.id} .ant-table .ant-table-tbody`;
|
||||
sortableInstance.value = useSortable(sortableClass, modelList, {
|
||||
disabled: false, // 启用排序
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 处理模型的排序提交 */
|
||||
const handleModelSortSubmit = async () => {
|
||||
try {
|
||||
// 保存排序
|
||||
const ids = modelList.value.map((item) => item.id);
|
||||
await updateModelSortBatch(ids);
|
||||
// 刷新列表
|
||||
isModelSorting.value = false;
|
||||
message.success('排序模型成功');
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
console.error('排序保存失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 处理模型的排序取消 */
|
||||
const handleModelSortCancel = () => {
|
||||
// 恢复初始数据
|
||||
modelList.value = cloneDeep(originalData.value);
|
||||
isModelSorting.value = false;
|
||||
// 禁用排序功能
|
||||
if (sortableInstance.value) {
|
||||
sortableInstance.value.option('disabled', true);
|
||||
}
|
||||
};
|
||||
|
||||
/** 处理表单详情点击 */
|
||||
const handleFormDetail = (row: any) => {
|
||||
// TODO 待实现
|
||||
console.warn('待实现', row);
|
||||
};
|
||||
|
||||
/** 更新 modelList 模型列表 */
|
||||
const updateModelList = useDebounceFn(() => {
|
||||
const newModelList = props.categoryInfo.modelList;
|
||||
if (!isEqual(modelList.value, newModelList)) {
|
||||
modelList.value = cloneDeep(newModelList);
|
||||
if (newModelList?.length > 0) {
|
||||
isExpand.value = true;
|
||||
}
|
||||
// 关闭排序
|
||||
isModelSorting.value = false;
|
||||
// 重置排序实例
|
||||
sortableInstance.value = null;
|
||||
}
|
||||
}, 100);
|
||||
|
||||
/** 监听分类信息和排序状态变化 */
|
||||
watchEffect(() => {
|
||||
if (props.categoryInfo?.modelList) {
|
||||
updateModelList();
|
||||
}
|
||||
|
||||
if (props.isCategorySorting) {
|
||||
isExpand.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
/** 自定义表格行渲染 */
|
||||
const customRow = (_record: any) => {
|
||||
return {
|
||||
class: isModelSorting.value ? 'cursor-move' : '',
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card
|
||||
:body-style="{ padding: 0 }"
|
||||
class="category-draggable-model mb-5 rounded-lg transition-all duration-300 ease-in-out hover:shadow-xl"
|
||||
>
|
||||
<div class="flex h-12 items-center">
|
||||
<!-- 头部:分类名 -->
|
||||
<div class="flex items-center">
|
||||
<Tooltip v-if="isCategorySorting" title="拖动排序">
|
||||
<span
|
||||
class="icon-[ic--round-drag-indicator] ml-2.5 cursor-move text-2xl text-gray-500"
|
||||
></span>
|
||||
</Tooltip>
|
||||
<div class="ml-4 mr-2 text-lg font-medium">{{ categoryInfo.name }}</div>
|
||||
<div class="text-gray-500">
|
||||
({{ categoryInfo.modelList?.length || 0 }})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头部:操作 -->
|
||||
<div class="flex flex-1 items-center" v-show="!isCategorySorting">
|
||||
<div
|
||||
v-if="categoryInfo.modelList.length > 0"
|
||||
class="ml-3 flex cursor-pointer items-center transition-transform duration-300"
|
||||
:class="isExpand ? 'rotate-180' : 'rotate-0'"
|
||||
@click="isExpand = !isExpand"
|
||||
>
|
||||
<span
|
||||
class="icon-[ic--round-expand-more] text-3xl text-gray-400"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="ml-auto flex items-center"
|
||||
:class="isModelSorting ? 'mr-4' : 'mr-12'"
|
||||
>
|
||||
<template v-if="!isModelSorting">
|
||||
<Button
|
||||
v-if="categoryInfo.modelList.length > 0"
|
||||
type="link"
|
||||
class="mr-5 flex items-center text-[14px]"
|
||||
@click.stop="handleModelSort"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="icon-[fa--sort-amount-desc] mr-1"></span>
|
||||
</template>
|
||||
排序
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<Button @click.stop="handleModelSortCancel" class="mr-2">
|
||||
取 消
|
||||
</Button>
|
||||
<Button type="primary" @click.stop="handleModelSortSubmit">
|
||||
保存排序
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模型列表 -->
|
||||
<Collapse :active-key="expandKeys" :bordered="false" class="bg-transparent">
|
||||
<Collapse.Panel
|
||||
key="1"
|
||||
:show-arrow="false"
|
||||
class="border-0 bg-transparent p-0"
|
||||
v-show="isExpand"
|
||||
>
|
||||
<Table
|
||||
v-if="modelList && modelList.length > 0"
|
||||
:class="`category-${categoryInfo.id}`"
|
||||
ref="tableRef"
|
||||
:data-source="modelList"
|
||||
:columns="columns"
|
||||
:pagination="false"
|
||||
:custom-row="customRow"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<!-- 流程名 -->
|
||||
<template v-if="column.key === 'name'">
|
||||
<div class="flex items-center">
|
||||
<Tooltip v-if="isModelSorting" title="拖动排序">
|
||||
<span
|
||||
class="icon-[ic--round-drag-indicator] mr-2.5 cursor-move text-2xl text-gray-500"
|
||||
></span>
|
||||
</Tooltip>
|
||||
<div
|
||||
v-if="!record.icon"
|
||||
class="mr-2.5 flex h-9 w-9 items-center justify-center rounded bg-blue-500 text-white"
|
||||
>
|
||||
<span style="font-size: 12px">{{
|
||||
record.name.substring(0, 2)
|
||||
}}</span>
|
||||
</div>
|
||||
<img
|
||||
v-else
|
||||
:src="record.icon"
|
||||
class="mr-2.5 h-9 w-9 rounded"
|
||||
alt="图标"
|
||||
/>
|
||||
{{ record.name }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 可见范围列-->
|
||||
<template v-else-if="column.key === 'startUserIds'">
|
||||
<span
|
||||
v-if="!record.startUsers?.length && !record.startDepts?.length"
|
||||
>
|
||||
全部可见
|
||||
</span>
|
||||
<span v-else-if="record.startUsers?.length === 1">
|
||||
{{ record.startUsers[0].nickname }}
|
||||
</span>
|
||||
<span v-else-if="record.startDepts?.length === 1">
|
||||
{{ record.startDepts[0].name }}
|
||||
</span>
|
||||
<span v-else-if="record.startDepts?.length > 1">
|
||||
<Tooltip
|
||||
placement="top"
|
||||
:title="
|
||||
record.startDepts.map((dept: any) => dept.name).join('、')
|
||||
"
|
||||
>
|
||||
{{ record.startDepts[0].name }}等
|
||||
{{ record.startDepts.length }} 个部门可见
|
||||
</Tooltip>
|
||||
</span>
|
||||
<span v-else-if="record.startUsers?.length > 1">
|
||||
<Tooltip
|
||||
placement="top"
|
||||
:title="
|
||||
record.startUsers
|
||||
.map((user: any) => user.nickname)
|
||||
.join('、')
|
||||
"
|
||||
>
|
||||
{{ record.startUsers[0].nickname }}等
|
||||
{{ record.startUsers.length }} 人可见
|
||||
</Tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<!-- 流程类型列 -->
|
||||
<template v-else-if="column.key === 'type'">
|
||||
<!-- <DictTag :value="record.type" :type="DICT_TYPE.BPM_MODEL_TYPE" /> -->
|
||||
<!-- <Tag>{{ record.type }}</Tag> -->
|
||||
<DictTag :type="DICT_TYPE.BPM_MODEL_TYPE" :value="record.type" />
|
||||
</template>
|
||||
<!-- 表单信息列 -->
|
||||
<template v-else-if="column.key === 'formType'">
|
||||
<!-- TODO BpmModelFormType.NORMAL -->
|
||||
<Button
|
||||
v-if="record.formType === 10"
|
||||
type="link"
|
||||
@click="handleFormDetail(record)"
|
||||
>
|
||||
{{ record.formName }}
|
||||
</Button>
|
||||
<!-- TODO BpmModelFormType.CUSTOM -->
|
||||
<Button
|
||||
v-else-if="record.formType === 20"
|
||||
type="link"
|
||||
@click="handleFormDetail(record)"
|
||||
>
|
||||
{{ record.formCustomCreatePath }}
|
||||
</Button>
|
||||
<span v-else>暂无表单</span>
|
||||
</template>
|
||||
<!-- 最后发布列 -->
|
||||
<template v-else-if="column.key === 'deploymentTime'">
|
||||
<div class="flex items-center justify-center">
|
||||
<span v-if="record.processDefinition" class="w-[150px]">
|
||||
{{ formatDateTime(record.processDefinition.deploymentTime) }}
|
||||
</span>
|
||||
<Tag v-if="record.processDefinition">
|
||||
v{{ record.processDefinition.version }}
|
||||
</Tag>
|
||||
<Tag v-else color="warning">未部署</Tag>
|
||||
<Tag
|
||||
v-if="record.processDefinition?.suspensionState === 2"
|
||||
color="warning"
|
||||
class="ml-[10px]"
|
||||
>
|
||||
已停用
|
||||
</Tag>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 操作列 -->
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<div class="flex items-center justify-center">待实现</div>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.category-draggable-model {
|
||||
// ant-table-tbody 自定义样式
|
||||
:deep(.ant-table-tbody > tr > td) {
|
||||
overflow: hidden;
|
||||
border-bottom: none;
|
||||
}
|
||||
// ant-collapse-header 自定义样式
|
||||
:deep(.ant-collapse-header) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// 优化表格渲染性能
|
||||
:deep(.ant-table-tbody) {
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
// 折叠面板样式
|
||||
:deep(.ant-collapse-content-box) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="审批接入(业务表单)" url="https://doc.iocoder.cn/bpm/use-business-form/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<DocAlert
|
||||
title="审批接入(业务表单)"
|
||||
url="https://doc.iocoder.cn/bpm/use-business-form/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/oa/leave/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/oa/leave/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/oa/leave/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/oa/leave/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="流程表达式" url="https://doc.iocoder.cn/bpm/expression/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processExpression/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processExpression/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processExpression/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processExpression/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="流程发起、取消、重新发起" url="https://doc.iocoder.cn/bpm/process-instance/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<DocAlert
|
||||
title="流程发起、取消、重新发起"
|
||||
url="https://doc.iocoder.cn/bpm/process-instance/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/manager/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/manager/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/manager/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processInstance/manager/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="执行监听器、任务监听器" url="https://doc.iocoder.cn/bpm/listener/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<DocAlert
|
||||
title="执行监听器、任务监听器"
|
||||
url="https://doc.iocoder.cn/bpm/listener/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processListener/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processListener/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processListener/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/processListener/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="审批转办、委派、抄送" url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<DocAlert
|
||||
title="审批转办、委派、抄送"
|
||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/copy/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/copy/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/copy/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/copy/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
|
||||
<DocAlert
|
||||
title="审批通过、不通过、驳回"
|
||||
url="https://doc.iocoder.cn/bpm/task-todo-done/"
|
||||
/>
|
||||
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
||||
<DocAlert title="审批转办、委派、抄送" url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/" />
|
||||
<DocAlert
|
||||
title="审批转办、委派、抄送"
|
||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
||||
/>
|
||||
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/done/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/done/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/done/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/done/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/manager/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/manager/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/manager/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/manager/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,21 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
|
||||
<DocAlert
|
||||
title="审批通过、不通过、驳回"
|
||||
url="https://doc.iocoder.cn/bpm/task-todo-done/"
|
||||
/>
|
||||
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
||||
<DocAlert title="审批转办、委派、抄送" url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/" />
|
||||
<DocAlert
|
||||
title="审批转办、委派、抄送"
|
||||
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
|
||||
/>
|
||||
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
|
||||
<Button danger type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3">
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button type="link" target="_blank" href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/todo/index">
|
||||
可参考 https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/todo/index 代码,pull request 贡献给我们!
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/todo/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/bpm/task/todo/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
</Page>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
895
apps/web-antd/src/views/crm/backlog/data.ts
Normal file
895
apps/web-antd/src/views/crm/backlog/data.ts
Normal file
@@ -0,0 +1,895 @@
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { CrmContractApi } from '#/api/crm/contract';
|
||||
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
export interface LeftSideItem {
|
||||
name: string;
|
||||
menu: string;
|
||||
count: Ref<number>;
|
||||
}
|
||||
|
||||
/** 跟进状态 */
|
||||
export const FOLLOWUP_STATUS = [
|
||||
{ label: '待跟进', value: false },
|
||||
{ label: '已跟进', value: true },
|
||||
];
|
||||
|
||||
/** 归属范围 */
|
||||
export const SCENE_TYPES = [
|
||||
{ label: '我负责的', value: 1 },
|
||||
{ label: '我参与的', value: 2 },
|
||||
{ label: '下属负责的', value: 3 },
|
||||
];
|
||||
|
||||
/** 联系状态 */
|
||||
export const CONTACT_STATUS = [
|
||||
{ label: '今日需联系', value: 1 },
|
||||
{ label: '已逾期', value: 2 },
|
||||
{ label: '已联系', value: 3 },
|
||||
];
|
||||
|
||||
/** 审批状态 */
|
||||
export const AUDIT_STATUS = [
|
||||
{ label: '待审批', value: 10 },
|
||||
{ label: '审核通过', value: 20 },
|
||||
{ label: '审核不通过', value: 30 },
|
||||
];
|
||||
|
||||
/** 回款提醒类型 */
|
||||
export const RECEIVABLE_REMIND_TYPE = [
|
||||
{ label: '待回款', value: 1 },
|
||||
{ label: '已逾期', value: 2 },
|
||||
{ label: '已回款', value: 3 },
|
||||
];
|
||||
|
||||
/** 合同过期状态 */
|
||||
export const CONTRACT_EXPIRY_TYPE = [
|
||||
{ label: '即将过期', value: 1 },
|
||||
{ label: '已过期', value: 2 },
|
||||
];
|
||||
|
||||
export const useLeftSides = (
|
||||
customerTodayContactCount: Ref<number>,
|
||||
clueFollowCount: Ref<number>,
|
||||
customerFollowCount: Ref<number>,
|
||||
customerPutPoolRemindCount: Ref<number>,
|
||||
contractAuditCount: Ref<number>,
|
||||
contractRemindCount: Ref<number>,
|
||||
receivableAuditCount: Ref<number>,
|
||||
receivablePlanRemindCount: Ref<number>,
|
||||
): LeftSideItem[] => {
|
||||
return [
|
||||
{
|
||||
name: '今日需联系客户',
|
||||
menu: 'customerTodayContact',
|
||||
count: customerTodayContactCount,
|
||||
},
|
||||
{
|
||||
name: '分配给我的线索',
|
||||
menu: 'clueFollow',
|
||||
count: clueFollowCount,
|
||||
},
|
||||
{
|
||||
name: '分配给我的客户',
|
||||
menu: 'customerFollow',
|
||||
count: customerFollowCount,
|
||||
},
|
||||
{
|
||||
name: '待进入公海的客户',
|
||||
menu: 'customerPutPoolRemind',
|
||||
count: customerPutPoolRemindCount,
|
||||
},
|
||||
{
|
||||
name: '待审核合同',
|
||||
menu: 'contractAudit',
|
||||
count: contractAuditCount,
|
||||
},
|
||||
{
|
||||
name: '待审核回款',
|
||||
menu: 'receivableAudit',
|
||||
count: receivableAuditCount,
|
||||
},
|
||||
{
|
||||
name: '待回款提醒',
|
||||
menu: 'receivablePlanRemind',
|
||||
count: receivablePlanRemindCount,
|
||||
},
|
||||
{
|
||||
name: '即将到期的合同',
|
||||
menu: 'contractRemind',
|
||||
count: contractRemindCount,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/** 分配给我的线索 列表的搜索表单 */
|
||||
export function useClueFollowFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'followUpStatus',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: FOLLOWUP_STATUS,
|
||||
},
|
||||
defaultValue: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 分配给我的线索 列表的字段 */
|
||||
export function useClueFollowColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'name',
|
||||
title: '线索名称',
|
||||
minWidth: 160,
|
||||
fixed: 'left',
|
||||
slots: { default: 'name' },
|
||||
},
|
||||
{
|
||||
field: 'source',
|
||||
title: '线索来源',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
title: '手机',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'telephone',
|
||||
title: '电话',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
title: '邮箱',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'detailAddress',
|
||||
title: '地址',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'industryId',
|
||||
title: '客户行业',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'level',
|
||||
title: '客户级别',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contactNextTime',
|
||||
title: '下次联系时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'contactLastTime',
|
||||
title: '最后跟进时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'contactLastContent',
|
||||
title: '最后跟进记录',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserName',
|
||||
title: '负责人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserDeptName',
|
||||
title: '所属部门',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'updateTime',
|
||||
title: '更新时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 100,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 合同审核列表的搜索表单 */
|
||||
export function useContractAuditFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'auditStatus',
|
||||
label: '合同状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: AUDIT_STATUS,
|
||||
},
|
||||
defaultValue: 10,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 合同提醒列表的搜索表单 */
|
||||
export function useContractRemindFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'expiryType',
|
||||
label: '到期状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: CONTRACT_EXPIRY_TYPE,
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 合同审核列表的字段 */
|
||||
export function useContractColumns<T = CrmContractApi.Contract>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'no',
|
||||
title: '合同编号',
|
||||
minWidth: 160,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '合同名称',
|
||||
minWidth: 160,
|
||||
slots: {
|
||||
default: 'name',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'customerName',
|
||||
title: '客户名称',
|
||||
minWidth: 160,
|
||||
slots: {
|
||||
default: 'customerName',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'businessName',
|
||||
title: '商机名称',
|
||||
minWidth: 160,
|
||||
slots: {
|
||||
default: 'businessName',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '合同金额(元)',
|
||||
minWidth: 120,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'orderDate',
|
||||
title: '下单时间',
|
||||
minWidth: 120,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'startTime',
|
||||
title: '合同开始时间',
|
||||
minWidth: 120,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '合同结束时间',
|
||||
minWidth: 120,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'contactName',
|
||||
title: '客户签约人',
|
||||
minWidth: 130,
|
||||
slots: {
|
||||
default: 'contactName',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'signUserName',
|
||||
title: '公司签约人',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'totalReceivablePrice',
|
||||
title: '已回款金额(元)',
|
||||
minWidth: 140,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'noReceivablePrice',
|
||||
title: '未回款金额(元)',
|
||||
minWidth: 120,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'contactLastTime',
|
||||
title: '最后跟进时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'ownerUserName',
|
||||
title: '负责人',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserDeptName',
|
||||
title: '所属部门',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'updateTime',
|
||||
title: '更新时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'auditStatus',
|
||||
title: '合同状态',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_AUDIT_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
minWidth: 130,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
nameField: 'no',
|
||||
nameTitle: '合同编号',
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
options: [
|
||||
{
|
||||
code: 'processDetail',
|
||||
show: hasAccessByCodes(['crm:contract:update']),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 客户跟进列表的搜索表单 */
|
||||
export function useCustomerFollowFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'followUpStatus',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: FOLLOWUP_STATUS,
|
||||
},
|
||||
defaultValue: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 待进入公海客户列表的搜索表单 */
|
||||
export function useCustomerPutPoolFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'sceneType',
|
||||
label: '归属',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: SCENE_TYPES,
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 今日需联系客户列表的搜索表单 */
|
||||
export function useCustomerTodayContactFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'contactStatus',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: CONTACT_STATUS,
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
{
|
||||
fieldName: 'sceneType',
|
||||
label: '归属',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: SCENE_TYPES,
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 客户列表的字段 */
|
||||
export function useCustomerColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'name',
|
||||
title: '客户名称',
|
||||
minWidth: 160,
|
||||
slots: {
|
||||
default: 'name',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'source',
|
||||
title: '客户来源',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'mobile',
|
||||
title: '手机',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'telephone',
|
||||
title: '电话',
|
||||
minWidth: 130,
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
title: '邮箱',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'level',
|
||||
title: '客户级别',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'industryId',
|
||||
title: '客户行业',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contactNextTime',
|
||||
title: '下次联系时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'lockStatus',
|
||||
title: '锁定状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'dealStatus',
|
||||
title: '成交状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contactLastTime',
|
||||
title: '最后跟进时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'contactLastContent',
|
||||
title: '最后跟进记录',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'detailAddress',
|
||||
title: '地址',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'poolDay',
|
||||
title: '距离进入公海天数',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserName',
|
||||
title: '负责人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserDeptName',
|
||||
title: '所属部门',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'updateTime',
|
||||
title: '更新时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 100,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 回款审核列表的搜索表单 */
|
||||
export function useReceivableAuditFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'auditStatus',
|
||||
label: '合同状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: AUDIT_STATUS,
|
||||
},
|
||||
defaultValue: 10,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 回款审核列表的字段 */
|
||||
export function useReceivableAuditColumns<T = CrmReceivableApi.Receivable>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'no',
|
||||
title: '回款编号',
|
||||
minWidth: 180,
|
||||
fixed: 'left',
|
||||
slots: {
|
||||
default: 'no',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'customerName',
|
||||
title: '客户名称',
|
||||
minWidth: 120,
|
||||
slots: {
|
||||
default: 'customerName',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contractNo',
|
||||
title: '合同编号',
|
||||
minWidth: 180,
|
||||
slots: {
|
||||
default: 'contractNo',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'returnTime',
|
||||
title: '回款日期',
|
||||
minWidth: 150,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '回款金额(元)',
|
||||
minWidth: 140,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'returnType',
|
||||
title: '回款方式',
|
||||
minWidth: 130,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'contract.totalPrice',
|
||||
title: '合同金额(元)',
|
||||
minWidth: 140,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'ownerUserName',
|
||||
title: '负责人',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserDeptName',
|
||||
title: '所属部门',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'updateTime',
|
||||
title: '更新时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'auditStatus',
|
||||
title: '回款状态',
|
||||
minWidth: 120,
|
||||
fixed: 'right',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_AUDIT_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
nameField: 'name',
|
||||
nameTitle: '角色',
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
options: [
|
||||
{
|
||||
code: 'processDetail',
|
||||
text: '查看审批',
|
||||
show: hasAccessByCodes(['crm:receivable:update']),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 回款计划提醒列表的搜索表单 */
|
||||
export function useReceivablePlanRemindFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'remindType',
|
||||
label: '合同状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: RECEIVABLE_REMIND_TYPE,
|
||||
},
|
||||
defaultValue: 1,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 回款计划提醒列表的字段 */
|
||||
export function useReceivablePlanRemindColumns<T = CrmReceivableApi.Receivable>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'customerName',
|
||||
title: '客户名称',
|
||||
minWidth: 160,
|
||||
fixed: 'left',
|
||||
slots: {
|
||||
default: 'customerName',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'contractNo',
|
||||
title: '合同编号',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'period',
|
||||
title: '期数',
|
||||
minWidth: 160,
|
||||
slots: {
|
||||
default: 'period',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '计划回款金额(元)',
|
||||
minWidth: 120,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'returnTime',
|
||||
title: '计划回款日期',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'remindDays',
|
||||
title: '提前几天提醒',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'remindTime',
|
||||
title: '提醒日期',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'returnType',
|
||||
title: '回款方式',
|
||||
minWidth: 120,
|
||||
fixed: 'right',
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
title: '备注',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'ownerUserName',
|
||||
title: '负责人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'receivable.price',
|
||||
title: '实际回款金额(元)',
|
||||
minWidth: 160,
|
||||
formatter: 'formatAmount',
|
||||
},
|
||||
{
|
||||
field: 'receivable.returnTime',
|
||||
title: '实际回款日期',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'updateTime',
|
||||
title: '更新时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'creatorName',
|
||||
title: '创建人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
width: 140,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
nameField: 'customerName',
|
||||
nameTitle: '客户名称',
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
options: [
|
||||
{
|
||||
code: 'receivableForm',
|
||||
text: '创建回款',
|
||||
show: hasAccessByCodes(['crm:receivable:create']),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,34 +1,121 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onActivated, onMounted, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Badge, Card, List } from 'ant-design-vue';
|
||||
|
||||
import * as ClueApi from '#/api/crm/clue';
|
||||
import * as ContractApi from '#/api/crm/contract';
|
||||
import * as CustomerApi from '#/api/crm/customer';
|
||||
import * as ReceivableApi from '#/api/crm/receivable';
|
||||
import * as ReceivablePlanApi from '#/api/crm/receivable/plan';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
import { useLeftSides } from './data';
|
||||
import ClueFollowList from './modules/ClueFollowList.vue';
|
||||
import ContractAuditList from './modules/ContractAuditList.vue';
|
||||
import ContractRemindList from './modules/ContractRemindList.vue';
|
||||
import CustomerFollowList from './modules/CustomerFollowList.vue';
|
||||
import CustomerPutPoolRemindList from './modules/CustomerPutPoolRemindList.vue';
|
||||
import CustomerTodayContactList from './modules/CustomerTodayContactList.vue';
|
||||
import ReceivableAuditList from './modules/ReceivableAuditList.vue';
|
||||
import ReceivablePlanRemindList from './modules/ReceivablePlanRemindList.vue';
|
||||
|
||||
defineOptions({ name: 'CrmBacklog' });
|
||||
|
||||
const leftMenu = ref('customerTodayContact');
|
||||
|
||||
const clueFollowCount = ref(0);
|
||||
const customerFollowCount = ref(0);
|
||||
const customerPutPoolRemindCount = ref(0);
|
||||
const customerTodayContactCount = ref(0);
|
||||
const contractAuditCount = ref(0);
|
||||
const contractRemindCount = ref(0);
|
||||
const receivableAuditCount = ref(0);
|
||||
const receivablePlanRemindCount = ref(0);
|
||||
|
||||
const leftSides = useLeftSides(
|
||||
customerTodayContactCount,
|
||||
clueFollowCount,
|
||||
customerFollowCount,
|
||||
customerPutPoolRemindCount,
|
||||
contractAuditCount,
|
||||
contractRemindCount,
|
||||
receivableAuditCount,
|
||||
receivablePlanRemindCount,
|
||||
);
|
||||
|
||||
const currentComponent = computed(() => {
|
||||
const components = {
|
||||
customerTodayContact: CustomerTodayContactList,
|
||||
clueFollow: ClueFollowList,
|
||||
contractAudit: ContractAuditList,
|
||||
receivableAudit: ReceivableAuditList,
|
||||
contractRemind: ContractRemindList,
|
||||
customerFollow: CustomerFollowList,
|
||||
customerPutPoolRemind: CustomerPutPoolRemindList,
|
||||
receivablePlanRemind: ReceivablePlanRemindList,
|
||||
} as const;
|
||||
return components[leftMenu.value as keyof typeof components];
|
||||
});
|
||||
|
||||
/** 侧边点击 */
|
||||
function sideClick(item: { menu: string }) {
|
||||
leftMenu.value = item.menu;
|
||||
}
|
||||
|
||||
/** 获取数量 */
|
||||
async function getCount() {
|
||||
customerTodayContactCount.value =
|
||||
await CustomerApi.getTodayContactCustomerCount();
|
||||
customerPutPoolRemindCount.value =
|
||||
await CustomerApi.getPutPoolRemindCustomerCount();
|
||||
customerFollowCount.value = await CustomerApi.getFollowCustomerCount();
|
||||
clueFollowCount.value = await ClueApi.getFollowClueCount();
|
||||
contractAuditCount.value = await ContractApi.getAuditContractCount();
|
||||
contractRemindCount.value = await ContractApi.getRemindContractCount();
|
||||
receivableAuditCount.value = await ReceivableApi.getAuditReceivableCount();
|
||||
receivablePlanRemindCount.value =
|
||||
await ReceivablePlanApi.getReceivablePlanRemindCount();
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
onActivated(async () => {
|
||||
getCount();
|
||||
});
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
getCount();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert
|
||||
title="【通用】跟进记录、待办事项"
|
||||
url="https://doc.iocoder.cn/crm/follow-up/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/backlog/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/backlog/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【通用】跟进记录、待办事项"
|
||||
url="https://doc.iocoder.cn/crm/follow-up/"
|
||||
/>
|
||||
</template>
|
||||
<div class="flex h-full w-full">
|
||||
<Card class="w-1/5">
|
||||
<List item-layout="horizontal" :data-source="leftSides">
|
||||
<template #renderItem="{ item }">
|
||||
<List.Item>
|
||||
<List.Item.Meta>
|
||||
<template #title>
|
||||
<a @click="sideClick(item)"> {{ item.name }} </a>
|
||||
</template>
|
||||
</List.Item.Meta>
|
||||
<template #extra v-if="item.count.value && item.count.value > 0">
|
||||
<Badge :count="item.count.value" />
|
||||
</template>
|
||||
</List.Item>
|
||||
</template>
|
||||
</List>
|
||||
</Card>
|
||||
<component class="ml-4 w-4/5" :is="currentComponent" />
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<!-- 分配给我的线索 -->
|
||||
<script lang="ts" setup>
|
||||
import type { CrmClueApi } from '#/api/crm/clue';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCluePage } from '#/api/crm/clue';
|
||||
|
||||
import { useClueFollowColumns, useClueFollowFormSchema } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 打开线索详情 */
|
||||
function onDetail(row: CrmClueApi.Clue) {
|
||||
push({ name: 'CrmClueDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useClueFollowFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useClueFollowColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCluePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
transformStatus: false,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="分配给我的线索">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,111 @@
|
||||
<!-- 待审核合同 -->
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { CrmContractApi } from '#/api/crm/contract';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getContractPage } from '#/api/crm/contract';
|
||||
|
||||
import { useContractAuditFormSchema, useContractColumns } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 查看审批 */
|
||||
function openProcessDetail(row: CrmContractApi.Contract) {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: { id: row.processInstanceId },
|
||||
});
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
function openContractDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmContractDetail', params: { id: row.id } });
|
||||
}
|
||||
/** 打开客户详情 */
|
||||
function openCustomerDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
function openContactDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmContactDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
function openBusinessDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmBusinessDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<CrmContractApi.Contract>) {
|
||||
switch (code) {
|
||||
case 'processDetail': {
|
||||
openProcessDetail(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useContractAuditFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useContractColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getContractPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
sceneType: 1, // 我负责的
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="待审核合同">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="openContractDetail(row)">
|
||||
{{ row.name }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #customerName="{ row }">
|
||||
<Button type="link" @click="openCustomerDetail(row)">
|
||||
{{ row.customerName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #businessName="{ row }">
|
||||
<Button type="link" @click="openBusinessDetail(row)">
|
||||
{{ row.businessName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #contactName="{ row }">
|
||||
<Button type="link" @click="openContactDetail(row)">
|
||||
{{ row.contactName }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,111 @@
|
||||
<!-- 即将到期的合同 -->
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { CrmContractApi } from '#/api/crm/contract';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getContractPage } from '#/api/crm/contract';
|
||||
|
||||
import { useContractColumns, useContractRemindFormSchema } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 查看审批 */
|
||||
function openProcessDetail(row: CrmContractApi.Contract) {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: { id: row.processInstanceId },
|
||||
});
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
function openContractDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmContractDetail', params: { id: row.id } });
|
||||
}
|
||||
/** 打开客户详情 */
|
||||
function openCustomerDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开联系人详情 */
|
||||
function openContactDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmContactDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开商机详情 */
|
||||
function openBusinessDetail(row: CrmContractApi.Contract) {
|
||||
push({ name: 'CrmBusinessDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<CrmContractApi.Contract>) {
|
||||
switch (code) {
|
||||
case 'processDetail': {
|
||||
openProcessDetail(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useContractRemindFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useContractColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getContractPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
sceneType: 1, // 自己负责的
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="即将到期的合同">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="openContractDetail(row)">
|
||||
{{ row.name }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #customerName="{ row }">
|
||||
<Button type="link" @click="openCustomerDetail(row)">
|
||||
{{ row.customerName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #businessName="{ row }">
|
||||
<Button type="link" @click="openBusinessDetail(row)">
|
||||
{{ row.businessName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #contactName="{ row }">
|
||||
<Button type="link" @click="openContactDetail(row)">
|
||||
{{ row.contactName }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<!-- 分配给我的客户 -->
|
||||
<script lang="ts" setup>
|
||||
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCustomerPage } from '#/api/crm/customer';
|
||||
|
||||
import { useCustomerColumns, useCustomerFollowFormSchema } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 打开客户详情 */
|
||||
function onDetail(row: CrmCustomerApi.Customer) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useCustomerFollowFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useCustomerColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCustomerPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
sceneType: 1,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="分配给我的客户">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<!-- 待进入公海的客户 -->
|
||||
<script lang="ts" setup>
|
||||
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCustomerPage } from '#/api/crm/customer';
|
||||
|
||||
import { useCustomerColumns, useCustomerPutPoolFormSchema } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 打开客户详情 */
|
||||
function onDetail(row: CrmCustomerApi.Customer) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useCustomerPutPoolFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useCustomerColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCustomerPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
pool: true, // 固定 公海参数为 true
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="待进入公海的客户">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<!-- 今日需联系客户 -->
|
||||
<script lang="ts" setup>
|
||||
import type { CrmCustomerApi } from '#/api/crm/customer';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getCustomerPage } from '#/api/crm/customer';
|
||||
|
||||
import { useCustomerColumns, useCustomerTodayContactFormSchema } from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 打开客户详情 */
|
||||
function onDetail(row: CrmCustomerApi.Customer) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useCustomerTodayContactFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useCustomerColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getCustomerPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
pool: null, // 是否公海数据
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="今日需联系客户">
|
||||
<template #name="{ row }">
|
||||
<Button type="link" @click="onDetail(row)">{{ row.name }}</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,104 @@
|
||||
<!-- 待审核回款 -->
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getReceivablePage } from '#/api/crm/receivable';
|
||||
|
||||
import {
|
||||
useReceivableAuditColumns,
|
||||
useReceivableAuditFormSchema,
|
||||
} from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 查看审批 */
|
||||
function openProcessDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: { id: row.processInstanceId },
|
||||
});
|
||||
}
|
||||
|
||||
/** 打开回款详情 */
|
||||
function openDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({ name: 'CrmReceivableDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
function openCustomerDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||
}
|
||||
|
||||
/** 打开合同详情 */
|
||||
function openContractDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({ name: 'CrmContractDetail', params: { id: row.contractId } });
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<CrmReceivableApi.Receivable>) {
|
||||
switch (code) {
|
||||
case 'processDetail': {
|
||||
openProcessDetail(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useReceivableAuditFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useReceivableAuditColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getReceivablePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="待审核回款">
|
||||
<template #no="{ row }">
|
||||
<Button type="link" @click="openDetail(row)">
|
||||
{{ row.no }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #customerName="{ row }">
|
||||
<Button type="link" @click="openCustomerDetail(row)">
|
||||
{{ row.customerName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #contractNo="{ row }">
|
||||
<Button type="link" @click="openContractDetail(row)">
|
||||
{{ row.contractNo }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
@@ -0,0 +1,90 @@
|
||||
<!-- 待回款提醒 -->
|
||||
<script lang="ts" setup>
|
||||
import type { OnActionClickParams } from '#/adapter/vxe-table';
|
||||
import type { CrmReceivableApi } from '#/api/crm/receivable';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getReceivablePage } from '#/api/crm/receivable';
|
||||
|
||||
import {
|
||||
useReceivablePlanRemindColumns,
|
||||
useReceivablePlanRemindFormSchema,
|
||||
} from '../data';
|
||||
|
||||
const { push } = useRouter();
|
||||
|
||||
/** 打开回款详情 */
|
||||
function openDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({ name: 'CrmReceivableDetail', params: { id: row.id } });
|
||||
}
|
||||
|
||||
/** 打开客户详情 */
|
||||
function openCustomerDetail(row: CrmReceivableApi.Receivable) {
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||
}
|
||||
|
||||
/** 创建回款 */
|
||||
function openReceivableForm(row: CrmReceivableApi.Receivable) {
|
||||
// Todo: 打开创建回款
|
||||
push({ name: 'CrmCustomerDetail', params: { id: row.customerId } });
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<CrmReceivableApi.Receivable>) {
|
||||
switch (code) {
|
||||
case 'receivableForm': {
|
||||
openReceivableForm(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useReceivablePlanRemindFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useReceivablePlanRemindColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getReceivablePage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid table-title="待回款提醒">
|
||||
<template #customerName="{ row }">
|
||||
<Button type="link" @click="openCustomerDetail(row)">
|
||||
{{ row.customerName }}
|
||||
</Button>
|
||||
</template>
|
||||
<template #period="{ row }">
|
||||
<Button type="link" @click="openDetail(row)">{{ row.period }}</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</template>
|
||||
131
apps/web-antd/src/views/crm/business/status/data.ts
Normal file
131
apps/web-antd/src/views/crm/business/status/data.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
import { getRangePickerDefaultProps } from '@vben/utils';
|
||||
|
||||
import { z } from '#/adapter/form';
|
||||
import { CommonStatusEnum } from '#/utils/constants';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 新增/修改的表单 */
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'id',
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
show: () => false,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '状态组名',
|
||||
component: 'Input',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'deptIds',
|
||||
label: '应用部门',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
multiple: true,
|
||||
treeCheckable: true,
|
||||
showCheckedStrategy: 'SHOW_PARENT',
|
||||
placeholder: '请选择应用部门',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: z.number().default(CommonStatusEnum.ENABLE),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '状态组名',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
...getRangePickerDefaultProps(),
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = CrmBusinessStatusApi.BusinessStatus>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'name',
|
||||
title: '状态组名',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'deptNames',
|
||||
title: '应用部门',
|
||||
minWidth: 200,
|
||||
formatter: ({ cellValue }) => {
|
||||
return cellValue?.length > 0 ? cellValue.join(' ') : '全公司';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'creator',
|
||||
title: '创建人',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
width: 160,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
name: 'TableAction',
|
||||
props: {
|
||||
actions: [
|
||||
{
|
||||
label: '编辑',
|
||||
code: 'edit',
|
||||
show: hasAccessByCodes(['crm:business-status:update']),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
code: 'delete',
|
||||
show: hasAccessByCodes(['crm:business-status:delete']),
|
||||
},
|
||||
],
|
||||
onActionClick,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,38 +1,137 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
deleteBusinessStatus,
|
||||
getBusinessStatusPage,
|
||||
} from '#/api/crm/business/status';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 创建商机状态 */
|
||||
function onCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 删除商机状态 */
|
||||
async function onDelete(row: CrmBusinessStatusApi.BusinessStatus) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteBusinessStatus(row.id as number);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
} catch {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 编辑商机状态 */
|
||||
function onEdit(row: CrmBusinessStatusApi.BusinessStatus) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({
|
||||
code,
|
||||
row,
|
||||
}: OnActionClickParams<CrmBusinessStatusApi.BusinessStatus>) {
|
||||
switch (code) {
|
||||
case 'delete': {
|
||||
onDelete(row);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
onEdit(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getBusinessStatusPage({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<CrmBusinessStatusApi.BusinessStatus>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert
|
||||
title="【商机】商机管理、商机状态"
|
||||
url="https://doc.iocoder.cn/crm/business/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="【通用】数据权限"
|
||||
url="https://doc.iocoder.cn/crm/permission/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/business/status/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/crm/business/status/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="【商机】商机管理、商机状态"
|
||||
url="https://doc.iocoder.cn/crm/business/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="【通用】数据权限"
|
||||
url="https://doc.iocoder.cn/crm/permission/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<FormModal @success="onRefresh" />
|
||||
<Grid table-title="商机状态列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['crm:business-status:create']"
|
||||
>
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', ['商机状态']) }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
94
apps/web-antd/src/views/crm/business/status/modules/form.vue
Normal file
94
apps/web-antd/src/views/crm/business/status/modules/form.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CrmBusinessStatusApi } from '#/api/crm/business/status';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
createBusinessStatus,
|
||||
getBusinessStatus,
|
||||
updateBusinessStatus,
|
||||
} from '#/api/crm/business/status';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<CrmBusinessStatusApi.BusinessStatusType>();
|
||||
const getTitle = computed(() => {
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['商机状态'])
|
||||
: $t('ui.actionTitle.create', ['商机状态']);
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onConfirm() {
|
||||
const { valid } = await formApi.validate();
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data =
|
||||
(await formApi.getValues()) as CrmBusinessStatusApi.BusinessStatusType;
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateBusinessStatus(data)
|
||||
: createBusinessStatus(data));
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.operationSuccess'),
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<CrmBusinessStatusApi.BusinessStatusType>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getBusinessStatus(data.id as number);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle" class="w-1/2">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -1,66 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, message, notification, Space } from 'ant-design-vue';
|
||||
|
||||
type NotificationType = 'error' | 'info' | 'success' | 'warning';
|
||||
|
||||
function info() {
|
||||
message.info('How many roads must a man walk down');
|
||||
}
|
||||
|
||||
function error() {
|
||||
message.error({
|
||||
content: 'Once upon a time you dressed so fine',
|
||||
duration: 2500,
|
||||
});
|
||||
}
|
||||
|
||||
function warning() {
|
||||
message.warning('How many roads must a man walk down');
|
||||
}
|
||||
function success() {
|
||||
message.success('Cause you walked hand in hand With another man in my place');
|
||||
}
|
||||
|
||||
function notify(type: NotificationType) {
|
||||
notification[type]({
|
||||
duration: 2500,
|
||||
message: '说点啥呢',
|
||||
type,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
description="支持多语言,主题功能集成切换等"
|
||||
title="Ant Design Vue组件使用演示"
|
||||
>
|
||||
<Card class="mb-5" title="按钮">
|
||||
<Space>
|
||||
<Button>Default</Button>
|
||||
<Button type="primary"> Primary </Button>
|
||||
<Button> Info </Button>
|
||||
<Button danger> Error </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
<Card class="mb-5" title="Message">
|
||||
<Space>
|
||||
<Button @click="info"> 信息 </Button>
|
||||
<Button danger @click="error"> 错误 </Button>
|
||||
<Button @click="warning"> 警告 </Button>
|
||||
<Button @click="success"> 成功 </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
<Card class="mb-5" title="Notification">
|
||||
<Space>
|
||||
<Button @click="notify('info')"> 信息 </Button>
|
||||
<Button danger @click="notify('error')"> 错误 </Button>
|
||||
<Button @click="notify('warning')"> 警告 </Button>
|
||||
<Button @click="notify('success')"> 成功 </Button>
|
||||
</Space>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -210,14 +210,14 @@ initDataSourceConfig();
|
||||
<DocAlert
|
||||
title="代码生成(单表)"
|
||||
url="https://doc.iocoder.cn/new-feature/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(树表)"
|
||||
url="https://doc.iocoder.cn/new-feature/tree/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(主子表)"
|
||||
url="https://doc.iocoder.cn/new-feature/master-sub/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(树表)"
|
||||
url="https://doc.iocoder.cn/new-feature/tree/"
|
||||
/>
|
||||
<DocAlert
|
||||
title="代码生成(主子表)"
|
||||
url="https://doc.iocoder.cn/new-feature/master-sub/"
|
||||
/>
|
||||
<DocAlert title="单元测试" url="https://doc.iocoder.cn/unit-test/" />
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { watch } from 'vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import { useBasicInfoFormSchema } from '../data';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
import type { SystemDictTypeApi } from '#/api/system/dict/type';
|
||||
|
||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { Checkbox, Input, Select } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getSimpleDictTypeList } from '#/api/system/dict/type';
|
||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { useCodegenColumnTableColumns } from '../data';
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
});
|
||||
} finally {
|
||||
hideLoading();
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,6 +22,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -46,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -65,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -53,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -72,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -52,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -71,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo01Contact(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo02CategoryApi } from '#/api/infra/demo/demo02';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
@@ -36,7 +34,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
});
|
||||
return handleTree(data);
|
||||
},
|
||||
class: 'w-full',
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
|
||||
@@ -31,6 +31,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -58,7 +65,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -79,7 +86,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo02Category(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useDemo03CourseFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -54,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -73,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo03Course(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useDemo03GradeFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -54,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -73,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo03Grade(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -52,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -71,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo03Student(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
@@ -33,6 +33,13 @@ const demo03CourseFormRef = ref<InstanceType<typeof Demo03CourseForm>>();
|
||||
const demo03GradeFormRef = ref<InstanceType<typeof Demo03GradeForm>>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -54,9 +61,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||
// 拼接子表的数据
|
||||
// TODO @puhui999:字段对不上
|
||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
||||
data.demo03courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03grade = await demo03GradeFormRef.value?.getValues();
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateDemo03Student(data)
|
||||
@@ -69,7 +75,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -88,7 +94,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo03Student(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
@@ -33,6 +33,13 @@ const demo03CourseFormRef = ref<InstanceType<typeof Demo03CourseForm>>();
|
||||
const demo03GradeFormRef = ref<InstanceType<typeof Demo03GradeForm>>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -54,8 +61,8 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Student;
|
||||
// 拼接子表的数据
|
||||
data.demo03Courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03Grade = await demo03GradeFormRef.value?.getValues();
|
||||
data.demo03courses = demo03CourseFormRef.value?.getData();
|
||||
data.demo03grade = await demo03GradeFormRef.value?.getValues();
|
||||
try {
|
||||
await (formData.value?.id
|
||||
? updateDemo03Student(data)
|
||||
@@ -68,7 +75,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -87,7 +94,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo03Student(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -80,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -98,7 +98,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDemo01Contact(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
formData.value = data;
|
||||
|
||||
@@ -14,12 +14,17 @@ import { useFormSchema } from '../data';
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
hideLabel: true,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema().map((item) => ({ ...item, label: '' })), // 去除label
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
hideLabel: true,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -41,7 +46,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -36,7 +36,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE, 'number'),
|
||||
placeholder: '请选择存储器',
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
@@ -87,7 +86,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入主机端口',
|
||||
},
|
||||
|
||||
@@ -27,7 +27,11 @@ const getTitle = computed(() => {
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
@@ -55,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -74,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -65,7 +65,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入重试次数。设置为 0 时,不进行重试',
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -76,7 +75,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入重试间隔,单位:毫秒。设置为 0 时,无需间隔',
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -87,7 +85,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入监控超时时间,单位:毫秒',
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -29,7 +29,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = await getJobLog(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 获取下一次执行时间
|
||||
nextTimes.value = await getJobNextTimes(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,12 +22,16 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -49,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -68,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
187
apps/web-antd/src/views/pay/notify/data.ts
Normal file
187
apps/web-antd/src/views/pay/notify/data.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getAppList } from '#/api/pay/app';
|
||||
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'appId',
|
||||
label: '应用编号',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: async () => {
|
||||
const data = await getAppList();
|
||||
return data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
autoSelect: 'first',
|
||||
placeholder: '请选择数据源',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'type',
|
||||
label: '通知类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.PAY_NOTIFY_TYPE, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'dataId',
|
||||
label: '关联编号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '通知状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getDictOptions(DICT_TYPE.PAY_NOTIFY_STATUS, 'number'),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'merchantOrderId',
|
||||
label: '商户订单编号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = any>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '任务编号',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'appName',
|
||||
title: '应用编号',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'merchantOrderId',
|
||||
title: '商户订单编号',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
title: '通知类型',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PAY_NOTIFY_TYPE },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'dataId',
|
||||
title: '关联编号',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '通知状态',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PAY_NOTIFY_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'lastExecuteTime',
|
||||
title: '最后通知时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'nextNotifyTime',
|
||||
title: '下次通知时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'notifyTimes',
|
||||
title: '通知次数',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
props: {
|
||||
type: 'success',
|
||||
content: '{notifyTimes} / {maxNotifyTimes}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
options: [
|
||||
{
|
||||
code: 'detail',
|
||||
show: hasAccessByCodes(['pay:notify:query']),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 详情列表的字段 */
|
||||
export const detailColumns = [
|
||||
{
|
||||
label: '日志编号',
|
||||
prop: 'id',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
label: '通知状态',
|
||||
prop: 'status',
|
||||
key: 'status',
|
||||
},
|
||||
{
|
||||
label: '通知次数',
|
||||
prop: 'notifyTimes',
|
||||
key: 'notifyTimes',
|
||||
},
|
||||
{
|
||||
label: '通知时间',
|
||||
prop: 'lastExecuteTime',
|
||||
key: 'lastExecuteTime',
|
||||
},
|
||||
{
|
||||
label: '响应结果',
|
||||
prop: 'response',
|
||||
key: 'response',
|
||||
},
|
||||
];
|
||||
@@ -1,31 +1,78 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import * as PayNotifyApi from '#/api/pay/notify';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
|
||||
const [NotifyDetailModal, notifyDetailModalApi] = useVbenModal({
|
||||
connectedComponent: Detail,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function onDetail(row: any) {
|
||||
notifyDetailModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<any>) {
|
||||
switch (code) {
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await PayNotifyApi.getNotifyTaskPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<any>,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="支付功能开启" url="https://doc.iocoder.cn/pay/build/" />
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/notify/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/notify/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="支付功能开启" url="https://doc.iocoder.cn/pay/build/" />
|
||||
</template>
|
||||
<NotifyDetailModal @success="onRefresh" />
|
||||
<Grid table-title="支付通知列表" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
107
apps/web-antd/src/views/pay/notify/modules/detail.vue
Normal file
107
apps/web-antd/src/views/pay/notify/modules/detail.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, Divider, Table, Tag } from 'ant-design-vue';
|
||||
|
||||
import { getNotifyTaskDetail } from '#/api/pay/notify';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
import { detailColumns } from '../data';
|
||||
|
||||
const formData = ref();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getNotifyTaskDetail(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = (id: number) => {
|
||||
modalApi.setData({ id }).open();
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
title="通知详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="商户订单编号">
|
||||
<Tag>{{ formData?.merchantOrderId }}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="通知状态">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.PAY_NOTIFY_STATUS"
|
||||
:value="formData?.status"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="应用编号">
|
||||
{{ formData?.appId }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="应用名称">
|
||||
{{ formData?.appName }}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="关联编号">
|
||||
{{ formData?.dataId }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="通知类型">
|
||||
<DictTag :type="DICT_TYPE.PAY_NOTIFY_TYPE" :value="formData?.type" />
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="通知次数">
|
||||
{{ formData?.notifyTimes }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="最大通知次数">
|
||||
{{ formData?.maxNotifyTimes }}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="最后通知时间">
|
||||
{{ formatDateTime(formData?.lastExecuteTime || '') }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="下次通知时间">
|
||||
{{ formatDateTime(formData?.nextNotifyTime || '') }}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="创建时间">
|
||||
{{ formatDateTime(formData?.createTime || '') }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="更新时间">
|
||||
{{ formatDateTime(formData?.updateTime || '') }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Descriptions bordered :column="1" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="回调日志">
|
||||
<Table :data="formData.logs" :columns="detailColumns" />
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Modal>
|
||||
</template>
|
||||
177
apps/web-antd/src/views/pay/refund/data.ts
Normal file
177
apps/web-antd/src/views/pay/refund/data.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { PayRefundApi } from '#/api/pay/refund';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
import { getAppList } from '#/api/pay/app';
|
||||
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '#/utils/dict';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'appId',
|
||||
label: '应用编号',
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: async () => {
|
||||
const data = await getAppList();
|
||||
return data.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
},
|
||||
autoSelect: 'first',
|
||||
placeholder: '请选择数据源',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'channelCode',
|
||||
label: '退款渠道',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'merchantOrderId',
|
||||
label: '商户支付单号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'merchantRefundId',
|
||||
label: '商户退款单号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'channelOrderNo',
|
||||
label: '渠道支付单号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'channelRefundNo',
|
||||
label: '渠道退款单号',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '退款状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: getIntDictOptions(DICT_TYPE.PAY_REFUND_STATUS),
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'createTime',
|
||||
label: '创建时间',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的字段 */
|
||||
export function useGridColumns<T = PayRefundApi.Refund>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 100,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 180,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'payPrice',
|
||||
title: '支付金额',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
props: {
|
||||
type: 'success',
|
||||
content: '¥{payPrice}',
|
||||
formatter: (value: number) => (value / 100).toFixed(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'refundPrice',
|
||||
title: '退款金额',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
props: {
|
||||
type: 'danger',
|
||||
content: '¥{refundPrice}',
|
||||
formatter: (value: number) => (value / 100).toFixed(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'merchantRefundId',
|
||||
title: '退款订单号',
|
||||
minWidth: 300,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
props: {
|
||||
type: 'info',
|
||||
content: '商户 {merchantRefundId}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'channelRefundNo',
|
||||
title: '渠道退款单号',
|
||||
minWidth: 200,
|
||||
cellRender: {
|
||||
name: 'CellTag',
|
||||
props: {
|
||||
type: 'success',
|
||||
content: '{channelRefundNo}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '退款状态',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.PAY_REFUND_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'operation',
|
||||
title: '操作',
|
||||
minWidth: 100,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
options: [
|
||||
{
|
||||
code: 'detail',
|
||||
show: hasAccessByCodes(['pay:refund:query']),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,34 +1,104 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import type {
|
||||
OnActionClickParams,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download } from '@vben/icons';
|
||||
import { downloadFileFromBlobPart } from '@vben/utils';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import * as RefundApi from '#/api/pay/refund';
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
</script>
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Detail from './modules/detail.vue';
|
||||
|
||||
const [RefundDetailModal, refundDetailModalApi] = useVbenModal({
|
||||
connectedComponent: Detail,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function onExport() {
|
||||
const data = await RefundApi.exportRefund(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '支付退款.xls', source: data });
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function onDetail(row: any) {
|
||||
refundDetailModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 表格操作按钮的回调函数 */
|
||||
function onActionClick({ code, row }: OnActionClickParams<any>) {
|
||||
switch (code) {
|
||||
case 'detail': {
|
||||
onDetail(row);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(onActionClick),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await RefundApi.getRefundPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<any>,
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert
|
||||
title="支付宝、微信退款接入"
|
||||
url="https://doc.iocoder.cn/pay/refund-demo/"
|
||||
/>
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/refund/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/pay/refund/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="支付宝、微信退款接入"
|
||||
url="https://doc.iocoder.cn/pay/refund-demo/"
|
||||
/>
|
||||
</template>
|
||||
<RefundDetailModal @success="onRefresh" />
|
||||
<Grid table-title="支付退款列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['pay:refund:export']"
|
||||
>
|
||||
<Download class="size-5" />
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
137
apps/web-antd/src/views/pay/refund/modules/detail.vue
Normal file
137
apps/web-antd/src/views/pay/refund/modules/detail.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PayRefundApi } from '#/api/pay/refund';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { Descriptions, Divider, Tag } from 'ant-design-vue';
|
||||
|
||||
import { getRefund } from '#/api/pay/refund';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { DICT_TYPE } from '#/utils/dict';
|
||||
|
||||
const formData = ref<PayRefundApi.Refund>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
const data = modalApi.getData<PayRefundApi.Refund>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getRefund(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = (id: number) => {
|
||||
modalApi.setData({ id }).open();
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
title="退款详情"
|
||||
class="w-1/2"
|
||||
:show-cancel-button="false"
|
||||
:show-confirm-button="false"
|
||||
>
|
||||
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="商户退款单号">
|
||||
<Tag size="small">{{ formData?.merchantRefundId }}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="渠道退款单号">
|
||||
<Tag type="success" size="small" v-if="formData?.channelRefundNo">
|
||||
{{ formData?.channelRefundNo }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="商户支付单号">
|
||||
<Tag size="small">{{ formData?.merchantOrderId }}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="渠道支付单号">
|
||||
<Tag type="success" size="small">
|
||||
{{ formData?.channelOrderNo }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="应用编号">
|
||||
{{ formData?.appId }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="应用名称">
|
||||
{{ formData?.appName }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="支付金额">
|
||||
<Tag type="success" size="small">
|
||||
¥{{ (formData?.payPrice || 0) / 100.0 }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款金额">
|
||||
<Tag size="mini" type="danger">
|
||||
¥{{ (formData?.refundPrice || 0) / 100.0 }}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款状态">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.PAY_REFUND_STATUS"
|
||||
:value="formData?.status"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款时间">
|
||||
{{ formatDateTime(formData?.successTime || '') }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间">
|
||||
{{ formatDateTime(formData?.createTime || '') }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="更新时间">
|
||||
{{ formatDateTime(formData?.updateTime || '') }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Divider />
|
||||
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="退款渠道">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.PAY_CHANNEL_CODE"
|
||||
:value="formData?.channelCode"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款原因">
|
||||
{{ formData?.reason }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="退款 IP">
|
||||
{{ formData?.userIp }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="通知 URL">
|
||||
{{ formData?.notifyUrl }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Divider />
|
||||
<Descriptions bordered :column="2" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="渠道错误码">
|
||||
{{ formData?.channelErrorCode }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="渠道错误码描述">
|
||||
{{ formData?.channelErrorMsg }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Descriptions bordered :column="1" size="middle" class="mx-4">
|
||||
<Descriptions.Item label="支付通道异步回调内容">
|
||||
<p class="whitespace-pre-wrap break-words">
|
||||
{{ formData?.channelNotifyData }}
|
||||
</p>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -1,31 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
defineOptions({ name: 'GoView' });
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const src = ref(
|
||||
`${import.meta.env.VITE_GOVIEW_URL}?accessToken=${accessStore.accessToken}&refreshToken=${accessStore.refreshToken}`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="大屏设计器" url="https://doc.iocoder.cn/report/screen/" />
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/goview/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/goview/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="大屏设计器"
|
||||
url="https://doc.iocoder.cn/report/screen/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<IFrame :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
28
apps/web-antd/src/views/report/jmreport/bi.vue
Normal file
28
apps/web-antd/src/views/report/jmreport/bi.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
defineOptions({ name: 'JimuBI' });
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const src = ref(
|
||||
`${import.meta.env.VITE_BASE_URL}/drag/list?token=${
|
||||
accessStore.refreshToken
|
||||
}`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="大屏设计器" url="https://doc.iocoder.cn/screen/" />
|
||||
</template>
|
||||
|
||||
<IFrame :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
@@ -1,31 +1,28 @@
|
||||
<script lang="ts" setup>
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { DocAlert } from '#/components/doc-alert';
|
||||
import { IFrame } from '#/components/iframe';
|
||||
|
||||
defineOptions({ name: 'JimuReport' });
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const src = ref(
|
||||
`${import.meta.env.VITE_BASE_URL}/jmreport/list?token=${
|
||||
accessStore.refreshToken
|
||||
}`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<DocAlert title="报表设计器" url="https://doc.iocoder.cn/report/" />
|
||||
<Button
|
||||
danger
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3"
|
||||
>
|
||||
该功能支持 Vue3 + element-plus 版本!
|
||||
</Button>
|
||||
<br />
|
||||
<Button
|
||||
type="link"
|
||||
target="_blank"
|
||||
href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/jmreport/index"
|
||||
>
|
||||
可参考
|
||||
https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/report/jmreport/index
|
||||
代码,pull request 贡献给我们!
|
||||
</Button>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="报表设计器" url="https://doc.iocoder.cn/report/" />
|
||||
</template>
|
||||
|
||||
<IFrame :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { SystemAreaApi } from '#/api/system/area';
|
||||
|
||||
/** 查询 IP 的表单 */
|
||||
|
||||
@@ -10,6 +10,13 @@ import { $t } from '#/locales';
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const [Form, { setFieldValue, validate, getValues }] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -33,7 +40,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn } from '#/adapter/vxe-table';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { SystemDeptApi } from '#/api/system/dept';
|
||||
|
||||
import { useAccess } from '@vben/access';
|
||||
@@ -40,7 +38,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
});
|
||||
return handleTree(data);
|
||||
},
|
||||
class: 'w-full',
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
@@ -64,7 +61,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入显示顺序',
|
||||
},
|
||||
@@ -76,7 +72,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleUserList,
|
||||
class: 'w-full',
|
||||
labelField: 'nickname',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择负责人',
|
||||
|
||||
@@ -22,6 +22,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -46,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -64,7 +71,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getDept(data.id);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
|
||||
@@ -38,13 +38,15 @@ export function useTypeFormSchema(): VbenFormSchema[] {
|
||||
fieldName: 'type',
|
||||
label: '字典类型',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入字典类型',
|
||||
componentProps: (values) => {
|
||||
return {
|
||||
placeholder: '请输入字典类型',
|
||||
disabled: !!values.id,
|
||||
};
|
||||
},
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
triggerFields: [''],
|
||||
disabled: ({ values }) => values.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -107,9 +109,8 @@ export function useTypeGridColumns<T = SystemDictTypeApi.DictType>(
|
||||
{
|
||||
field: 'name',
|
||||
title: '字典名称',
|
||||
minWidth: 180,
|
||||
minWidth: 200,
|
||||
},
|
||||
// TODO @芋艿:disable的;
|
||||
{
|
||||
field: 'type',
|
||||
title: '字典类型',
|
||||
@@ -118,7 +119,7 @@ export function useTypeGridColumns<T = SystemDictTypeApi.DictType>(
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 180,
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
@@ -204,7 +205,6 @@ export function useDataFormSchema(): VbenFormSchema[] {
|
||||
return {
|
||||
api: getSimpleDictTypeList,
|
||||
placeholder: '请输入字典类型',
|
||||
class: 'w-full',
|
||||
labelField: 'name',
|
||||
valueField: 'type',
|
||||
disabled: !!values.id,
|
||||
@@ -239,7 +239,6 @@ export function useDataFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入显示排序',
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -262,7 +261,6 @@ export function useDataFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
options: colorOptions,
|
||||
placeholder: '请选择颜色类型',
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,6 +28,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useDataFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -52,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -75,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
} else if (data && 'dictType' in data && data.dictType) {
|
||||
// 新增时,如果传入了dictType,则需要设置
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useTypeFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -50,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -71,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -26,12 +26,16 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -56,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -75,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -47,7 +47,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: async () => await getSimpleMailAccountList(),
|
||||
class: 'w-full',
|
||||
labelField: 'mail',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择邮箱账号',
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -53,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -72,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,6 +16,13 @@ const emit = defineEmits(['success']);
|
||||
const formData = ref<SystemMailTemplateApi.MailTemplate>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
showDefaultActions: false,
|
||||
});
|
||||
@@ -54,7 +61,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
} catch (error) {
|
||||
console.error('发送邮件失败', error);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
|
||||
@@ -44,7 +44,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
} as SystemMenuApi.Menu);
|
||||
return handleTree(data);
|
||||
},
|
||||
class: 'w-full',
|
||||
labelField: 'name',
|
||||
valueField: 'id',
|
||||
childrenField: 'children',
|
||||
@@ -167,7 +166,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'AutoComplete',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
class: 'w-full',
|
||||
filterOption(input: string, option: { value: string }) {
|
||||
return option.value.toLowerCase().includes(input.toLowerCase());
|
||||
},
|
||||
@@ -203,7 +201,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入显示顺序',
|
||||
},
|
||||
|
||||
@@ -22,6 +22,13 @@ const getTitle = computed(() =>
|
||||
);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -46,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -64,7 +71,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
data = await getMenu(data.id as number);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
}
|
||||
// 设置到 values
|
||||
@@ -75,7 +82,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :title="getTitle">
|
||||
<Modal class="w-[40%]" :title="getTitle">
|
||||
<Form class="mx-4" />
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -22,6 +22,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -46,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -67,7 +74,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = data;
|
||||
descApi.setState({ data });
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -68,7 +68,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
|
||||
'number',
|
||||
),
|
||||
class: 'w-full',
|
||||
placeholder: '请选择模板类型',
|
||||
},
|
||||
rules: 'required',
|
||||
@@ -201,7 +200,6 @@ export function useSendNotifyFormSchema(): VbenFormSchema[] {
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
api: getSimpleUserList,
|
||||
class: 'w-full',
|
||||
labelField: 'nickname',
|
||||
valueField: 'id',
|
||||
placeholder: '请选择接收人',
|
||||
|
||||
@@ -26,6 +26,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -53,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -72,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,11 +17,15 @@ const emit = defineEmits(['success']);
|
||||
const formData = ref<SystemNotifyTemplateApi.NotifyTemplate>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -59,7 +63,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
} catch (error) {
|
||||
console.error('发送站内信失败', error);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
|
||||
@@ -48,11 +48,10 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
// TODO @芋艿:图片上传
|
||||
{
|
||||
fieldName: 'logo',
|
||||
label: '应用图标',
|
||||
component: 'UploadImage',
|
||||
component: 'ImageUpload',
|
||||
componentProps: {
|
||||
limit: 1,
|
||||
},
|
||||
@@ -84,7 +83,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入访问令牌的有效期,单位:秒',
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
},
|
||||
rules: 'required',
|
||||
@@ -96,7 +94,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入刷新令牌的有效期,单位:秒',
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
},
|
||||
rules: 'required',
|
||||
@@ -109,7 +106,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE),
|
||||
mode: 'multiple',
|
||||
placeholder: '请输入授权类型',
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -120,7 +116,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入授权范围',
|
||||
mode: 'tags',
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,7 +125,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入自动授权范围',
|
||||
mode: 'multiple',
|
||||
class: 'w-full',
|
||||
// TODO @芋艿:根据权限,自动授权范围
|
||||
},
|
||||
},
|
||||
@@ -141,7 +135,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入可重定向的 URI 地址',
|
||||
mode: 'tags',
|
||||
class: 'w-full',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
@@ -152,7 +145,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
placeholder: '请输入权限',
|
||||
mode: 'tags',
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -162,7 +154,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
mode: 'tags',
|
||||
placeholder: '请输入资源',
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,12 +26,16 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
commonConfig: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
@@ -56,7 +60,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -75,7 +79,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
try {
|
||||
formData.value = data;
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -72,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -40,7 +40,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
min: 0,
|
||||
class: 'w-full',
|
||||
controlsPosition: 'right',
|
||||
placeholder: '请输入显示顺序',
|
||||
},
|
||||
@@ -97,7 +96,6 @@ export function useAssignDataPermissionFormSchema(): VbenFormSchema[] {
|
||||
fieldName: 'dataScope',
|
||||
label: '权限范围',
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE, 'number'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -64,7 +64,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -83,7 +83,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
await loadDeptTree();
|
||||
toggleExpandAll();
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -25,6 +25,13 @@ const isExpanded = ref(false); // 展开状态
|
||||
const expandedKeys = ref<number[]>([]); // 展开的节点
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useAssignMenuFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -52,7 +59,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -73,7 +80,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 加载菜单列表
|
||||
await loadMenuTree();
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,6 +22,13 @@ const getTitle = computed(() => {
|
||||
});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
showDefaultActions: false,
|
||||
@@ -46,7 +53,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
@@ -65,7 +72,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 设置到 values
|
||||
await formApi.setValues(formData.value);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
modalApi.unlock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,7 +37,6 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'),
|
||||
class: 'w-full',
|
||||
placeholder: '请选择短信渠道',
|
||||
},
|
||||
rules: 'required',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user