fix(vben): 修复 antdv-next 与 ele 端差异问题

- 修复 pay 收银台二维码展示:ele 使用 useQRCode 渲染协议串二维码,并补齐两端 QR 弹窗打开逻辑
- 修复 ele 端 mp 消息语音、视频播放器组件对调问题
- 修复 ele 端 ERP 库存单据商品、仓库下拉选项不渲染问题
- 修复 ele 端系统菜单组件路径搜索过滤逻辑
- 修复 ele 端 IM 表情包抽屉打开后不加载表情列表问题
- 修复 ele 端文件配置 loading 关闭路径,避免成功或异常后持续转圈
- 修复 ele 端客户公海、合同配置等 CRM 表单文案不可见问题,并保持两端数字校验一致
- 修复 BPM、商城、字典等页面的组件适配和条件判断差异
- 启用 web-antdv-next typecheck 脚本,保持与 web-ele 一致
This commit is contained in:
YunaiV
2026-06-26 17:30:58 -07:00
parent a55f0facb0
commit eb9b1c9364
43 changed files with 303 additions and 165 deletions

View File

@@ -20,7 +20,7 @@
"build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development",
"preview": "vite preview",
"#typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vue-tsc --noEmit --skipLibCheck --incremental --tsBuildInfoFile node_modules/.cache/vue-tsc/tsconfig.tsbuildinfo"
"typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vue-tsc --noEmit --skipLibCheck --incremental --tsBuildInfoFile node_modules/.cache/vue-tsc/tsconfig.tsbuildinfo"
},
"imports": {
"#/*": "./src/*"

View File

@@ -258,14 +258,14 @@ function updateFormFieldKey(
) {
if (!formSetting?.updateFormFields || !newKey) return;
const value = formSetting.updateFormFields[oldKey];
delete formSetting.updateFormFields.oldKey;
delete formSetting.updateFormFields[oldKey];
formSetting.updateFormFields[String(newKey)] = value;
}
/** 删除修改字段设置项 */
function deleteFormFieldSetting(formSetting: FormTriggerSetting, key: string) {
if (!formSetting?.updateFormFields) return;
delete formSetting.updateFormFields.key;
delete formSetting.updateFormFields[key];
}
/** 保存配置 */

View File

@@ -135,6 +135,7 @@ function openStartDeptSelect() {
/** 处理部门选择确认 */
function handleDeptSelectConfirm(depts: SystemDeptApi.Dept[]) {
selectedStartDepts.value = depts;
modelData.value = {
...modelData.value,
startDeptIds: depts.map((d) => d.id),

View File

@@ -8,13 +8,14 @@ import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { useTabs } from '@vben/hooks';
import { Button, Card, TabPane, Tabs } from 'antdv-next';
import { Card, TabPane, Tabs } from 'antdv-next';
import { getBusiness } from '#/api/crm/business';
import { getOperateLogPage } from '#/api/crm/operateLog';
import { BizTypeEnum } from '#/api/crm/permission';
import { useDescription } from '#/components/description';
import { OperateLog } from '#/components/operate-log';
import { ACTION_ICON, TableAction } from '#/components/table-action';
import { $t } from '#/locales';
import { ContactDetailsList } from '#/views/crm/contact/components';
import { ContractDetailsList } from '#/views/crm/contract/components';
@@ -108,27 +109,36 @@ onMounted(() => {
<TransferModal @success="getBusinessDetail" />
<UpStatusModal @success="getBusinessDetail" />
<template #extra>
<div class="flex items-center gap-2">
<Button
v-if="permissionListRef?.validateWrite"
type="primary"
@click="handleEdit"
>
{{ $t('ui.actionTitle.edit') }}
</Button>
<Button
v-if="permissionListRef?.validateWrite"
@click="handleUpdateStatus"
>
变更商机状态
</Button>
<Button
v-if="permissionListRef?.validateOwnerUser"
@click="handleTransfer"
>
转移
</Button>
</div>
<TableAction
:actions="[
{
label: '返回',
type: 'default',
icon: 'lucide:arrow-left',
onClick: handleBack,
},
{
label: $t('ui.actionTitle.edit'),
type: 'primary',
icon: ACTION_ICON.EDIT,
auth: ['crm:business:update'],
ifShow: permissionListRef?.validateWrite,
onClick: handleEdit,
},
{
label: '变更商机状态',
type: 'primary',
ifShow: permissionListRef?.validateWrite,
onClick: handleUpdateStatus,
},
{
label: '转移',
type: 'primary',
ifShow: permissionListRef?.validateOwnerUser,
onClick: handleTransfer,
},
]"
/>
</template>
<Card class="min-h-[10%]">
<Descriptions :data="business" />

View File

@@ -1,5 +1,7 @@
import type { VbenFormSchema } from '#/adapter/form';
import { z } from '#/adapter/form';
export const schema: VbenFormSchema[] = [
{
component: 'RadioGroup',
@@ -14,17 +16,17 @@ export const schema: VbenFormSchema[] = [
defaultValue: true,
},
{
component: 'InputNumber',
component: 'Input',
fieldName: 'notifyDays',
componentProps: {
placeholder: '请输入天数',
class: '!w-full',
min: 0,
precision: 0,
},
renderComponentContent: () => ({
addonBefore: () => '提前',
addonAfter: () => '天提醒',
}),
rules: z.coerce.number().int().min(0, '天数不能小于 0'),
dependencies: {
triggerFields: ['notifyEnabled'],
show: (values) => values.notifyEnabled,

View File

@@ -1,5 +1,7 @@
import type { VbenFormSchema } from '#/adapter/form';
import { z } from '#/adapter/form';
export const schema: VbenFormSchema[] = [
{
component: 'RadioGroup',
@@ -13,33 +15,33 @@ export const schema: VbenFormSchema[] = [
},
},
{
component: 'InputNumber',
component: 'Input',
fieldName: 'contactExpireDays',
componentProps: {
placeholder: '请输入天数',
class: '!w-full',
min: 0,
precision: 0,
},
renderComponentContent: () => ({
addonAfter: () => '天不跟进或',
}),
rules: z.coerce.number().int().min(0, '天数不能小于 0'),
dependencies: {
triggerFields: ['enabled'],
show: (value) => value.enabled,
},
},
{
component: 'InputNumber',
component: 'Input',
fieldName: 'dealExpireDays',
renderComponentContent: () => ({
addonBefore: () => '或',
addonAfter: () => '天未成交',
}),
componentProps: {
placeholder: '请输入天数',
class: '!w-full',
min: 0,
precision: 0,
},
rules: z.coerce.number().int().min(0, '天数不能小于 0'),
dependencies: {
triggerFields: ['enabled'],
show: (value) => value.enabled,
@@ -62,17 +64,17 @@ export const schema: VbenFormSchema[] = [
defaultValue: false,
},
{
component: 'InputNumber',
component: 'Input',
fieldName: 'notifyDays',
componentProps: {
placeholder: '请输入天数',
class: '!w-full',
min: 0,
precision: 0,
},
renderComponentContent: () => ({
addonBefore: () => '提前',
addonAfter: () => '天提醒',
}),
rules: z.coerce.number().int().min(0, '天数不能小于 0'),
dependencies: {
triggerFields: ['notifyEnabled'],
show: (value) => value.enabled && value.notifyEnabled,

View File

@@ -99,7 +99,7 @@ function calculateWidth() {
:class="[
{
'w-full': property.layoutType !== 'oneColSmallImg',
'w-[calc(100vh-140px-16px)]':
'w-[calc(100%-140px-16px)]':
property.layoutType === 'oneColSmallImg',
},
]"

View File

@@ -44,6 +44,9 @@ defineProps<{
<div v-else-if="item.event === 'location_select'">
<Tag>选择地理位置</Tag>
</div>
<div v-else-if="item.event === 'SCAN'">
<Tag>扫码</Tag>
</div>
<div v-else>
<Tag color="error">未知事件类型</Tag>
</div>

View File

@@ -213,6 +213,7 @@ function displayQrCode(channelCode: string, data: any) {
url: data.displayContent,
visible: true,
};
modalApi.open();
}
/** 提交支付后App */

View File

@@ -126,51 +126,56 @@ const resetTaskForm = () => {
(ex: any) => ex.$type === `${prefix}:CandidateParam`,
)?.value;
if (candidateParamStr && candidateParamStr.length > 0) {
if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) {
// 特殊:流程表达式,只有一个 input 输入框
// @ts-expect-error: expression strategy stores a scalar in an array-shaped field
userTaskForm.value.candidateParam = [candidateParamStr];
} else if (
userTaskForm.value.candidateStrategy ===
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
) {
// 特殊:多级不部门负责人,需要通过'|'分割
userTaskForm.value.candidateParam = candidateParamStr
.split('|')[0]
.split(',')
.map((item: any) => {
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
const num = Number(item);
return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER
? item
: num;
});
deptLevel.value = +candidateParamStr.split('|')[1];
} else if (
userTaskForm.value.candidateStrategy ===
CandidateStrategy.START_USER_DEPT_LEADER ||
userTaskForm.value.candidateStrategy ===
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
) {
// @ts-expect-error: dynamic candidate param shape varies by strategy
userTaskForm.value.candidateParam = +candidateParamStr;
deptLevel.value = +candidateParamStr;
} else if (
userTaskForm.value.candidateStrategy ===
CandidateStrategy.FORM_DEPT_LEADER
) {
userTaskForm.value.candidateParam = candidateParamStr.split('|')[0];
deptLevel.value = +candidateParamStr.split('|')[1];
} else {
userTaskForm.value.candidateParam = candidateParamStr
.split(',')
.map((item: any) => {
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
const num = Number(item);
return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER
? item
: num;
});
const candidateStrategy = userTaskForm.value.candidateStrategy as
| CandidateStrategy
| undefined;
switch (candidateStrategy) {
case CandidateStrategy.EXPRESSION: {
// 特殊:流程表达式,只有一个 input 输入框
// @ts-expect-error: expression strategy stores a scalar in an array-shaped field
userTaskForm.value.candidateParam = [candidateParamStr];
break;
}
case CandidateStrategy.FORM_DEPT_LEADER: {
userTaskForm.value.candidateParam = candidateParamStr.split('|')[0];
deptLevel.value = +candidateParamStr.split('|')[1];
break;
}
case CandidateStrategy.MULTI_LEVEL_DEPT_LEADER: {
// 特殊:多级不部门负责人,需要通过'|'分割
userTaskForm.value.candidateParam = candidateParamStr
.split('|')[0]
.split(',')
.map((item: any) => {
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
const num = Number(item);
return num > Number.MAX_SAFE_INTEGER ||
num < -Number.MAX_SAFE_INTEGER
? item
: num;
});
deptLevel.value = +candidateParamStr.split('|')[1];
break;
}
case CandidateStrategy.START_USER_DEPT_LEADER:
case CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER: {
// @ts-expect-error: dynamic candidate param shape varies by strategy
userTaskForm.value.candidateParam = +candidateParamStr;
deptLevel.value = +candidateParamStr;
break;
}
default: {
userTaskForm.value.candidateParam = candidateParamStr
.split(',')
.map((item: any) => {
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
const num = Number(item);
return num > Number.MAX_SAFE_INTEGER ||
num < -Number.MAX_SAFE_INTEGER
? item
: num;
});
}
}
} else {
userTaskForm.value.candidateParam = [];
@@ -402,7 +407,6 @@ onBeforeUnmount(() => {
:props="defaultProps"
placeholder="加载中,请稍后"
multiple
check-strictly
show-checkbox
@change="updateElementTask"
/>

View File

@@ -9,6 +9,22 @@ import { useVbenDrawer } from '@vben/common-ui';
import { BpmNodeTypeEnum } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import {
ElButton,
ElDatePicker,
ElDivider,
ElForm,
ElFormItem,
ElInput,
ElInputNumber,
ElOption,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElSelect,
ElSwitch,
} from 'element-plus';
import { getForm } from '#/api/bpm/form';
import { getModelList } from '#/api/bpm/model';
@@ -442,7 +458,7 @@ onMounted(async () => {
>
<div class="mr-2 mt-1">
<ElFormItem
:prop="['inVariables', index, 'source']"
:prop="`inVariables.${index}.source`"
:rules="{
required: true,
message: '变量不能为空',
@@ -461,7 +477,7 @@ onMounted(async () => {
</div>
<div class="mr-2 mt-1">
<ElFormItem
:prop="['inVariables', index, 'target']"
:prop="`inVariables.${index}.target`"
:rules="{
required: true,
message: '变量不能为空',
@@ -510,7 +526,7 @@ onMounted(async () => {
>
<div class="mr-2 mt-1">
<ElFormItem
:prop="['outVariables', index, 'source']"
:prop="`outVariables.${index}.source`"
:rules="{
required: true,
message: '变量不能为空',
@@ -529,7 +545,7 @@ onMounted(async () => {
</div>
<div class="mr-2 mt-1">
<ElFormItem
:prop="['outVariables', index, 'target']"
:prop="`outVariables.${index}.target`"
:rules="{
required: true,
message: '变量不能为空',

View File

@@ -258,14 +258,14 @@ function updateFormFieldKey(
) {
if (!formSetting?.updateFormFields || !newKey) return;
const value = formSetting.updateFormFields[oldKey];
delete formSetting.updateFormFields.oldkey;
delete formSetting.updateFormFields[oldKey];
formSetting.updateFormFields[String(newKey)] = value;
}
/** 删除修改字段设置项 */
function deleteFormFieldSetting(formSetting: FormTriggerSetting, key: string) {
if (!formSetting?.updateFormFields) return;
delete formSetting.updateFormFields.key;
delete formSetting.updateFormFields[key];
}
/** 保存配置 */

View File

@@ -103,7 +103,7 @@ onBeforeUnmount(() => {
<div class="mb-3">
<ElAlert
title="输入 @ 可选择插入流程选项和表单选项"
type="primary"
type="info"
show-icon
/>
</div>

View File

@@ -5,6 +5,7 @@ import { useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { base64ToFile } from '@vben/utils';
import { ElButton, ElTooltip } from 'element-plus';
import Vue3Signature from 'vue3-signature';
import { uploadFile } from '#/api/infra/file';

View File

@@ -9,6 +9,7 @@ import { DICT_TYPE } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import FormCreate from '@form-create/element-ui';
import { ElButton } from 'element-plus';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getTaskListByProcessInstanceId } from '#/api/bpm/task';

View File

@@ -10,7 +10,7 @@ import {
erpPriceMultiply,
} from '@vben/utils';
import { ElInput, ElInputNumber, ElSelect } from 'element-plus';
import { ElInput, ElInputNumber, ElOption, ElSelect } from 'element-plus';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProductSimpleList } from '#/api/erp/product/product';
@@ -219,26 +219,36 @@ onMounted(async () => {
<template #warehouseId="{ row }">
<ElSelect
v-model="row.warehouseId"
:options="warehouseOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择仓库"
filterable
:disabled="disabled"
@change="handleWarehouseChange($event, row)"
/>
>
<ElOption
v-for="warehouse in warehouseOptions"
:key="warehouse.id"
:label="warehouse.name"
:value="warehouse.id!"
/>
</ElSelect>
</template>
<template #productId="{ row }">
<ElSelect
v-model="row.productId"
:options="productOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择产品"
filterable
:disabled="disabled"
@change="handleProductChange($event, row)"
/>
>
<ElOption
v-for="product in productOptions"
:key="product.id"
:label="product.name"
:value="product.id!"
/>
</ElSelect>
</template>
<template #actualCount="{ row }">
<ElInputNumber

View File

@@ -10,7 +10,7 @@ import {
erpPriceMultiply,
} from '@vben/utils';
import { ElInput, ElInputNumber, ElSelect } from 'element-plus';
import { ElInput, ElInputNumber, ElOption, ElSelect } from 'element-plus';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProductSimpleList } from '#/api/erp/product/product';
@@ -208,26 +208,36 @@ onMounted(async () => {
<template #warehouseId="{ row }">
<ElSelect
v-model="row.warehouseId"
:options="warehouseOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择仓库"
filterable
:disabled="disabled"
@change="handleWarehouseChange($event, row)"
/>
>
<ElOption
v-for="warehouse in warehouseOptions"
:key="warehouse.id"
:label="warehouse.name"
:value="warehouse.id!"
/>
</ElSelect>
</template>
<template #productId="{ row }">
<ElSelect
v-model="row.productId"
:options="productOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择产品"
filterable
:disabled="disabled"
@change="handleProductChange($event, row)"
/>
>
<ElOption
v-for="product in productOptions"
:key="product.id"
:label="product.name"
:value="product.id!"
/>
</ElSelect>
</template>
<template #count="{ row }">
<ElInputNumber

View File

@@ -10,7 +10,7 @@ import {
erpPriceMultiply,
} from '@vben/utils';
import { ElInput, ElInputNumber, ElSelect } from 'element-plus';
import { ElInput, ElInputNumber, ElOption, ElSelect } from 'element-plus';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProductSimpleList } from '#/api/erp/product/product';
@@ -216,38 +216,53 @@ onMounted(async () => {
<template #fromWarehouseId="{ row }">
<ElSelect
v-model="row.fromWarehouseId"
:options="warehouseOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择调出仓库"
filterable
:disabled="disabled"
@change="handleFromWarehouseChange($event, row)"
/>
>
<ElOption
v-for="warehouse in warehouseOptions"
:key="warehouse.id"
:label="warehouse.name"
:value="warehouse.id!"
/>
</ElSelect>
</template>
<template #toWarehouseId="{ row }">
<ElSelect
v-model="row.toWarehouseId"
:options="warehouseOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择调入仓库"
filterable
:disabled="disabled"
@change="handleToWarehouseChange($event, row)"
/>
>
<ElOption
v-for="warehouse in warehouseOptions"
:key="warehouse.id"
:label="warehouse.name"
:value="warehouse.id!"
/>
</ElSelect>
</template>
<template #productId="{ row }">
<ElSelect
v-model="row.productId"
:options="productOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择产品"
filterable
:disabled="disabled"
@change="handleProductChange($event, row)"
/>
>
<ElOption
v-for="product in productOptions"
:key="product.id"
:label="product.name"
:value="product.id!"
/>
</ElSelect>
</template>
<template #count="{ row }">
<ElInputNumber

View File

@@ -10,7 +10,7 @@ import {
erpPriceMultiply,
} from '@vben/utils';
import { ElInput, ElInputNumber, ElSelect } from 'element-plus';
import { ElInput, ElInputNumber, ElOption, ElSelect } from 'element-plus';
import { TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProductSimpleList } from '#/api/erp/product/product';
@@ -206,26 +206,36 @@ onMounted(async () => {
<template #warehouseId="{ row }">
<ElSelect
v-model="row.warehouseId"
:options="warehouseOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择仓库"
filterable
:disabled="disabled"
@change="handleWarehouseChange($event, row)"
/>
>
<ElOption
v-for="warehouse in warehouseOptions"
:key="warehouse.id"
:label="warehouse.name"
:value="warehouse.id!"
/>
</ElSelect>
</template>
<template #productId="{ row }">
<ElSelect
v-model="row.productId"
:options="productOptions"
:props="{ label: 'name', value: 'id' }"
class="w-full"
placeholder="请选择产品"
filterable
:disabled="disabled"
@change="handleProductChange($event, row)"
/>
>
<ElOption
v-for="product in productOptions"
:key="product.id"
:label="product.name"
:value="product.id!"
/>
</ElSelect>
</template>
<template #count="{ row }">
<ElInputNumber

View File

@@ -150,7 +150,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
:title="title"
destroy-on-close
width="65%"
@after-open-change="handleOpenChange"
@opened="handleOpenChange(true)"
>
<ItemFormModal @success="handleRefresh" />
<Grid table-title="表情列表">

View File

@@ -49,9 +49,9 @@ async function handleMaster(row: InfraFileConfigApi.FileConfig) {
});
try {
await updateFileConfigMaster(row.id!);
ElMessage.success($t('ui.actionMessage.operationSuccess'));
ElMessage.success($t('ui.actionMessage.updateSuccess'));
handleRefresh();
} catch {
} finally {
loadingInstance.close();
}
}
@@ -63,7 +63,6 @@ async function handleTest(row: InfraFileConfigApi.FileConfig) {
});
try {
const response = await testFileConfig(row.id!);
loadingInstance.close();
// 确认是否访问该文件
confirm({
title: '测试上传成功',
@@ -73,7 +72,7 @@ async function handleTest(row: InfraFileConfigApi.FileConfig) {
}).then(() => {
openWindow(response);
});
} catch {
} finally {
loadingInstance.close();
}
}

View File

@@ -59,7 +59,7 @@ onMounted(async () => {
}"
:multiple="multiple"
:show-checkbox="multiple"
check-strictly
clearable
default-expand-all
class="w-full"
placeholder="请选择商品分类"

View File

@@ -15,12 +15,14 @@ import { floatToFixed2 } from '@vben/utils';
import { useVModel } from '@vueuse/core';
import {
ElButton,
ElCard,
ElForm,
ElFormItem,
ElRadioButton,
ElRadioGroup,
ElSlider,
ElText,
ElTooltip,
} from 'element-plus';

View File

@@ -6,6 +6,7 @@ import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core';
import {
ElCard,
ElCheckbox,
ElForm,
ElFormItem,
ElInput,

View File

@@ -10,6 +10,7 @@ import {
ElForm,
ElFormItem,
ElInput,
ElRadio,
ElRadioButton,
ElRadioGroup,
ElSlider,

View File

@@ -10,6 +10,7 @@ import { cloneDeep, isEmpty, isString } from '@vben/utils';
import { useQRCode } from '@vueuse/integrations/useQRCode';
import {
ElAside,
ElButton,
ElCard,
ElContainer,
ElHeader,

View File

@@ -81,7 +81,10 @@ const handleDelete = function (index: number) {
</div>
</template>
</VueDraggable>
<ElTooltip :disabled="limit < 1" :content="`最多添加${limit}个`">
<ElTooltip
:disabled="limit >= Number.MAX_VALUE"
:content="`最多添加${limit}个`"
>
<ElButton
type="primary"
plain

View File

@@ -67,7 +67,7 @@ async function getPageDetail(id: any) {
// 拼接手机预览链接
const domain = import.meta.env.VITE_MALL_H5_DOMAIN;
const accessStore = useAccessStore();
previewUrl.value = `${domain}?templateId=${formData.value.id}&${accessStore.tenantId}`;
previewUrl.value = `${domain}?templateId=${formData.value.id}&tenantId=${accessStore.tenantId}`;
} finally {
loadingInstance.close();
}

View File

@@ -122,7 +122,7 @@ async function handleExport() {
</script>
<template>
<ElCard :bordered="false" shadow="never" class="h-full">
<ElCard :border="false" shadow="never" class="h-full">
<template #header>
<div class="flex items-center justify-between">
<span class="text-base font-medium">交易状况</span>

View File

@@ -11,6 +11,7 @@ import { formatDate, isEmpty } from '@vben/utils';
import {
ElAvatar,
ElButton,
ElDescriptions,
ElDescriptionsItem,
ElDivider,

View File

@@ -11,6 +11,7 @@ import { formatDate, isEmpty } from '@vben/utils';
import {
ElAvatar,
ElButton,
ElDescriptions,
ElDescriptionsItem,
ElDivider,

View File

@@ -165,7 +165,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
link: true,
ifShow: () =>
row.deliveryType === DeliveryTypeEnum.EXPRESS.type &&
row.status === TradeOrderStatusEnum.UNDELIVERED.status,
(row.status === TradeOrderStatusEnum.UNDELIVERED.status ||
row.status === TradeOrderStatusEnum.DELIVERED.status),
onClick: handleDelivery.bind(null, row),
},
{

View File

@@ -129,13 +129,13 @@ const [Modal, modalApi] = useVbenModal({
v-model="subTabsName"
class="mx-4 mt-4"
>
<ElTabPane name="machine" label="设备资源">
<ElTabPane name="machine" label="设备资源" lazy>
<MachineList :form-type="formType" :workstation-id="formData.id" />
</ElTabPane>
<ElTabPane name="tool" label="工装夹具">
<ElTabPane name="tool" label="工装夹具" lazy>
<ToolList :form-type="formType" :workstation-id="formData.id" />
</ElTabPane>
<ElTabPane name="worker" label="人力资源">
<ElTabPane name="worker" label="人力资源" lazy>
<WorkerList :form-type="formType" :workstation-id="formData.id" />
</ElTabPane>
</ElTabs>

View File

@@ -30,7 +30,7 @@ withDefaults(
<div v-else-if="item.type === MpMsgType.Text">{{ item.content }}</div>
<div v-else-if="item.type === MpMsgType.Voice">
<WxVideoPlayer :url="item.mediaUrl" :content="item.recognition" />
<WxVoicePlayer :url="item.mediaUrl" :content="item.recognition" />
</div>
<div v-else-if="item.type === MpMsgType.Image">
@@ -43,7 +43,7 @@ withDefaults(
v-else-if="item.type === MpMsgType.Video || item.type === 'shortvideo'"
class="text-center"
>
<WxVoicePlayer :url="item.mediaUrl" />
<WxVideoPlayer :url="item.mediaUrl" />
</div>
<div v-else-if="item.type === MpMsgType.Link" class="flex-1">

View File

@@ -133,14 +133,30 @@ function selectMaterial(item: any) {
</ElDialog>
</ElCol>
<ElCol :span="18">
<ElInput v-model="reply.title" placeholder="请输入标题" />
<ElInput
:model-value="reply.title || undefined"
placeholder="请输入标题"
@update:model-value="(val) => (reply.title = val || null)"
/>
<div class="my-5"></div>
<ElInput v-model="reply.description" placeholder="请输入描述" />
<ElInput
:model-value="reply.description || undefined"
placeholder="请输入描述"
@update:model-value="(val) => (reply.description = val || null)"
/>
</ElCol>
</ElRow>
<div class="my-5"></div>
<ElInput v-model="reply.musicUrl" placeholder="请输入音乐链接" />
<ElInput
:model-value="reply.musicUrl || undefined"
placeholder="请输入音乐链接"
@update:model-value="(val) => (reply.musicUrl = val || null)"
/>
<div class="my-5"></div>
<ElInput v-model="reply.hqMusicUrl" placeholder="请输入高质量音乐链接" />
<ElInput
:model-value="reply.hqMusicUrl || undefined"
placeholder="请输入高质量音乐链接"
@update:model-value="(val) => (reply.hqMusicUrl = val || null)"
/>
</div>
</template>

View File

@@ -12,10 +12,10 @@ const emit = defineEmits<{
}>();
const content = computed({
get: () => props.modelValue,
set: (val: null | string) => {
emit('update:modelValue', val);
emit('input', val);
get: () => props.modelValue ?? '',
set: (val: string) => {
emit('update:modelValue', val || null);
emit('input', val || null);
},
});
</script>

View File

@@ -91,11 +91,17 @@ function selectMaterial(item: any) {
<template>
<div>
<ElRow>
<ElInput v-model="reply.title" class="mb-[2%]" placeholder="请输入标题" />
<ElInput
:model-value="reply.title || undefined"
class="mb-[2%]"
placeholder="请输入标题"
@update:model-value="(val) => (reply.title = val || null)"
/>
<ElInput
class="mb-[2%]"
v-model="reply.description"
:model-value="reply.description || undefined"
placeholder="请输入描述"
@update:model-value="(val) => (reply.description = val || null)"
/>
<ElRow class="w-full pt-[10px] text-center" justify="center">
<VideoPlayer v-if="reply.url" :url="reply.url" />

View File

@@ -113,7 +113,7 @@ defineExpose({
</script>
<template>
<ElTabs type="border-card" v-model="reply.type" @tab-change="clear">
<ElTabs type="border-card" v-model="currentTab">
<!-- 类型 1文本 -->
<ElTabPane :name="ReplyType.Text">
<template #label>

View File

@@ -13,11 +13,12 @@ import {
import { useTabs } from '@vben/hooks';
import { fenToYuan, formatDate } from '@vben/utils';
import { useQRCode } from '@vueuse/integrations/useQRCode';
import {
ElButton,
ElCard,
ElDescriptions,
ElImage,
ElDescriptionsItem,
ElInput,
ElMessage,
} from 'element-plus';
@@ -48,6 +49,10 @@ const qrCode = ref({
url: '',
visible: false,
});
const qrCodeImage = useQRCode(() => qrCode.value.url, {
errorCorrectionLevel: 'H',
margin: 4,
});
/** 展示形式:条形码 */
const barCode = ref({
@@ -212,6 +217,7 @@ function displayQrCode(channelCode: string, data: any) {
url: data.displayContent,
visible: true,
};
modalApi.open();
}
/** 提交支付后App */
@@ -307,24 +313,24 @@ onBeforeUnmount(() => {
<Page auto-content-height>
<ElCard class="mt-4">
<ElDescriptions :column="3" :title="payOrder?.subject ?? '商品详情'">
<ElDescriptions.Item label="支付单号">
<ElDescriptionsItem label="支付单号">
{{ payOrder?.id }}
</ElDescriptions.Item>
<ElDescriptions.Item label="商品标题">
</ElDescriptionsItem>
<ElDescriptionsItem label="商品标题">
{{ payOrder?.subject }}
</ElDescriptions.Item>
<ElDescriptions.Item label="商品内容">
</ElDescriptionsItem>
<ElDescriptionsItem label="商品内容">
{{ payOrder?.body }}
</ElDescriptions.Item>
<ElDescriptions.Item label="支付金额">
</ElDescriptionsItem>
<ElDescriptionsItem label="支付金额">
{{ `${fenToYuan(payOrder?.price || 0)}` }}
</ElDescriptions.Item>
<ElDescriptions.Item label="创建时间">
</ElDescriptionsItem>
<ElDescriptionsItem label="创建时间">
{{ formatDate(payOrder?.createTime) }}
</ElDescriptions.Item>
<ElDescriptions.Item label="过期时间">
</ElDescriptionsItem>
<ElDescriptionsItem label="过期时间">
{{ formatDate(payOrder?.expireTime) }}
</ElDescriptions.Item>
</ElDescriptionsItem>
</ElDescriptions>
</ElCard>
<ElCard title="选择支付宝支付" class="mt-4">
@@ -373,10 +379,15 @@ onBeforeUnmount(() => {
</div>
</ElCard>
<Modal class="w-2/5" :title="title">
<ElImage v-if="qrCode.visible" :src="qrCode.url" />
<img
v-if="qrCode.visible"
:src="qrCodeImage"
alt="支付二维码"
class="max-w-full"
/>
<ElInput
v-if="barCode.visible"
v-model:value="barCode.value"
v-model="barCode.value"
placeholder="请输入条形码"
required
/>

View File

@@ -181,6 +181,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
auth: ['system:dept:delete'],
disabled: row.children && row.children.length > 0,
popConfirm: {
disabled: row.children && row.children.length > 0,
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
confirm: handleDelete.bind(null, row),
},

View File

@@ -325,11 +325,13 @@ export function useDataGridColumns(): VxeTableGridOptions['columns'] {
field: 'colorType',
title: '颜色类型',
minWidth: 120,
slots: { default: 'colorType' },
},
{
field: 'cssClass',
title: 'CSS Class',
minWidth: 120,
slots: { default: 'cssClass' },
},
{
title: '创建时间',

View File

@@ -7,7 +7,7 @@ import { ref, watch } from 'vue';
import { confirm, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
import { ElLoading, ElMessage } from 'element-plus';
import { ElLoading, ElMessage, ElTag } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -172,6 +172,12 @@ watch(
]"
/>
</template>
<template #colorType="{ row }">
<ElTag :color="row.colorType">{{ row.colorType }}</ElTag>
</template>
<template #cssClass="{ row }">
<ElTag :color="row.cssClass">{{ row.cssClass }}</ElTag>
</template>
<template #actions="{ row }">
<TableAction
:actions="[

View File

@@ -174,7 +174,7 @@ export function useFormSchema(): VbenFormSchema[] {
};
};
const results = queryString
? options.filter(() => createFilter(queryString))
? options.filter(createFilter(queryString))
: options;
cb(results);
},