fix(ts): 低风险类型修复并修复预览交互问题

- formatDate 入参放宽为 dayjs.ConfigType,删除冗余 formatDateByConfig
- 多处 ref([]) 补精确数组类型(BPM/AI workflow/SMS log/DiyEditor 等)
- 路由参数/模板 index 显式 Number(),Upload 响应补局部类型
- LeaveCreateData 局部扩展 startUserSelectAssignees,不污染共享 VO
- 修复 mall 订单详情 formatDate.deliveryTime typo(发货时间行此前不显示)
- 修复 FloatingActionButton 缺失 handleActive,预览态点击仅收起面板

ts:check 542 → 478,无新增类型错误
master
YunaiV 2026-06-20 21:46:59 -07:00
parent 3f779091be
commit bc25430fa5
23 changed files with 104 additions and 53 deletions

View File

@ -115,5 +115,7 @@ export const getOrderCountTrendComparison = (
/** 时间参数需要格式化, 确保接口能识别 */
const formatDateParam = (params: TradeTrendReqVO) => {
return { times: [formatDate(params.times[0]), formatDate(params.times[1])] } as TradeTrendReqVO
return {
times: [formatDate(params.times[0]), formatDate(params.times[1])]
} as TradeTrendReqVO
}

View File

@ -13,7 +13,7 @@
v-for="(item, index) in property.list"
:key="index"
class="flex flex-col items-center"
@click="handleActive(index)"
@click="handleActive"
>
<el-image :src="item.imgUrl" fit="contain" class="h-27px w-27px">
<template #error>
@ -49,6 +49,10 @@ const expanded = ref(false)
const handleToggleFab = () => {
expanded.value = !expanded.value
}
const handleActive = () => {
expanded.value = false
}
</script>
<style scoped lang="scss">

View File

@ -54,7 +54,7 @@
class="text-12px"
:style="{ color: property.fields.price.color }"
>
{{ fenToYuan(spu.price) }}
{{ fenToYuan(spu.price || 0) }}
</span>
</div>
</div>
@ -76,7 +76,9 @@ const spuList = ref<ProductSpuApi.Spu[]>([])
watch(
() => props.property.spuIds,
async () => {
spuList.value = await ProductSpuApi.getSpuDetailList(props.property.spuIds)
spuList.value = props.property.spuIds
? await ProductSpuApi.getSpuDetailList(props.property.spuIds)
: []
},
{
immediate: true,

View File

@ -35,7 +35,7 @@ const props = defineProps<{ modelValue: PromotionArticleProperty }>()
const emit = defineEmits(['update:modelValue'])
const formData = useVModel(props, 'modelValue', emit)
//
const articles = ref<ArticleApi.ArticleVO>([])
const articles = ref<ArticleApi.ArticleVO[]>([])
//
const loading = ref(false)

View File

@ -301,7 +301,7 @@
ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY
"
>
<el-input-number v-model="configForm.multiInstanceSource" :min="1" />
<el-input-number v-model="multiInstanceSourceNumber" :min="1" />
</el-form-item>
<el-form-item
v-if="
@ -453,6 +453,12 @@ const digitalFormFieldOptions = computed(() => {
const multiFormFieldOptions = computed(() => {
return formFieldOptions.filter((item) => item.type === 'select' || item.type === 'checkbox')
})
const multiInstanceSourceNumber = computed({
get: () => Number(configForm.value.multiInstanceSource || 1),
set: (value?: number) => {
configForm.value.multiInstanceSource = String(value || '')
}
})
const childFormFieldOptions = ref()
//

View File

@ -64,6 +64,7 @@
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import type { ComponentPublicInstance } from 'vue'
import { SimpleFlowNode, NodeType, ConditionType, RouterSetting } from '../consts'
import { useWatchNode, useDrawer, useNodeName } from '../node'
import Condition from './components/Condition.vue'
@ -86,15 +87,18 @@ const currentNode = useWatchNode(props)
//
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
const routerGroups = ref<RouterSetting[]>([])
const nodeOptions = ref<any>([])
const conditionRef = ref([])
const nodeOptions = ref<Array<{ label: string; value: string }>>([])
type ConditionRef = ComponentPublicInstance & {
validate?: () => Promise<boolean>
}
const conditionRef = ref<Array<ConditionRef | Element | null>>([])
/** 保存配置 */
const saveConfig = async () => {
//
let valid = true
for (const item of conditionRef.value) {
if (item && !(await item.validate())) {
if (item && 'validate' in item && item.validate && !(await item.validate())) {
valid = false
}
}
@ -173,7 +177,7 @@ const deleteRouterGroup = (index: number) => {
}
//
const getRouterNode = (node) => {
const getRouterNode = (node?: SimpleFlowNode) => {
// TODO
//
//

View File

@ -70,7 +70,7 @@
<Icon
icon="ep:delete"
:size="18"
@click="deleteHttpResponseSetting(setting.response!, index)"
@click="deleteHttpResponseSetting(setting.response!, Number(index))"
/>
</div>
</div>

View File

@ -132,10 +132,13 @@ const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
//
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
const response = res as { data: string }
//
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
const index = fileList.value.findIndex(
(item) => (item.response as { data?: string } | undefined)?.data === response.data
)
fileList.value.splice(index, 1)
uploadList.value.push({ name: res.data, url: res.data })
uploadList.value.push({ name: response.data, url: response.data })
if (uploadList.value.length == uploadNumber.value) {
fileList.value.push(...uploadList.value)
uploadList.value = []

View File

@ -135,10 +135,13 @@ interface UploadEmits {
const emit = defineEmits<UploadEmits>()
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
const response = res as { data: string }
//
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
const index = fileList.value.findIndex(
(item) => (item.response as { data?: string } | undefined)?.data === response.data
)
fileList.value.splice(index, 1)
uploadList.value.push({ name: res.data, url: res.data })
uploadList.value.push({ name: response.data, url: response.data })
if (uploadList.value.length == uploadNumber.value) {
fileList.value.push(...uploadList.value)
uploadList.value = []

View File

@ -12,7 +12,7 @@ export const useNProgress = () => {
await nextTick()
const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
if (bar) {
bar.style.background = unref(primaryColor.value)
bar.style.background = unref(primaryColor.value) || ''
}
}

View File

@ -12,6 +12,6 @@ export const setupElementPlus = (app: App<Element>) => {
})
components.forEach((component) => {
app.component(component.name, component)
app.component(component.name!, component)
})
}

View File

@ -126,7 +126,7 @@ const components = [
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
export const setupFormCreate = (app: App<Element>) => {
components.forEach((component) => {
app.component(component.name, component)
app.component(component.name!, component)
})
formCreate.use(install)
app.use(formCreate)

View File

@ -12,7 +12,7 @@ const HM_ID = import.meta.env.VITE_APP_BAIDU_CODE
const hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?' + HM_ID
const s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
s.parentNode?.insertBefore(hm, s)
})()
router.afterEach(function (to) {

View File

@ -54,7 +54,7 @@ export const defaultShortcuts = [
/**
*
* @param date new Date()
* @param date new Date()dayjs
* @param format
* @description format `YYYY-MM、YYYY-MM-DD`
* @description format "YYYY-MM-DD HH:mm:ss QQQQ"
@ -63,7 +63,7 @@ export const defaultShortcuts = [
* @description format + + "YYYY-MM-DD HH:mm:ss WWW QQQQ ZZZ"
* @returns
*/
export function formatDate(date: Date | string, format?: string): string {
export function formatDate(date: dayjs.ConfigType, format?: string): string {
// 日期不存在,则返回空
if (!date) {
return ''

View File

@ -48,7 +48,7 @@
<el-dropdown-item
v-for="(file, index) in modelData.list"
:key="index"
@click="selectFile(index)"
@click="selectFile(Number(index))"
>
{{ file.name }}
<span v-if="file.segments" class="ml-5px text-gray-500 text-12px">
@ -141,7 +141,7 @@ const splitContent = async (file: any) => {
// Token
file.segments = await KnowledgeSegmentApi.splitContent(
file.url,
modelData.value.segmentMaxTokens
Number(modelData.value.segmentMaxTokens)
)
} catch (error) {
console.error('获取分段内容失败:', file, error)

View File

@ -48,7 +48,7 @@
<Icon icon="ep:document" class="mr-8px text-[#409eff]" />
<span class="text-[13px] text-[#303133] break-all">{{ file.name }}</span>
</div>
<el-button type="danger" link @click="removeFile(index)" class="ml-2">
<el-button type="danger" link @click="removeFile(Number(index))" class="ml-2">
<Icon icon="ep:delete" />
</el-button>
</div>

View File

@ -70,14 +70,28 @@ defineProps<{
provider: any
}>()
type WorkflowParam = {
key: string
value: string
}
type StartNodeParameter = {
name: string
dataType: string
description?: string
disabled?: boolean
required?: boolean
defaultValue?: string
}
const tinyflowRef = ref()
const workflowData = inject('workflowData') as Ref
const showTestDrawer = ref(false)
const params4Test = ref([])
const paramsOfStartNode = ref({})
const params4Test = ref<WorkflowParam[]>([])
const paramsOfStartNode = ref<Record<string, StartNodeParameter>>({})
const testResult = ref(null)
const loading = ref(false)
const error = ref(null)
const error = ref<string | null>(null)
/** 展示工作流测试抽屉 */
const testWorkflowModel = () => {
@ -96,8 +110,8 @@ const goRun = async () => {
//
const parameters = startNode.data?.parameters || []
const paramDefinitions = {}
parameters.forEach((param) => {
const paramDefinitions: Record<string, string> = {}
parameters.forEach((param: StartNodeParameter) => {
paramDefinitions[param.name] = param.dataType
})
@ -115,7 +129,8 @@ const goRun = async () => {
try {
convertedParams[paramKey] = convertParamValue(value, dataType)
} catch (e) {
throw new Error(`参数 ${paramKey} 转换失败: ${e.message}`)
const message = e instanceof Error ? e.message : String(e)
throw new Error(`参数 ${paramKey} 转换失败: ${message}`)
}
}
@ -127,7 +142,11 @@ const goRun = async () => {
const response = await WorkflowApi.testWorkflow(data)
testResult.value = response
} catch (err) {
error.value = err.response?.data?.message || '运行失败,请检查参数和网络连接'
const responseMessage =
err && typeof err === 'object' && 'response' in err
? (err as any).response?.data?.message
: undefined
error.value = responseMessage || '运行失败,请检查参数和网络连接'
} finally {
loading.value = false
}
@ -142,20 +161,20 @@ watch(showTestDrawer, (value) => {
//
const parameters = startNode.data?.parameters || []
const paramDefinitions = {}
const paramDefinitions: Record<string, StartNodeParameter> = {}
// 便
parameters.forEach((param) => {
parameters.forEach((param: StartNodeParameter) => {
paramDefinitions[param.name] = param
})
function mergeIfRequiredButNotSet(target) {
let needPushList = []
function mergeIfRequiredButNotSet(target: WorkflowParam[]) {
let needPushList: WorkflowParam[] = []
for (let key in paramDefinitions) {
let param = paramDefinitions[key]
if (param.required) {
let item = target.find((item) => item.key === key)
let item = target.find((item: WorkflowParam) => item.key === key)
if (!item) {
needPushList.push({ key: param.name, value: param.defaultValue || '' })
@ -186,12 +205,12 @@ const addParam = () => {
}
/** 删除参数项 */
const removeParam = (index) => {
const removeParam = (index: number) => {
params4Test.value.splice(index, 1)
}
/** 类型转换函数 */
const convertParamValue = (value, dataType) => {
const convertParamValue = (value: string, dataType: string) => {
if (value === '') return null //
switch (dataType) {
@ -210,7 +229,8 @@ const convertParamValue = (value, dataType) => {
try {
return JSON.parse(value)
} catch (e) {
throw new Error(`JSON格式错误: ${e.message}`)
const message = e instanceof Error ? e.message : String(e)
throw new Error(`JSON格式错误: ${message}`)
}
default:
throw new Error(`不支持的类型: ${dataType}`)

View File

@ -97,10 +97,17 @@ const formRules = reactive({
const formRef = ref() // Ref
//
type StartUserSelectTask = {
id: string
name: string
}
type LeaveCreateData = LeaveApi.LeaveVO & {
startUserSelectAssignees?: Record<string, number[]>
}
const processDefineKey = 'oa_leave' // Key
const startUserSelectTasks = ref([]) //
const startUserSelectAssignees = ref({}) //
const tempStartUserSelectAssignees = ref({}) //
const startUserSelectTasks = ref<StartUserSelectTask[]>([]) //
const startUserSelectAssignees = ref<Record<string, number[]>>({}) //
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}) //
const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) //
const processDefinitionId = ref('')
@ -125,7 +132,7 @@ const submitForm = async () => {
// 2.
formLoading.value = true
try {
const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
const data = { ...formData.value } as unknown as LeaveCreateData
//
if (startUserSelectTasks.value?.length > 0) {
data.startUserSelectAssignees = startUserSelectAssignees.value

View File

@ -167,7 +167,7 @@ import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
import { ElMessageBox } from 'element-plus'
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import { CategoryApi } from '@/api/bpm/category'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import * as UserApi from '@/api/system/user'
//
@ -179,7 +179,7 @@ const { t } = useI18n() // 国际化
const loading = ref(true) //
const total = ref(0) //
const list = ref([]) //
const list = ref<ProcessInstanceApi.ProcessInstanceVO[]>([]) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
@ -191,7 +191,7 @@ const queryParams = reactive({
createTime: []
})
const queryFormRef = ref() //
const categoryList = ref([]) //
const categoryList = ref<CategoryVO[]>([]) //
const userList = ref<any[]>([]) //
/** 查询列表 */
@ -219,7 +219,7 @@ const resetQuery = () => {
}
/** 查看详情 */
const handleDetail = (row) => {
const handleDetail = (row: ProcessInstanceApi.ProcessInstanceVO) => {
router.push({
name: 'BpmProcessInstanceDetail',
query: {
@ -229,7 +229,7 @@ const handleDetail = (row) => {
}
/** 取消按钮操作 */
const handleCancel = async (row) => {
const handleCancel = async (row: ProcessInstanceApi.ProcessInstanceVO) => {
//
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
confirmButtonText: t('common.ok'),

View File

@ -24,7 +24,7 @@ defineOptions({ name: 'CrmProductDetail' })
const route = useRoute()
const message = useMessage()
const id = route.params.id //
const id = Number(route.params.id) //
const loading = ref(true) //
const product = ref<ProductApi.ProductVO>({} as ProductApi.ProductVO) //

View File

@ -28,7 +28,7 @@ const { currentRoute } = useRouter()
const route = useRoute()
const message = useMessage()
const id = route.params.id //
const id = Number(route.params.id) //
const loading = ref(true) //
const product = ref<ProductVO>({} as ProductVO) //
const activeTab = ref('info') // info

View File

@ -165,7 +165,7 @@
<el-descriptions-item v-if="formData.logisticsId" label="运单号: ">
{{ formData.logisticsNo }}
</el-descriptions-item>
<el-descriptions-item v-if="formatDate.deliveryTime" label="发货时间: ">
<el-descriptions-item v-if="formData.deliveryTime" label="发货时间: ">
{{ formatDate(formData.deliveryTime) }}
</el-descriptions-item>
<el-descriptions-item v-for="item in 2" :key="item" label-class-name="no-colon" />

View File

@ -198,7 +198,7 @@ const message = useMessage() // 消息弹窗
const loading = ref(false) //
const total = ref(0) //
const list = ref([]) //
const list = ref<SmsLogApi.SmsLogVO[]>([]) //
const queryFormRef = ref() //
const queryParams = reactive({
pageNo: 1,
@ -212,7 +212,7 @@ const queryParams = reactive({
receiveTime: []
})
const exportLoading = ref(false) //
const channelList = ref([]) //
const channelList = ref<SmsChannelApi.SmsChannelVO[]>([]) //
/** 查询列表 */
const getList = async () => {