!372 fix(vben): 对齐 antd/next/ele 业务代码差异
Merge pull request !372 from 芋道源码/migration
This commit is contained in:
@@ -35,11 +35,6 @@ export namespace CrmReceivableApi {
|
||||
no: string;
|
||||
totalPrice: number;
|
||||
}
|
||||
|
||||
export interface ReceivablePageParam extends PageParam {
|
||||
contractId?: number;
|
||||
customerId?: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询回款列表 */
|
||||
@@ -51,9 +46,7 @@ export function getReceivablePage(params: PageParam) {
|
||||
}
|
||||
|
||||
/** 查询回款列表,基于指定客户 */
|
||||
export function getReceivablePageByCustomer(
|
||||
params: CrmReceivableApi.ReceivablePageParam,
|
||||
) {
|
||||
export function getReceivablePageByCustomer(params: PageParam) {
|
||||
return requestClient.get<PageResult<CrmReceivableApi.Receivable>>(
|
||||
'/crm/receivable/page-by-customer',
|
||||
{ params },
|
||||
|
||||
@@ -76,9 +76,9 @@ export function getSpuPage(params: PageParam) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 tabsCount */
|
||||
export function getTabsCount() {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count');
|
||||
/** 获得商品 SPU 列表 tabsCount(支持按 name/categoryId/createTime 筛选) */
|
||||
export function getTabsCount(params?: Record<string, any>) {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count', { params });
|
||||
}
|
||||
|
||||
/** 创建商品 SPU */
|
||||
|
||||
@@ -110,7 +110,10 @@ export function getSimpleUser(id: number | string) {
|
||||
|
||||
/** 按昵称模糊搜索用户 */
|
||||
export function getSimpleUserListByNickname(nickname: string) {
|
||||
return requestClient.get<SystemUserApi.UserSimple[]>('/system/user/list-by-nickname', {
|
||||
params: { nickname },
|
||||
});
|
||||
return requestClient.get<SystemUserApi.UserSimple[]>(
|
||||
'/system/user/list-by-nickname',
|
||||
{
|
||||
params: { nickname },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,21 +109,6 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
/**
|
||||
* 用于 bpm 移动端流程表单 web-view 的嵌入
|
||||
*/
|
||||
{
|
||||
component: () => import('#/views/bpm/form/mobile/index.vue'),
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
ignoreAccess: true,
|
||||
title: '移动端流程表单展示',
|
||||
},
|
||||
name: 'BpmMobileFormPreview',
|
||||
path: '/bpm/mobile/form-preview',
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
|
||||
@@ -40,7 +40,7 @@ const props = defineProps({
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const prefix = inject<string>('prefix', 'flowable'); // 增加默认值flowable
|
||||
const prefix = inject<string>('prefix', 'flowable');
|
||||
const elementListenersList = ref<any[]>([]); // 监听器列表
|
||||
const listenerForm = ref<any>({}); // 监听器详情表单
|
||||
const fieldsListOfListener = ref<any[]>([]);
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 移动端流程表单展示页面 - Ant Design Vue 版本
|
||||
* 使用 @form-create/ant-design-vue 渲染表单
|
||||
* 用于 UniApp 通过 iframe/webview 嵌入
|
||||
*
|
||||
* URL 参数说明:
|
||||
* - type: 环境类型(必填)'miniapp' 小程序(微信/支付宝/百度等) | 'h5' H5
|
||||
* - processInstanceId: 流程实例ID(查看已有流程时使用)
|
||||
* - taskId: 任务ID(可选)
|
||||
* - activityId: 活动节点ID(可选)
|
||||
* - token: 访问令牌(用于 API 认证)
|
||||
*/
|
||||
import { computed, nextTick, onMounted, ref, toRaw } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { BpmFieldPermissionType, BpmModelFormType } from '@vben/constants';
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { Button, Empty, Spin } from 'ant-design-vue';
|
||||
|
||||
import { getApprovalDetail } from '#/api/bpm/processInstance';
|
||||
import { setConfAndFields2 } from '#/components/form-create';
|
||||
|
||||
type EnvType = 'h5' | 'miniapp'; // 环境类型
|
||||
|
||||
// UniApp WebView 类型声明
|
||||
interface UniWebView {
|
||||
postMessage: (options: { data: any }, targetOrigin?: string) => void;
|
||||
getEnv: (callback: (res: any) => void) => void;
|
||||
navigateTo: (options: {
|
||||
fail?: () => void;
|
||||
success?: () => void;
|
||||
url: string;
|
||||
}) => void;
|
||||
navigateBack: (options?: { delta?: number }) => void;
|
||||
switchTab: (options: { url: string }) => void;
|
||||
reLaunch: (options: { url: string }) => void;
|
||||
redirectTo: (options: { url: string }) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
uni?: UniWebView;
|
||||
}
|
||||
}
|
||||
|
||||
defineOptions({ name: 'BpmMobileFormPreview' });
|
||||
|
||||
const route = useRoute();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const envType = ref<EnvType>('h5'); // 环境类型
|
||||
const loading = ref(true); // 页面加载状态
|
||||
const error = ref<null | string>(null);
|
||||
|
||||
const processInstance = ref<any>(null);
|
||||
const processDefinition = ref<any>(null);
|
||||
|
||||
const detailForm = ref<{
|
||||
option: any;
|
||||
rule: any[];
|
||||
value: Record<string, any>;
|
||||
}>({
|
||||
option: {},
|
||||
rule: [],
|
||||
value: {},
|
||||
}); // 流程实例的表单详情
|
||||
const fApi = ref<any>(null); // form-create API 引用
|
||||
const fieldPermissions = ref<Record<string, string>>({}); // 字段权限
|
||||
|
||||
// 是否有表单内容
|
||||
const hasFormContent = computed(() => {
|
||||
return detailForm.value.rule && detailForm.value.rule.length > 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化 Token
|
||||
* 从 URL 参数获取 token 并设置到 store
|
||||
*/
|
||||
function initToken() {
|
||||
const token = route.query.token as string;
|
||||
if (token) {
|
||||
accessStore.setAccessToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并初始化环境类型
|
||||
*/
|
||||
function initEnvType(): boolean {
|
||||
const type = route.query.type as string;
|
||||
|
||||
if (!type) {
|
||||
error.value = '缺少必填参数: type';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type !== 'h5' && type !== 'miniapp') {
|
||||
error.value = 'type 参数值无效,必须是 h5 或 miniapp';
|
||||
return false;
|
||||
}
|
||||
envType.value = type as EnvType;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批详情
|
||||
*/
|
||||
async function getDetail() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const processInstanceId = route.query.processInstanceId as string;
|
||||
const taskId = route.query.taskId as string;
|
||||
const activityId = route.query.activityId as string;
|
||||
|
||||
if (!processInstanceId) {
|
||||
throw new Error('缺少流程实例ID参数');
|
||||
}
|
||||
|
||||
const data = await getApprovalDetail({
|
||||
processInstanceId,
|
||||
taskId,
|
||||
activityId,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new Error('查询不到审批详情信息');
|
||||
}
|
||||
|
||||
if (!data.processDefinition || !data.processInstance) {
|
||||
throw new Error('查询不到流程信息');
|
||||
}
|
||||
|
||||
processInstance.value = data.processInstance;
|
||||
processDefinition.value = data.processDefinition;
|
||||
|
||||
// 设置普通表单信息
|
||||
if (data.processDefinition.formType === BpmModelFormType.NORMAL) {
|
||||
if (detailForm.value.rule?.length > 0) {
|
||||
// 避免刷新 form-create 显示不了
|
||||
detailForm.value.value = processInstance.value.formVariables;
|
||||
} else {
|
||||
setConfAndFields2(
|
||||
detailForm,
|
||||
processDefinition.value.formConf,
|
||||
processDefinition.value.formFields,
|
||||
processInstance.value.formVariables,
|
||||
);
|
||||
}
|
||||
await nextTick();
|
||||
fApi.value?.btn.show(false);
|
||||
fApi.value?.resetBtn.show(false);
|
||||
fApi.value?.disabled(true);
|
||||
// 设置表单字段权限
|
||||
if (data.formFieldsPermission) {
|
||||
Object.keys(data.formFieldsPermission).forEach((item) => {
|
||||
setFieldPermission(item, data.formFieldsPermission[item]);
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向父页面发送消息
|
||||
* 根据环境类型选择不同的通信方式
|
||||
*/
|
||||
function postMessageToParent(message: { data: any; type: string }) {
|
||||
const messageData = {
|
||||
source: 'bpm-mobile-form',
|
||||
type: message.type,
|
||||
data: message.data,
|
||||
};
|
||||
|
||||
// 小程序环境:使用 uni.postMessage
|
||||
if (envType.value === 'miniapp') {
|
||||
if (window.uni?.postMessage) {
|
||||
// 传递的消息信息,必须写在 data 对象中
|
||||
window.uni.postMessage({ data: message.data }, window.location.origin);
|
||||
} else {
|
||||
console.error('小程序环境下 uni 对象未定义');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// H5 环境:使用 window.postMessage
|
||||
if (envType.value === 'h5' && window.parent !== window) {
|
||||
window.parent.postMessage(messageData, '*');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地克隆对象,移除不可序列化的属性
|
||||
*/
|
||||
function safeClone(obj: any): any {
|
||||
try {
|
||||
// 先使用 toRaw 移除 Vue 的响应式代理
|
||||
const raw = toRaw(obj);
|
||||
// 使用 JSON 序列化来移除函数、DOM 元素等不可序列化的内容
|
||||
// eslint-disable-next-line unicorn/prefer-structured-clone
|
||||
return JSON.parse(JSON.stringify(raw));
|
||||
} catch (error) {
|
||||
console.error('克隆对象失败:', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置表单权限 */
|
||||
function setFieldPermission(field: string, permission: string) {
|
||||
fieldPermissions.value[field] = permission;
|
||||
if (permission === BpmFieldPermissionType.READ) {
|
||||
fApi.value?.disabled(true, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.WRITE) {
|
||||
fApi.value?.disabled(false, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.NONE) {
|
||||
fApi.value?.hidden(true, field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮点击事件
|
||||
* 获取表单数据并发送给父页面
|
||||
*/
|
||||
function handleConfirm() {
|
||||
// 获取最新的表单值(转换为普通对象,避免 Proxy 序列化问题)
|
||||
const rawValue = detailForm.value.value;
|
||||
const currentValue = safeClone(rawValue);
|
||||
|
||||
// 发送表单数据给父页面
|
||||
postMessageToParent({
|
||||
type: 'FORM_SUBMIT',
|
||||
data: {
|
||||
formValue: currentValue,
|
||||
fieldPermissions: safeClone(fieldPermissions.value),
|
||||
processInstanceId: route.query.processInstanceId,
|
||||
taskId: route.query.taskId,
|
||||
},
|
||||
});
|
||||
window.uni?.navigateBack();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 验证环境类型
|
||||
if (!initEnvType()) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 先加载微信 JSSDK(微信小程序需要)
|
||||
const wxScript = document.createElement('script');
|
||||
wxScript.type = 'text/javascript';
|
||||
wxScript.src = 'https://res.wx.qq.com/open/js/jweixin-1.4.0.js';
|
||||
|
||||
wxScript.addEventListener('load', () => {
|
||||
// 2. 微信 SDK 加载完成后,加载 UniApp WebView SDK
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
// 所有 SDK 加载完成后初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'UniApp WebView SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
wxScript.addEventListener('error', () => {
|
||||
// 微信 SDK 加载失败,尝试只加载 UniApp SDK(可能是其他小程序)
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
document.head.append(wxScript);
|
||||
|
||||
// 初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
function initApp() {
|
||||
// 设置主题为 light 模式
|
||||
updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 初始化 token
|
||||
initToken();
|
||||
|
||||
// 加载数据
|
||||
if (route.query.processInstanceId) {
|
||||
getDetail();
|
||||
} else {
|
||||
loading.value = false;
|
||||
error.value = '缺少必要参数:processInstanceId';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-form-preview-antd">
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<Spin size="large" tip="加载中..." />
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<Empty v-else-if="error" :description="error" />
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<template v-else>
|
||||
<!-- 有表单规则时渲染 form-create -->
|
||||
<div v-if="hasFormContent" class="mt-4">
|
||||
<form-create
|
||||
v-model="detailForm.value"
|
||||
v-model:api="fApi"
|
||||
:option="detailForm.option"
|
||||
:rule="detailForm.rule"
|
||||
/>
|
||||
|
||||
<!-- 确定按钮 -->
|
||||
<div class="form-footer">
|
||||
<Button type="primary" size="large" block @click="handleConfirm">
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无表单内容时显示空状态 -->
|
||||
<Empty v-else description="暂无表单内容" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 响应式适配 */
|
||||
@media (max-width: 768px) {
|
||||
.mobile-form-preview-antd {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-form-preview-antd {
|
||||
min-height: 100px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
box-shadow: 0 -2px 8px rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.form-footer :deep(.ant-btn) {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -33,6 +33,7 @@ watch(
|
||||
view.value = newModelView;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监听 bpmnXml */
|
||||
|
||||
@@ -56,6 +56,7 @@ watch(
|
||||
simpleModel.value = newModelView.simpleModel || {};
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监控模型结构数据 */
|
||||
|
||||
@@ -70,17 +70,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
const queryParams: CrmReceivableApi.ReceivablePageParam = {
|
||||
const queryParams = {
|
||||
...(props.customerId ? { customerId: props.customerId } : {}),
|
||||
...(props.customerId && props.contractId
|
||||
? { contractId: props.contractId }
|
||||
: {}),
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
};
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId;
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId;
|
||||
queryParams.contractId = props.contractId;
|
||||
}
|
||||
return await getReceivablePageByCustomer(queryParams);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
deleteDemo03StudentList,
|
||||
exportDemo03Student,
|
||||
getDemo03StudentPage,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IconifyIcon } from '@vben/icons';
|
||||
import { Button, Input } from 'ant-design-vue';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContentWrap } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number; // 学生编号(主表的关联字段)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { Form, Input } from 'ant-design-vue';
|
||||
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number; // 学生编号(主表的关联字段)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContentWrap } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number; // 学生编号(主表的关联字段)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { Rule } from 'ant-design-vue/es/form';
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
updateDemo03Student,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ async function handleExport() {
|
||||
}
|
||||
|
||||
/** 获得每个 Tab 的数量 */
|
||||
async function getTabCount() {
|
||||
const res = await getTabsCount();
|
||||
async function getTabCount(params?: Record<string, any>) {
|
||||
const res = await getTabsCount(params ?? (await gridApi.formApi.getValues()));
|
||||
for (const objName in res) {
|
||||
const index = Number(objName);
|
||||
if (tabsData.value[index]) {
|
||||
@@ -160,12 +160,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getSpuPage({
|
||||
const result = await getSpuPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
tabType: tabType.value,
|
||||
...formValues,
|
||||
});
|
||||
void getTabCount(formValues); // 跟随筛选刷新 tab 数量
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -35,11 +35,6 @@ export namespace CrmReceivableApi {
|
||||
no: string;
|
||||
totalPrice: number;
|
||||
}
|
||||
|
||||
export interface ReceivablePageParam extends PageParam {
|
||||
contractId?: number;
|
||||
customerId?: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询回款列表 */
|
||||
@@ -51,9 +46,7 @@ export function getReceivablePage(params: PageParam) {
|
||||
}
|
||||
|
||||
/** 查询回款列表,基于指定客户 */
|
||||
export function getReceivablePageByCustomer(
|
||||
params: CrmReceivableApi.ReceivablePageParam,
|
||||
) {
|
||||
export function getReceivablePageByCustomer(params: PageParam) {
|
||||
return requestClient.get<PageResult<CrmReceivableApi.Receivable>>(
|
||||
'/crm/receivable/page-by-customer',
|
||||
{ params },
|
||||
|
||||
@@ -76,9 +76,9 @@ export function getSpuPage(params: PageParam) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 tabsCount */
|
||||
export function getTabsCount() {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count');
|
||||
/** 获得商品 SPU 列表 tabsCount(支持按 name/categoryId/createTime 筛选) */
|
||||
export function getTabsCount(params?: Record<string, any>) {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count', { params });
|
||||
}
|
||||
|
||||
/** 创建商品 SPU */
|
||||
|
||||
@@ -16,6 +16,7 @@ export namespace SystemUserApi {
|
||||
sex: number;
|
||||
avatar: string;
|
||||
loginIp: string;
|
||||
loginDate?: Date;
|
||||
status: number;
|
||||
remark: string;
|
||||
createTime?: Date;
|
||||
@@ -40,6 +41,13 @@ export function getUser(id: number) {
|
||||
return requestClient.get<SystemUserApi.User>(`/system/user/get?id=${id}`);
|
||||
}
|
||||
|
||||
/** 查询用户列表 */
|
||||
export function getUserList(ids: number[]) {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/list', {
|
||||
params: { ids: ids.join(',') },
|
||||
});
|
||||
}
|
||||
|
||||
/** 新增用户 */
|
||||
export function createUser(data: SystemUserApi.User) {
|
||||
return requestClient.post('/system/user/create', data);
|
||||
@@ -88,13 +96,6 @@ export function updateUserStatus(id: number, status: number) {
|
||||
return requestClient.put('/system/user/update-status', { id, status });
|
||||
}
|
||||
|
||||
/** 查询用户列表 */
|
||||
export function getUserList(ids: number[]) {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/list', {
|
||||
params: { ids: ids.join(',') },
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取用户精简信息列表 */
|
||||
export function getSimpleUserList() {
|
||||
return requestClient.get<SystemUserApi.User[]>('/system/user/simple-list');
|
||||
@@ -111,6 +112,8 @@ export function getSimpleUser(id: number | string) {
|
||||
export function getSimpleUserListByNickname(nickname: string) {
|
||||
return requestClient.get<SystemUserApi.UserSimple[]>(
|
||||
'/system/user/list-by-nickname',
|
||||
{ params: { nickname } },
|
||||
{
|
||||
params: { nickname },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,21 +109,6 @@ const coreRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
/**
|
||||
* 用于 bpm 移动端流程表单 web-view 的嵌入
|
||||
*/
|
||||
{
|
||||
component: () => import('#/views/bpm/form/mobile/index.vue'),
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
ignoreAccess: true,
|
||||
title: '移动端流程表单展示',
|
||||
},
|
||||
name: 'BpmMobileFormPreview',
|
||||
path: '/bpm/mobile/form-preview',
|
||||
},
|
||||
];
|
||||
|
||||
export { coreRoutes, fallbackNotFoundRoute };
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 移动端流程表单展示页面 - Ant Design Vue 版本
|
||||
* 使用 @form-create/antdv-next 渲染表单
|
||||
* 用于 UniApp 通过 iframe/webview 嵌入
|
||||
*
|
||||
* URL 参数说明:
|
||||
* - type: 环境类型(必填)'miniapp' 小程序(微信/支付宝/百度等) | 'h5' H5
|
||||
* - processInstanceId: 流程实例ID(查看已有流程时使用)
|
||||
* - taskId: 任务ID(可选)
|
||||
* - activityId: 活动节点ID(可选)
|
||||
* - token: 访问令牌(用于 API 认证)
|
||||
*/
|
||||
import { computed, nextTick, onMounted, ref, toRaw } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { BpmFieldPermissionType, BpmModelFormType } from '@vben/constants';
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { Button, Empty, Spin } from 'antdv-next';
|
||||
|
||||
import { getApprovalDetail } from '#/api/bpm/processInstance';
|
||||
import { setConfAndFields2 } from '#/components/form-create';
|
||||
|
||||
type EnvType = 'h5' | 'miniapp'; // 环境类型
|
||||
|
||||
// UniApp WebView 类型声明
|
||||
interface UniWebView {
|
||||
postMessage: (options: { data: any }, targetOrigin?: string) => void;
|
||||
getEnv: (callback: (res: any) => void) => void;
|
||||
navigateTo: (options: {
|
||||
fail?: () => void;
|
||||
success?: () => void;
|
||||
url: string;
|
||||
}) => void;
|
||||
navigateBack: (options?: { delta?: number }) => void;
|
||||
switchTab: (options: { url: string }) => void;
|
||||
reLaunch: (options: { url: string }) => void;
|
||||
redirectTo: (options: { url: string }) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
uni?: UniWebView;
|
||||
}
|
||||
}
|
||||
|
||||
defineOptions({ name: 'BpmMobileFormPreview' });
|
||||
|
||||
const route = useRoute();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const envType = ref<EnvType>('h5'); // 环境类型
|
||||
const loading = ref(true); // 页面加载状态
|
||||
const error = ref<null | string>(null);
|
||||
|
||||
const processInstance = ref<any>(null);
|
||||
const processDefinition = ref<any>(null);
|
||||
|
||||
const detailForm = ref<{
|
||||
option: any;
|
||||
rule: any[];
|
||||
value: Record<string, any>;
|
||||
}>({
|
||||
option: {},
|
||||
rule: [],
|
||||
value: {},
|
||||
}); // 流程实例的表单详情
|
||||
const fApi = ref<any>(null); // form-create API 引用
|
||||
const fieldPermissions = ref<Record<string, string>>({}); // 字段权限
|
||||
|
||||
// 是否有表单内容
|
||||
const hasFormContent = computed(() => {
|
||||
return detailForm.value.rule && detailForm.value.rule.length > 0;
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化 Token
|
||||
* 从 URL 参数获取 token 并设置到 store
|
||||
*/
|
||||
function initToken() {
|
||||
const token = route.query.token as string;
|
||||
if (token) {
|
||||
accessStore.setAccessToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并初始化环境类型
|
||||
*/
|
||||
function initEnvType(): boolean {
|
||||
const type = route.query.type as string;
|
||||
|
||||
if (!type) {
|
||||
error.value = '缺少必填参数: type';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type !== 'h5' && type !== 'miniapp') {
|
||||
error.value = 'type 参数值无效,必须是 h5 或 miniapp';
|
||||
return false;
|
||||
}
|
||||
envType.value = type as EnvType;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批详情
|
||||
*/
|
||||
async function getDetail() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const processInstanceId = route.query.processInstanceId as string;
|
||||
const taskId = route.query.taskId as string;
|
||||
const activityId = route.query.activityId as string;
|
||||
|
||||
if (!processInstanceId) {
|
||||
throw new Error('缺少流程实例ID参数');
|
||||
}
|
||||
|
||||
const data = await getApprovalDetail({
|
||||
processInstanceId,
|
||||
taskId,
|
||||
activityId,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new Error('查询不到审批详情信息');
|
||||
}
|
||||
|
||||
if (!data.processDefinition || !data.processInstance) {
|
||||
throw new Error('查询不到流程信息');
|
||||
}
|
||||
|
||||
processInstance.value = data.processInstance;
|
||||
processDefinition.value = data.processDefinition;
|
||||
|
||||
// 设置普通表单信息
|
||||
if (data.processDefinition.formType === BpmModelFormType.NORMAL) {
|
||||
if (detailForm.value.rule?.length > 0) {
|
||||
// 避免刷新 form-create 显示不了
|
||||
detailForm.value.value = processInstance.value.formVariables;
|
||||
} else {
|
||||
setConfAndFields2(
|
||||
detailForm,
|
||||
processDefinition.value.formConf,
|
||||
processDefinition.value.formFields,
|
||||
processInstance.value.formVariables,
|
||||
);
|
||||
}
|
||||
await nextTick();
|
||||
fApi.value?.btn.show(false);
|
||||
fApi.value?.resetBtn.show(false);
|
||||
fApi.value?.disabled(true);
|
||||
// 设置表单字段权限
|
||||
if (data.formFieldsPermission) {
|
||||
Object.keys(data.formFieldsPermission).forEach((item) => {
|
||||
setFieldPermission(item, data.formFieldsPermission[item]);
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向父页面发送消息
|
||||
* 根据环境类型选择不同的通信方式
|
||||
*/
|
||||
function postMessageToParent(message: { data: any; type: string }) {
|
||||
const messageData = {
|
||||
source: 'bpm-mobile-form',
|
||||
type: message.type,
|
||||
data: message.data,
|
||||
};
|
||||
|
||||
// 小程序环境:使用 uni.postMessage
|
||||
if (envType.value === 'miniapp') {
|
||||
if (window.uni?.postMessage) {
|
||||
// 传递的消息信息,必须写在 data 对象中
|
||||
window.uni.postMessage({ data: message.data }, window.location.origin);
|
||||
} else {
|
||||
console.error('小程序环境下 uni 对象未定义');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// H5 环境:使用 window.postMessage
|
||||
if (envType.value === 'h5' && window.parent !== window) {
|
||||
window.parent.postMessage(messageData, '*');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地克隆对象,移除不可序列化的属性
|
||||
*/
|
||||
function safeClone(obj: any): any {
|
||||
try {
|
||||
// 先使用 toRaw 移除 Vue 的响应式代理
|
||||
const raw = toRaw(obj);
|
||||
// 使用 JSON 序列化来移除函数、DOM 元素等不可序列化的内容
|
||||
// eslint-disable-next-line unicorn/prefer-structured-clone
|
||||
return JSON.parse(JSON.stringify(raw));
|
||||
} catch (error) {
|
||||
console.error('克隆对象失败:', error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置表单权限 */
|
||||
function setFieldPermission(field: string, permission: string) {
|
||||
fieldPermissions.value[field] = permission;
|
||||
if (permission === BpmFieldPermissionType.READ) {
|
||||
fApi.value?.disabled(true, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.WRITE) {
|
||||
fApi.value?.disabled(false, field);
|
||||
}
|
||||
if (permission === BpmFieldPermissionType.NONE) {
|
||||
fApi.value?.hidden(true, field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮点击事件
|
||||
* 获取表单数据并发送给父页面
|
||||
*/
|
||||
function handleConfirm() {
|
||||
// 获取最新的表单值(转换为普通对象,避免 Proxy 序列化问题)
|
||||
const rawValue = detailForm.value.value;
|
||||
const currentValue = safeClone(rawValue);
|
||||
|
||||
// 发送表单数据给父页面
|
||||
postMessageToParent({
|
||||
type: 'FORM_SUBMIT',
|
||||
data: {
|
||||
formValue: currentValue,
|
||||
fieldPermissions: safeClone(fieldPermissions.value),
|
||||
processInstanceId: route.query.processInstanceId,
|
||||
taskId: route.query.taskId,
|
||||
},
|
||||
});
|
||||
window.uni?.navigateBack();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 验证环境类型
|
||||
if (!initEnvType()) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 先加载微信 JSSDK(微信小程序需要)
|
||||
const wxScript = document.createElement('script');
|
||||
wxScript.type = 'text/javascript';
|
||||
wxScript.src = 'https://res.wx.qq.com/open/js/jweixin-1.4.0.js';
|
||||
|
||||
wxScript.addEventListener('load', () => {
|
||||
// 2. 微信 SDK 加载完成后,加载 UniApp WebView SDK
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
// 所有 SDK 加载完成后初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'UniApp WebView SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
wxScript.addEventListener('error', () => {
|
||||
// 微信 SDK 加载失败,尝试只加载 UniApp SDK(可能是其他小程序)
|
||||
const uniScript = document.createElement('script');
|
||||
uniScript.type = 'text/javascript';
|
||||
uniScript.src = 'https://unpkg.com/@dcloudio/uni-webview-js@0.0.3/index.js';
|
||||
|
||||
uniScript.addEventListener('load', () => {
|
||||
initApp();
|
||||
});
|
||||
|
||||
uniScript.addEventListener('error', () => {
|
||||
error.value = 'SDK 加载失败';
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
document.head.append(uniScript);
|
||||
});
|
||||
|
||||
document.head.append(wxScript);
|
||||
|
||||
// 初始化
|
||||
initApp();
|
||||
});
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
function initApp() {
|
||||
// 设置主题为 light 模式
|
||||
updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 初始化 token
|
||||
initToken();
|
||||
|
||||
// 加载数据
|
||||
if (route.query.processInstanceId) {
|
||||
getDetail();
|
||||
} else {
|
||||
loading.value = false;
|
||||
error.value = '缺少必要参数:processInstanceId';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mobile-form-preview-antd">
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading-container">
|
||||
<Spin size="large" description="加载中..." />
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<Empty v-else-if="error" :description="error" />
|
||||
|
||||
<!-- 表单内容 -->
|
||||
<template v-else>
|
||||
<!-- 有表单规则时渲染 form-create -->
|
||||
<div v-if="hasFormContent" class="mt-4">
|
||||
<form-create
|
||||
v-model="detailForm.value"
|
||||
v-model:api="fApi"
|
||||
:option="detailForm.option"
|
||||
:rule="detailForm.rule"
|
||||
/>
|
||||
|
||||
<!-- 确定按钮 -->
|
||||
<div class="form-footer">
|
||||
<Button type="primary" size="large" block @click="handleConfirm">
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无表单内容时显示空状态 -->
|
||||
<Empty v-else description="暂无表单内容" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 响应式适配 */
|
||||
@media (max-width: 768px) {
|
||||
.mobile-form-preview-antd {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-form-preview-antd {
|
||||
min-height: 100px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
padding: 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
box-shadow: 0 -2px 8px rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.form-footer :deep(.ant-btn) {
|
||||
height: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -33,6 +33,7 @@ watch(
|
||||
view.value = newModelView;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监听 bpmnXml */
|
||||
|
||||
@@ -56,6 +56,7 @@ watch(
|
||||
simpleModel.value = newModelView.simpleModel || {};
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/** 监控模型结构数据 */
|
||||
|
||||
@@ -70,17 +70,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
const queryParams: CrmReceivableApi.ReceivablePageParam = {
|
||||
const queryParams = {
|
||||
...(props.customerId ? { customerId: props.customerId } : {}),
|
||||
...(props.customerId && props.contractId
|
||||
? { contractId: props.contractId }
|
||||
: {}),
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
};
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId;
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId;
|
||||
queryParams.contractId = props.contractId;
|
||||
}
|
||||
return await getReceivablePageByCustomer(queryParams);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
deleteDemo03StudentList,
|
||||
exportDemo03Student,
|
||||
getDemo03StudentPage,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
import { $t } from '#/locales';
|
||||
import { getRangePickerDefaultProps } from '#/utils';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IconifyIcon } from '@vben/icons';
|
||||
import { Button, Input } from 'antdv-next';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContentWrap } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03CourseListByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number; // 学生编号(主表的关联字段)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { Form, FormItem, Input } from 'antdv-next';
|
||||
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
type Rule = any;
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContentWrap } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/normal';
|
||||
import { getDemo03GradeByStudentId } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
const props = defineProps<{
|
||||
studentId?: number; // 学生编号(主表的关联字段)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
createDemo03Student,
|
||||
getDemo03Student,
|
||||
updateDemo03Student,
|
||||
} from '#/api/infra/demo/demo03/normal';
|
||||
} from '#/api/infra/demo/demo03/inner';
|
||||
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
|
||||
@@ -73,8 +73,8 @@ async function handleExport() {
|
||||
}
|
||||
|
||||
/** 获得每个 Tab 的数量 */
|
||||
async function getTabCount() {
|
||||
const res = await getTabsCount();
|
||||
async function getTabCount(params?: Record<string, any>) {
|
||||
const res = await getTabsCount(params ?? (await gridApi.formApi.getValues()));
|
||||
for (const objName in res) {
|
||||
const index = Number(objName);
|
||||
if (tabsData.value[index]) {
|
||||
@@ -166,12 +166,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getSpuPage({
|
||||
const result = await getSpuPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
tabType: tabType.value,
|
||||
...formValues,
|
||||
});
|
||||
void getTabCount(formValues); // 跟随筛选刷新 tab 数量
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,14 +3,6 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace CrmReceivableApi {
|
||||
/** 合同信息 */
|
||||
export interface Contract {
|
||||
id?: number;
|
||||
name?: string;
|
||||
no: string;
|
||||
totalPrice: number;
|
||||
}
|
||||
|
||||
/** 回款信息 */
|
||||
export interface Receivable {
|
||||
id: number;
|
||||
@@ -35,6 +27,14 @@ export namespace CrmReceivableApi {
|
||||
createTime: Date; // 创建时间
|
||||
updateTime: Date; // 更新时间
|
||||
}
|
||||
|
||||
/** 合同信息 */
|
||||
export interface Contract {
|
||||
id?: number;
|
||||
name?: string;
|
||||
no: string;
|
||||
totalPrice: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询回款列表 */
|
||||
|
||||
@@ -3,18 +3,6 @@ import type { PageParam, PageResult } from '@vben/request';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace ErpFinancePaymentApi {
|
||||
/** 付款单项 */
|
||||
export interface FinancePaymentItem {
|
||||
id?: number;
|
||||
row_id?: number; // 前端使用的临时 ID
|
||||
bizId: number; // 业务ID
|
||||
bizType: number; // 业务类型
|
||||
bizNo: string; // 业务编号
|
||||
totalPrice: number; // 应付金额
|
||||
paidPrice: number; // 已付金额
|
||||
paymentPrice: number; // 本次付款
|
||||
remark?: string; // 备注
|
||||
}
|
||||
/** 付款单信息 */
|
||||
export interface FinancePayment {
|
||||
id?: number; // 付款单编号
|
||||
@@ -37,6 +25,19 @@ export namespace ErpFinancePaymentApi {
|
||||
items?: FinancePaymentItem[]; // 付款明细
|
||||
bizNo?: string; // 业务单号
|
||||
}
|
||||
|
||||
/** 付款单项 */
|
||||
export interface FinancePaymentItem {
|
||||
id?: number;
|
||||
row_id?: number; // 前端使用的临时 ID
|
||||
bizId: number; // 业务ID
|
||||
bizType: number; // 业务类型
|
||||
bizNo: string; // 业务编号
|
||||
totalPrice: number; // 应付金额
|
||||
paidPrice: number; // 已付金额
|
||||
paymentPrice: number; // 本次付款
|
||||
remark?: string; // 备注
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询付款单分页 */
|
||||
|
||||
@@ -76,9 +76,9 @@ export function getSpuPage(params: PageParam) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 获得商品 SPU 列表 tabsCount */
|
||||
export function getTabsCount() {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count');
|
||||
/** 获得商品 SPU 列表 tabsCount(支持按 name/categoryId/createTime 筛选) */
|
||||
export function getTabsCount(params?: Record<string, any>) {
|
||||
return requestClient.get<Record<string, number>>('/product/spu/get-count', { params });
|
||||
}
|
||||
|
||||
/** 创建商品 SPU */
|
||||
|
||||
@@ -24,8 +24,8 @@ export namespace MesDvMachineryApi {
|
||||
/** 设备导入结果 */
|
||||
export interface MachineryImportRespVO {
|
||||
createCodes?: string[]; // 新增成功的设备编码
|
||||
failureCodes?: Record<string, string>; // 导入失败的设备编码及原因
|
||||
updateCodes?: string[]; // 更新成功的设备编码
|
||||
failureCodes?: Record<string, string>; // 导入失败的设备编码及原因
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,51 +83,6 @@ const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('#/views/crm/product/detail/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'statistics/customer',
|
||||
name: 'CrmStatisticsCustomer',
|
||||
meta: {
|
||||
title: '客户统计',
|
||||
activePath: '/crm/statistics/customer',
|
||||
},
|
||||
component: () => import('#/views/crm/statistics/customer/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'statistics/funnel',
|
||||
name: 'CrmStatisticsFunnel',
|
||||
meta: {
|
||||
title: '销售漏斗',
|
||||
activePath: '/crm/statistics/funnel',
|
||||
},
|
||||
component: () => import('#/views/crm/statistics/funnel/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'statistics/performance',
|
||||
name: 'CrmStatisticsPerformance',
|
||||
meta: {
|
||||
title: '员工业绩',
|
||||
activePath: '/crm/statistics/performance',
|
||||
},
|
||||
component: () => import('#/views/crm/statistics/performance/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'statistics/portrait',
|
||||
name: 'CrmStatisticsPortrait',
|
||||
meta: {
|
||||
title: '客户画像',
|
||||
activePath: '/crm/statistics/portrait',
|
||||
},
|
||||
component: () => import('#/views/crm/statistics/portrait/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'statistics/rank',
|
||||
name: 'CrmStatisticsRank',
|
||||
meta: {
|
||||
title: '排行榜',
|
||||
activePath: '/crm/statistics/rank',
|
||||
},
|
||||
component: () => import('#/views/crm/statistics/rank/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -40,7 +40,7 @@ const props = defineProps({
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const prefix = inject('prefix');
|
||||
const prefix = inject<string>('prefix', 'flowable');
|
||||
const elementListenersList = ref<any[]>([]); // 监听器列表
|
||||
const listenerForm = ref<any>({}); // 监听器详情表单
|
||||
const fieldsListOfListener = ref<any[]>([]);
|
||||
@@ -328,7 +328,7 @@ watch(
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="-mx-2 mb-2">
|
||||
<div class="-mx-2">
|
||||
<ListenerGrid :data="elementListenersList">
|
||||
<template #action="{ row, rowIndex }">
|
||||
<ElButton
|
||||
|
||||
@@ -38,7 +38,7 @@ interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const prefix = inject<string>('prefix');
|
||||
const prefix = inject<string>('prefix', 'flowable');
|
||||
|
||||
const elementListenersList = ref<any[]>([]);
|
||||
const listenerEventTypeObject = ref(eventType);
|
||||
@@ -324,7 +324,7 @@ watch(
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div class="-mx-2 mb-2">
|
||||
<div class="-mx-2">
|
||||
<ListenerGrid>
|
||||
<template #action="{ row, rowIndex }">
|
||||
<ElButton
|
||||
|
||||
@@ -1,7 +1,35 @@
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
interface ListenerFieldOptions {
|
||||
expression?: string;
|
||||
fieldType: string;
|
||||
name: string;
|
||||
string?: string;
|
||||
}
|
||||
|
||||
interface ListenerOptions {
|
||||
class?: string;
|
||||
delegateExpression?: string;
|
||||
event?: string;
|
||||
eventDefinitionType?: string;
|
||||
eventTimeDefinitions?: string;
|
||||
expression?: string;
|
||||
fields?: ListenerFieldOptions[];
|
||||
id?: string;
|
||||
listenerType?: string;
|
||||
resource?: string;
|
||||
scriptFormat?: string;
|
||||
scriptType?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
// 创建监听器实例
|
||||
export function createListenerObject(options: any, isTask: any, prefix: any) {
|
||||
const listenerObj = Object.create(null);
|
||||
export function createListenerObject(
|
||||
options: ListenerOptions,
|
||||
isTask: boolean,
|
||||
prefix: string,
|
||||
) {
|
||||
const listenerObj: Record<string, any> = Object.create(null);
|
||||
listenerObj.event = options.event;
|
||||
isTask && (listenerObj.id = options.id); // 任务监听器特有的 id 字段
|
||||
switch (options.listenerType) {
|
||||
@@ -23,7 +51,7 @@ export function createListenerObject(options: any, isTask: any, prefix: any) {
|
||||
}
|
||||
// 注入字段
|
||||
if (options.fields) {
|
||||
listenerObj.fields = options.fields.map((field: any) => {
|
||||
listenerObj.fields = options.fields.map((field) => {
|
||||
return createFieldObject(field, prefix);
|
||||
});
|
||||
}
|
||||
@@ -39,7 +67,7 @@ export function createListenerObject(options: any, isTask: any, prefix: any) {
|
||||
'bpmn:TimerEventDefinition',
|
||||
{
|
||||
id: `TimerEventDefinition_${uuid(8)}`,
|
||||
[`time${options.eventDefinitionType.replace(/^\S/, (s: any) => s.toUpperCase())}`]:
|
||||
[`time${options.eventDefinitionType.replace(/^\S/, (s) => s.toUpperCase())}`]:
|
||||
timeDefinition,
|
||||
},
|
||||
);
|
||||
@@ -52,7 +80,10 @@ export function createListenerObject(options: any, isTask: any, prefix: any) {
|
||||
}
|
||||
|
||||
// 创建 监听器的注入字段 实例
|
||||
export function createFieldObject(option: any, prefix: any) {
|
||||
export function createFieldObject(
|
||||
option: ListenerFieldOptions,
|
||||
prefix: string,
|
||||
) {
|
||||
const { name, fieldType, string, expression } = option;
|
||||
const fieldConfig =
|
||||
fieldType === 'string' ? { name, string } : { name, expression };
|
||||
@@ -60,7 +91,7 @@ export function createFieldObject(option: any, prefix: any) {
|
||||
}
|
||||
|
||||
// 创建脚本实例
|
||||
export function createScriptObject(options: any, prefix: any) {
|
||||
export function createScriptObject(options: ListenerOptions, prefix: string) {
|
||||
const { scriptType, scriptFormat, value, resource } = options;
|
||||
const scriptConfig =
|
||||
scriptType === 'inlineScript'
|
||||
@@ -70,7 +101,7 @@ export function createScriptObject(options: any, prefix: any) {
|
||||
}
|
||||
|
||||
// 更新元素扩展属性
|
||||
export function updateElementExtensions(element: any, extensionList: any) {
|
||||
export function updateElementExtensions(element: any, extensionList: any[]) {
|
||||
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||
values: extensionList,
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { UserTaskFormType } from '../../helpers';
|
||||
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
import {
|
||||
BpmModelFormType,
|
||||
BpmNodeTypeEnum,
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
ElDivider,
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElInputNumber,
|
||||
ElOption,
|
||||
ElRadio,
|
||||
ElRadioButton,
|
||||
@@ -35,6 +37,8 @@ import {
|
||||
ElTreeSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import { ProcessExpressionSelectModal } from '#/views/bpm/processExpression/components';
|
||||
|
||||
import {
|
||||
APPROVE_METHODS,
|
||||
APPROVE_TYPE,
|
||||
@@ -115,7 +119,11 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
||||
},
|
||||
});
|
||||
|
||||
// TODO @jason:和 antd 对应的文件,逻辑有点不一样;
|
||||
const [ExpressionSelectModal, expressionSelectModalApi] = useVbenModal({
|
||||
connectedComponent: ProcessExpressionSelectModal,
|
||||
destroyOnClose: true,
|
||||
showConfirmButton: false,
|
||||
});
|
||||
|
||||
// 节点名称配置
|
||||
// @ts-expect-error unused
|
||||
@@ -224,9 +232,18 @@ function changeCandidateStrategy() {
|
||||
configForm.value.deptLevel = 1;
|
||||
configForm.value.formUser = '';
|
||||
configForm.value.formDept = '';
|
||||
configForm.value.expression = '';
|
||||
configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE;
|
||||
}
|
||||
|
||||
function openExpressionSelect() {
|
||||
expressionSelectModalApi.open();
|
||||
}
|
||||
|
||||
function handleExpressionSelected(row: any) {
|
||||
configForm.value.expression = row?.expression ?? '';
|
||||
}
|
||||
|
||||
/** 审批方式改变 */
|
||||
function approveMethodChanged() {
|
||||
configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS;
|
||||
@@ -813,7 +830,6 @@ onMounted(() => {
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<!-- TODO @jason:后续要支持选择已经存好的表达式 -->
|
||||
<ElFormItem
|
||||
v-if="
|
||||
configForm.candidateStrategy === CandidateStrategy.EXPRESSION
|
||||
@@ -821,11 +837,21 @@ onMounted(() => {
|
||||
label="流程表达式"
|
||||
name="expression"
|
||||
>
|
||||
<ElInput
|
||||
v-model="configForm.expression"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<ElInput
|
||||
v-model="configForm.expression"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
/>
|
||||
<div class="flex flex-col gap-2">
|
||||
<ElButton type="primary" @click="openExpressionSelect">
|
||||
选择
|
||||
</ElButton>
|
||||
<ElButton @click="configForm.expression = ''">
|
||||
清空
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<!-- 多人审批/办理 方式 -->
|
||||
<ElFormItem :label="`多人${nodeTypeName}方式`" name="approveMethod">
|
||||
@@ -1182,4 +1208,6 @@ onMounted(() => {
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
</Drawer>
|
||||
|
||||
<ExpressionSelectModal @select="handleExpressionSelected" />
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeGridPropTypes } from '#/adapter/vxe-table';
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type {
|
||||
VxeGridPropTypes,
|
||||
VxeTableGridOptions,
|
||||
} from '#/adapter/vxe-table';
|
||||
import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getProcessExpressionPage } from '#/api/bpm/processExpression';
|
||||
@@ -16,35 +19,23 @@ const emit = defineEmits<{
|
||||
select: [expression: BpmProcessExpressionApi.ProcessExpression];
|
||||
}>();
|
||||
|
||||
// 查询参数
|
||||
// TODO @jason:这里的风格,和 antd 对应的不一致;
|
||||
const queryParams = ref({
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
});
|
||||
|
||||
// 配置 VxeGrid
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: [
|
||||
{ field: 'name', title: '名字', minWidth: 160 },
|
||||
{ field: 'expression', title: '表达式', minWidth: 260 },
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作',
|
||||
width: 120,
|
||||
slots: { default: 'action' },
|
||||
},
|
||||
],
|
||||
columns: useGridColumns(),
|
||||
showOverflow: true,
|
||||
minHeight: 300,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 查询表达式列表
|
||||
query: async ({ page }) => {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getProcessExpressionPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
status: queryParams.value.status,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -56,7 +47,7 @@ const [Grid] = useVbenVxeGrid({
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
} as VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpression>,
|
||||
});
|
||||
|
||||
// 配置 Modal
|
||||
@@ -70,6 +61,53 @@ function handleSelect(row: BpmProcessExpressionApi.ProcessExpression) {
|
||||
emit('select', row);
|
||||
modalApi.close();
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名字',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入名字',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
defaultValue: CommonStatusEnum.ENABLE,
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function useGridColumns(): VxeTableGridOptions<BpmProcessExpressionApi.ProcessExpression>['columns'] {
|
||||
return [
|
||||
{ field: 'name', title: '名字', minWidth: 160 },
|
||||
{ field: 'expression', title: '表达式', minWidth: 260 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 100,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作',
|
||||
width: 120,
|
||||
slots: { default: 'action' },
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { CommonStatusEnum, DICT_TYPE } from '@vben/constants';
|
||||
import { getDictOptions } from '@vben/hooks';
|
||||
|
||||
/** 选择监听器弹窗的列表字段 */
|
||||
// TODO @jason:和 antd 对应的,不太一致;
|
||||
export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{ field: 'name', title: '名字', minWidth: 120 },
|
||||
{ field: 'name', title: '名字', minWidth: 160 },
|
||||
{
|
||||
field: 'type',
|
||||
title: '类型',
|
||||
minWidth: 200,
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.BPM_PROCESS_LISTENER_TYPE },
|
||||
},
|
||||
},
|
||||
{ field: 'event', title: '事件', minWidth: 200 },
|
||||
{
|
||||
field: 'status',
|
||||
title: '状态',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.COMMON_STATUS },
|
||||
},
|
||||
},
|
||||
{ field: 'event', title: '事件', minWidth: 120 },
|
||||
{
|
||||
field: 'valueType',
|
||||
title: '值类型',
|
||||
@@ -35,3 +45,29 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/** 列表的搜索表单 */
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
fieldName: 'name',
|
||||
label: '名字',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入名字',
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
defaultValue: CommonStatusEnum.ENABLE,
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
placeholder: '请选择状态',
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ import type { BpmProcessListenerApi } from '#/api/bpm/processListener';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
|
||||
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getProcessListenerPage } from '#/api/bpm/processListener';
|
||||
|
||||
import { useGridColumns } from './data';
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
|
||||
defineOptions({ name: 'ProcessListenerSelectModal' });
|
||||
|
||||
@@ -21,27 +20,25 @@ const emit = defineEmits<{
|
||||
select: [listener: BpmProcessListenerApi.ProcessListener];
|
||||
}>();
|
||||
|
||||
// 查询参数
|
||||
// TODO @jason:这里的风格,和 antd 对应的不一致;
|
||||
const queryParams = ref({
|
||||
type: '',
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
});
|
||||
const listenerType = ref('');
|
||||
|
||||
// 配置 VxeGrid
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
showOverflow: true,
|
||||
minHeight: 300,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getProcessListenerPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
type: queryParams.value.type,
|
||||
status: queryParams.value.status,
|
||||
type: listenerType.value,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
@@ -61,12 +58,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
showConfirmButton: false,
|
||||
onOpenChange: async (isOpen: boolean) => {
|
||||
if (!isOpen) {
|
||||
queryParams.value.type = '';
|
||||
listenerType.value = '';
|
||||
return;
|
||||
}
|
||||
const data = modalApi.getData<{ type: string }>();
|
||||
if (data?.type) {
|
||||
queryParams.value.type = data.type;
|
||||
listenerType.value = data.type;
|
||||
}
|
||||
},
|
||||
destroyOnClose: true,
|
||||
|
||||
@@ -69,17 +69,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
const queryParams: any = {
|
||||
const queryParams = {
|
||||
...(props.customerId ? { customerId: props.customerId } : {}),
|
||||
...(props.customerId && props.contractId
|
||||
? { contractId: props.contractId }
|
||||
: {}),
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
};
|
||||
if (props.customerId && !props.contractId) {
|
||||
queryParams.customerId = props.customerId;
|
||||
} else if (props.customerId && props.contractId) {
|
||||
// 如果是合同的话客户编号也需要带上因为权限基于客户
|
||||
queryParams.customerId = props.customerId;
|
||||
queryParams.contractId = props.contractId;
|
||||
}
|
||||
return await getReceivablePageByCustomer(queryParams);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择付款时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -40,6 +40,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择收款时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -43,7 +43,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择入库时间',
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择订单时间',
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -43,7 +43,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择退货时间',
|
||||
showTime: true,
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -41,6 +41,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择订单时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -43,6 +43,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择出库时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -43,6 +43,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
componentProps: {
|
||||
disabled: formType === 'detail',
|
||||
placeholder: '选择退货时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -38,6 +38,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择盘点时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -39,6 +39,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择入库时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -38,6 +38,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择调度时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -39,6 +39,7 @@ export function useFormSchema(formType: FormType): VbenFormSchema[] {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
placeholder: '选择出库时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
class: '!w-full',
|
||||
|
||||
@@ -67,8 +67,8 @@ async function handleExport() {
|
||||
}
|
||||
|
||||
/** 获得每个 Tab 的数量 */
|
||||
async function getTabCount() {
|
||||
const res = await getTabsCount();
|
||||
async function getTabCount(params?: Record<string, any>) {
|
||||
const res = await getTabsCount(params ?? (await gridApi.formApi.getValues()));
|
||||
for (const objName in res) {
|
||||
const index = Number(objName);
|
||||
if (tabsData.value[index]) {
|
||||
@@ -158,12 +158,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getSpuPage({
|
||||
const result = await getSpuPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
tabType: Number(tabType.value),
|
||||
...formValues,
|
||||
});
|
||||
void getTabCount(formValues); // 跟随筛选刷新 tab 数量
|
||||
return result;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,6 +26,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
if (!props.feedbackId) {
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
return await getItemConsumeLinePage({
|
||||
feedbackId: props.feedbackId,
|
||||
pageNo: page.currentPage,
|
||||
@@ -39,7 +42,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
refresh: false,
|
||||
search: false,
|
||||
},
|
||||
} as VxeTableGridOptions<MesWmItemConsumeLineApi.ItemConsumeLine>,
|
||||
});
|
||||
|
||||
@@ -37,6 +37,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }) => {
|
||||
if (!props.feedbackId) {
|
||||
return { list: [], total: 0 };
|
||||
}
|
||||
return await getProductProduceLinePage({
|
||||
feedbackId: props.feedbackId,
|
||||
pageNo: page.currentPage,
|
||||
@@ -50,7 +53,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
refresh: false,
|
||||
search: false,
|
||||
},
|
||||
} as VxeTableGridOptions<MesWmProductProduceLineApi.ProductProduceLine>,
|
||||
});
|
||||
|
||||
27
apps/web-ele/src/views/report/goview/index.vue
Normal file
27
apps/web-ele/src/views/report/goview/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, IFrame, Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
defineOptions({ name: 'GoView' });
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const src = ref(
|
||||
`${import.meta.env.VITE_GOVIEW_URL}?accessToken=${accessStore.accessToken}&refreshToken=${accessStore.refreshToken}`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert
|
||||
title="大屏设计器"
|
||||
url="https://doc.iocoder.cn/report/screen/"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<IFrame :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
25
apps/web-ele/src/views/report/jmreport/bi.vue
Normal file
25
apps/web-ele/src/views/report/jmreport/bi.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, IFrame, Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
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>
|
||||
25
apps/web-ele/src/views/report/jmreport/index.vue
Normal file
25
apps/web-ele/src/views/report/jmreport/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { DocAlert, IFrame, Page } from '@vben/common-ui';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
defineOptions({ name: 'JimuReport' });
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const src = ref(
|
||||
`${import.meta.env.VITE_BASE_URL}/jmreport/list?token=${
|
||||
accessStore.refreshToken
|
||||
}`,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<template #doc>
|
||||
<DocAlert title="报表设计器" url="https://doc.iocoder.cn/report/" />
|
||||
</template>
|
||||
|
||||
<IFrame :src="src" />
|
||||
</Page>
|
||||
</template>
|
||||
@@ -1 +1,2 @@
|
||||
export { default as WmsInventorySelect } from './inventory-select.vue';
|
||||
export { default as WmsInventorySelect } from './select.vue';
|
||||
export type { InventorySelectRow } from './select.vue';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as WmsItemBrandSelect } from './item-brand-select.vue';
|
||||
export { default as WmsItemBrandSelect } from './select.vue';
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as WmsItemCategorySelect } from './item-category-select.vue';
|
||||
export { default as WmsItemCategoryTree } from './item-category-tree.vue';
|
||||
export { default as WmsItemCategorySelect } from './select.vue';
|
||||
export { default as WmsItemCategoryTree } from './tree.vue';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as WmsItemSkuSelect } from './item-sku-select.vue';
|
||||
export { default as WmsItemSkuSelect } from './select.vue';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as WmsMerchantSelect } from './merchant-select.vue';
|
||||
export { default as WmsMerchantSelect } from './select.vue';
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default as WmsWarehouseSelect } from './warehouse-select.vue';
|
||||
export { default as WmsWarehouseSelect } from './select.vue';
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { WmsWarehouseApi } from '#/api/wms/md/warehouse';
|
||||
import type { WmsMovementOrderApi } from '#/api/wms/order/movement';
|
||||
import type { WmsMovementOrderDetailApi } from '#/api/wms/order/movement/detail';
|
||||
import type { InventorySelectRow } from '#/views/wms/inventory/components/inventory-select.vue';
|
||||
import type { InventorySelectRow } from '#/views/wms/inventory/components';
|
||||
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { VxeTableInstance } from '#/adapter/vxe-table';
|
||||
import type { WmsWarehouseApi } from '#/api/wms/md/warehouse';
|
||||
import type { WmsShipmentOrderApi } from '#/api/wms/order/shipment';
|
||||
import type { WmsShipmentOrderDetailApi } from '#/api/wms/order/shipment/detail';
|
||||
import type { InventorySelectRow } from '#/views/wms/inventory/components/inventory-select.vue';
|
||||
import type { InventorySelectRow } from '#/views/wms/inventory/components';
|
||||
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user