pull/348/MERGE
xingyu4j 2026-05-23 18:39:12 +08:00
commit fb2595ef90
462 changed files with 12563 additions and 7574 deletions

View File

@ -6,7 +6,7 @@ runs:
using: 'composite' using: 'composite'
steps: steps:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v6
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6

View File

@ -33,7 +33,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v5 uses: pnpm/action-setup@v6
with: with:
run_install: false run_install: false

View File

@ -30,7 +30,7 @@ jobs:
run: pnpm build:play run: pnpm build:play
- name: Sync Playground files - name: Sync Playground files
uses: SamKirkland/FTP-Deploy-Action@v4.3.6 uses: SamKirkland/FTP-Deploy-Action@v4.4.0
with: with:
server: ${{ secrets.PRO_FTP_HOST }} server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }} username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }}
@ -54,7 +54,7 @@ jobs:
run: pnpm build:docs run: pnpm build:docs
- name: Sync Docs files - name: Sync Docs files
uses: SamKirkland/FTP-Deploy-Action@v4.3.6 uses: SamKirkland/FTP-Deploy-Action@v4.4.0
with: with:
server: ${{ secrets.PRO_FTP_HOST }} server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEBSITE_FTP_ACCOUNT }} username: ${{ secrets.WEBSITE_FTP_ACCOUNT }}
@ -85,7 +85,7 @@ jobs:
run: pnpm run build:antd run: pnpm run build:antd
- name: Sync files - name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.6 uses: SamKirkland/FTP-Deploy-Action@v4.4.0
with: with:
server: ${{ secrets.PRO_FTP_HOST }} server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }} username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }}
@ -116,7 +116,7 @@ jobs:
run: pnpm run build:ele run: pnpm run build:ele
- name: Sync files - name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.6 uses: SamKirkland/FTP-Deploy-Action@v4.4.0
with: with:
server: ${{ secrets.PRO_FTP_HOST }} server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }} username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }}
@ -147,7 +147,7 @@ jobs:
run: pnpm run build:naive run: pnpm run build:naive
- name: Sync files - name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.6 uses: SamKirkland/FTP-Deploy-Action@v4.4.0
with: with:
server: ${{ secrets.PRO_FTP_HOST }} server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }} username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }}

View File

@ -18,7 +18,7 @@ jobs:
steps: steps:
- name: remove enhancement pending - name: remove enhancement pending
if: github.event.label.name == 'enhancement' if: github.event.label.name == 'enhancement'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper-backup@d65454423c6fbbd20026b9b499d403f79422ac69
with: with:
actions: 'remove-labels' actions: 'remove-labels'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
@ -27,7 +27,7 @@ jobs:
- name: remove bug pending - name: remove bug pending
if: github.event.label.name == 'bug' if: github.event.label.name == 'bug'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper-backup@d65454423c6fbbd20026b9b499d403f79422ac69
with: with:
actions: 'remove-labels' actions: 'remove-labels'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
@ -36,7 +36,7 @@ jobs:
- name: needs reproduction - name: needs reproduction
if: github.event.label.name == 'needs reproduction' if: github.event.label.name == 'needs reproduction'
uses: actions-cool/issues-helper@v3 uses: actions-cool/issues-helper-backup@d65454423c6fbbd20026b9b499d403f79422ac69
with: with:
actions: 'create-comment, remove-labels' actions: 'create-comment, remove-labels'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -3,60 +3,44 @@ name: Create Release Tag
on: on:
push: push:
tags: tags:
- 'v*.*.*' # Push events to matching v*, i.e. v1.0, v20.15.10 - 'v*.*.*'
workflow_dispatch:
env: inputs:
HUSKY: '0' tag:
description: 'Tag to create (e.g. v1.2.3)'
required: true
type: string
permissions: permissions:
pull-requests: write
contents: write contents: write
jobs: jobs:
build: release:
name: Create Release name: Create Release
if: github.repository == 'vbenjs/vue-vben-admin' if: github.repository == 'vbenjs/vue-vben-admin'
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
# - name: Checkout code - name: Extract version
# uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - name: Install pnpm
# uses: pnpm/action-setup@v4
# - name: Use Node.js ${{ matrix.node-version }}
# uses: actions/setup-node@v4
# with:
# node-version: ${{ matrix.node-version }}
# cache: "pnpm"
# - name: Install dependencies
# run: pnpm install --frozen-lockfile
# - name: Test and Build
# run: |
# pnpm run test
# pnpm run build
- name: version
id: version id: version
run: | run: |
tag=${GITHUB_REF/refs\/tags\//} if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
version=${tag#v} raw_tag="${{ inputs.tag }}"
major=${version%%.*} else
echo "tag=${tag}" >> $GITHUB_OUTPUT raw_tag="${GITHUB_REF_NAME}"
echo "version=${version}" >> $GITHUB_OUTPUT fi
echo "major=${major}" >> $GITHUB_OUTPUT # Normalize: ensure v prefix
tag="${raw_tag}"
[[ "${tag:0:1}" != "v" ]] && tag="v${tag}"
version="${tag#v}"
major="${version%%.*}"
echo "tag=${tag}" >> "${GITHUB_OUTPUT}"
echo "version=${version}" >> "${GITHUB_OUTPUT}"
echo "major=${major}" >> "${GITHUB_OUTPUT}"
- uses: release-drafter/release-drafter@v7 - uses: release-drafter/release-drafter@v7
with: with:
@ -64,17 +48,3 @@ jobs:
publish: true publish: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: force update major tag
# run: |
# git tag v${{ steps.version.outputs.major }} ${{ steps.version.outputs.tag }} -f
# git push origin refs/tags/v${{ steps.version.outputs.major }} -f
# - name: Create Release for Tag
# id: release_tag
# uses: ncipollo/release-action@v1
# with:
# token: ${{ secrets.GITHUB_TOKEN }}
# generateReleaseNotes: "true"
# body: |
# > Please refer to [CHANGELOG.md](https://github.com/vbenjs/vue-vben-admin/blob/main/CHANGELOG.md) for details.

3
.gitignore vendored
View File

@ -23,6 +23,7 @@ package-lock.json
.VSCodeCounter .VSCodeCounter
**/backend-mock/data **/backend-mock/data
.omx .omx
.pnpm-store
# local env files # local env files
.env.local .env.local
.env.*.local .env.*.local
@ -57,3 +58,5 @@ vite.config.ts.*
.claude .claude
.codex .codex
skills-lock.json skills-lock.json
.atomcode
datalog

12
.npmrc
View File

@ -1,13 +1 @@
registry=https://registry.npmmirror.com registry=https://registry.npmmirror.com
public-hoist-pattern[]=lefthook
public-hoist-pattern[]=eslint
public-hoist-pattern[]=oxfmt
public-hoist-pattern[]=oxlint
public-hoist-pattern[]=stylelint
public-hoist-pattern[]=*postcss*
public-hoist-pattern[]=@commitlint/*
public-hoist-pattern[]=czg
strict-peer-dependencies=false
auto-install-peers=true
dedupe-peer-dependents=true

View File

@ -38,7 +38,7 @@
// lint && format // lint && format
"oxc.enable": true, "oxc.enable": true,
"oxc.typeAware": true, "oxc.typeAware": false,
"oxc.configPath": "oxlint.config.ts", "oxc.configPath": "oxlint.config.ts",
"oxc.fmt.configPath": "oxfmt.config.ts", "oxc.fmt.configPath": "oxfmt.config.ts",
"eslint.useFlatConfig": true, "eslint.useFlatConfig": true,

View File

@ -136,8 +136,8 @@ const PreviewGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup), import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup),
); );
const withDefaultPlaceholder = <T extends Component>( const withDefaultPlaceholder = (
component: T, component: Component,
type: 'input' | 'select', type: 'input' | 'select',
componentProps: Recordable<any> = {}, componentProps: Recordable<any> = {},
) => { ) => {
@ -711,7 +711,9 @@ async function initComponentAdapter() {
modelValueProp: 'value', modelValueProp: 'value',
}), }),
Input: withDefaultPlaceholder(Input, 'input'), Input: withDefaultPlaceholder(Input, 'input'),
InputNumber: withDefaultPlaceholder(InputNumber, 'input'), InputNumber: withDefaultPlaceholder(InputNumber, 'input', {
style: { width: '100%' },
}),
InputPassword: withDefaultPlaceholder(InputPassword, 'input'), InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'), Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮 // 自定义主要按钮

View File

@ -150,14 +150,14 @@ export function getDataSinkPage(params: PageParam) {
/** 查询数据流转目的详情 */ /** 查询数据流转目的详情 */
export function getDataSink(id: number) { export function getDataSink(id: number) {
return requestClient.get<DataSinkApi.DataSink>( return requestClient.get<DataSinkApi.DataSink>(`/iot/data-sink/get?id=${id}`);
`/iot/data-sink/get?id=${id}`,
);
} }
/** 查询数据流转目的(精简)列表 */ /** 查询数据流转目的(精简)列表 */
export function getDataSinkSimpleList() { export function getDataSinkSimpleList() {
return requestClient.get<DataSinkApi.DataSink[]>('/iot/data-sink/simple-list'); return requestClient.get<DataSinkApi.DataSink[]>(
'/iot/data-sink/simple-list',
);
} }
/** 新增数据流转目的 */ /** 新增数据流转目的 */

View File

@ -195,7 +195,9 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
trigger: 'blur', trigger: 'blur',
}, },
], ],
accessMode: [{ required: true, message: '请选择读写类型', trigger: 'change' }], accessMode: [
{ required: true, message: '请选择读写类型', trigger: 'change' },
],
callType: [{ required: true, message: '请选择调用方式', trigger: 'change' }], callType: [{ required: true, message: '请选择调用方式', trigger: 'change' }],
eventType: [{ required: true, message: '请选择事件类型', trigger: 'change' }], eventType: [{ required: true, message: '请选择事件类型', trigger: 'change' }],
}; };

View File

@ -20,5 +20,8 @@ export namespace MesCalCalendarApi {
/** 查询排班日历列表 */ /** 查询排班日历列表 */
export function getCalendarList(params: any) { export function getCalendarList(params: any) {
return requestClient.get<MesCalCalendarApi.CalendarDay[]>('/mes/cal/calendar/list', { params }); return requestClient.get<MesCalCalendarApi.CalendarDay[]>(
'/mes/cal/calendar/list',
{ params },
);
} }

View File

@ -19,12 +19,18 @@ export namespace MesCalHolidayApi {
/** 查询假期设置列表 */ /** 查询假期设置列表 */
export function getHolidayList(params?: MesCalHolidayApi.HolidayQuery) { export function getHolidayList(params?: MesCalHolidayApi.HolidayQuery) {
return requestClient.get<MesCalHolidayApi.Holiday[]>('/mes/cal/holiday/list', { params }); return requestClient.get<MesCalHolidayApi.Holiday[]>(
'/mes/cal/holiday/list',
{ params },
);
} }
/** 根据日期查询假期设置 */ /** 根据日期查询假期设置 */
export function getHolidayByDay(day: string) { export function getHolidayByDay(day: string) {
return requestClient.get<MesCalHolidayApi.Holiday>('/mes/cal/holiday/get-by-day', { params: { day } }); return requestClient.get<MesCalHolidayApi.Holiday>(
'/mes/cal/holiday/get-by-day',
{ params: { day } },
);
} }
/** 保存假期设置 */ /** 保存假期设置 */

View File

@ -22,7 +22,10 @@ export namespace MesCalPlanApi {
/** 查询排班计划分页 */ /** 查询排班计划分页 */
export function getPlanPage(params: PageParam) { export function getPlanPage(params: PageParam) {
return requestClient.get<PageResult<MesCalPlanApi.Plan>>('/mes/cal/plan/page', { params }); return requestClient.get<PageResult<MesCalPlanApi.Plan>>(
'/mes/cal/plan/page',
{ params },
);
} }
/** 查询排班计划详情 */ /** 查询排班计划详情 */

View File

@ -15,7 +15,9 @@ export namespace MesCalPlanShiftApi {
/** 查询指定排班计划的班次列表 */ /** 查询指定排班计划的班次列表 */
export function getPlanShiftListByPlan(planId: number) { export function getPlanShiftListByPlan(planId: number) {
return requestClient.get<MesCalPlanShiftApi.PlanShift[]>(`/mes/cal/plan-shift/list-by-plan?planId=${planId}`); return requestClient.get<MesCalPlanShiftApi.PlanShift[]>(
`/mes/cal/plan-shift/list-by-plan?planId=${planId}`,
);
} }
/** 新增计划班次 */ /** 新增计划班次 */

View File

@ -14,7 +14,9 @@ export namespace MesCalPlanTeamApi {
/** 查询指定排班计划的班组列表 */ /** 查询指定排班计划的班组列表 */
export function getPlanTeamListByPlan(planId: number) { export function getPlanTeamListByPlan(planId: number) {
return requestClient.get<MesCalPlanTeamApi.PlanTeam[]>(`/mes/cal/plan-team/list-by-plan?planId=${planId}`); return requestClient.get<MesCalPlanTeamApi.PlanTeam[]>(
`/mes/cal/plan-team/list-by-plan?planId=${planId}`,
);
} }
/** 新增计划班组关联 */ /** 新增计划班组关联 */

View File

@ -16,7 +16,10 @@ export namespace MesCalTeamApi {
/** 查询班组分页 */ /** 查询班组分页 */
export function getTeamPage(params: PageParam) { export function getTeamPage(params: PageParam) {
return requestClient.get<PageResult<MesCalTeamApi.Team>>('/mes/cal/team/page', { params }); return requestClient.get<PageResult<MesCalTeamApi.Team>>(
'/mes/cal/team/page',
{ params },
);
} }
/** 查询班组列表 */ /** 查询班组列表 */

View File

@ -26,17 +26,26 @@ export function deleteTeamMember(id: number) {
/** 查询班组成员分页 */ /** 查询班组成员分页 */
export function getTeamMemberPage(params: PageParam) { export function getTeamMemberPage(params: PageParam) {
return requestClient.get<PageResult<MesCalTeamMemberApi.TeamMember>>('/mes/cal/team-member/page', { params }); return requestClient.get<PageResult<MesCalTeamMemberApi.TeamMember>>(
'/mes/cal/team-member/page',
{ params },
);
} }
/** 查询指定班组的成员列表 */ /** 查询指定班组的成员列表 */
export function getTeamMemberListByTeam(teamId: number) { export function getTeamMemberListByTeam(teamId: number) {
return requestClient.get<MesCalTeamMemberApi.TeamMember[]>('/mes/cal/team-member/list-by-team', { params: { teamId } }); return requestClient.get<MesCalTeamMemberApi.TeamMember[]>(
'/mes/cal/team-member/list-by-team',
{ params: { teamId } },
);
} }
/** 查询多个班组的成员列表 */ /** 查询多个班组的成员列表 */
export function getTeamMemberListByTeamIds(teamIds: number[]) { export function getTeamMemberListByTeamIds(teamIds: number[]) {
return requestClient.get<MesCalTeamMemberApi.TeamMember[]>('/mes/cal/team-member/list-by-team', { return requestClient.get<MesCalTeamMemberApi.TeamMember[]>(
params: { teamIds: teamIds.join(',') }, '/mes/cal/team-member/list-by-team',
}); {
params: { teamIds: teamIds.join(',') },
},
);
} }

View File

@ -21,12 +21,17 @@ export namespace MesDvCheckPlanApi {
/** 查询点检保养方案分页 */ /** 查询点检保养方案分页 */
export function getCheckPlanPage(params: PageParam) { export function getCheckPlanPage(params: PageParam) {
return requestClient.get<PageResult<MesDvCheckPlanApi.CheckPlan>>('/mes/dv/check-plan/page', { params }); return requestClient.get<PageResult<MesDvCheckPlanApi.CheckPlan>>(
'/mes/dv/check-plan/page',
{ params },
);
} }
/** 查询点检保养方案详情 */ /** 查询点检保养方案详情 */
export function getCheckPlan(id: number) { export function getCheckPlan(id: number) {
return requestClient.get<MesDvCheckPlanApi.CheckPlan>(`/mes/dv/check-plan/get?id=${id}`); return requestClient.get<MesDvCheckPlanApi.CheckPlan>(
`/mes/dv/check-plan/get?id=${id}`,
);
} }
/** 新增点检保养方案 */ /** 新增点检保养方案 */

View File

@ -16,11 +16,15 @@ export namespace MesDvCheckPlanMachineryApi {
/** 查询指定方案的设备列表 */ /** 查询指定方案的设备列表 */
export function getCheckPlanMachineryListByPlan(planId: number) { export function getCheckPlanMachineryListByPlan(planId: number) {
return requestClient.get<MesDvCheckPlanMachineryApi.CheckPlanMachinery[]>(`/mes/dv/check-plan-machinery/list-by-plan?planId=${planId}`); return requestClient.get<MesDvCheckPlanMachineryApi.CheckPlanMachinery[]>(
`/mes/dv/check-plan-machinery/list-by-plan?planId=${planId}`,
);
} }
/** 新增方案设备关联 */ /** 新增方案设备关联 */
export function createCheckPlanMachinery(data: MesDvCheckPlanMachineryApi.CheckPlanMachinery) { export function createCheckPlanMachinery(
data: MesDvCheckPlanMachineryApi.CheckPlanMachinery,
) {
return requestClient.post('/mes/dv/check-plan-machinery/create', data); return requestClient.post('/mes/dv/check-plan-machinery/create', data);
} }

View File

@ -17,11 +17,15 @@ export namespace MesDvCheckPlanSubjectApi {
/** 查询指定方案的项目列表 */ /** 查询指定方案的项目列表 */
export function getCheckPlanSubjectListByPlan(planId: number) { export function getCheckPlanSubjectListByPlan(planId: number) {
return requestClient.get<MesDvCheckPlanSubjectApi.CheckPlanSubject[]>(`/mes/dv/check-plan-subject/list-by-plan?planId=${planId}`); return requestClient.get<MesDvCheckPlanSubjectApi.CheckPlanSubject[]>(
`/mes/dv/check-plan-subject/list-by-plan?planId=${planId}`,
);
} }
/** 新增方案项目关联 */ /** 新增方案项目关联 */
export function createCheckPlanSubject(data: MesDvCheckPlanSubjectApi.CheckPlanSubject) { export function createCheckPlanSubject(
data: MesDvCheckPlanSubjectApi.CheckPlanSubject,
) {
return requestClient.post('/mes/dv/check-plan-subject/create', data); return requestClient.post('/mes/dv/check-plan-subject/create', data);
} }

View File

@ -24,12 +24,17 @@ export namespace MesDvCheckRecordApi {
/** 查询设备点检记录分页 */ /** 查询设备点检记录分页 */
export function getCheckRecordPage(params: PageParam) { export function getCheckRecordPage(params: PageParam) {
return requestClient.get<PageResult<MesDvCheckRecordApi.CheckRecord>>('/mes/dv/check-record/page', { params }); return requestClient.get<PageResult<MesDvCheckRecordApi.CheckRecord>>(
'/mes/dv/check-record/page',
{ params },
);
} }
/** 查询设备点检记录详情 */ /** 查询设备点检记录详情 */
export function getCheckRecord(id: number) { export function getCheckRecord(id: number) {
return requestClient.get<MesDvCheckRecordApi.CheckRecord>(`/mes/dv/check-record/get?id=${id}`); return requestClient.get<MesDvCheckRecordApi.CheckRecord>(
`/mes/dv/check-record/get?id=${id}`,
);
} }
/** 新增设备点检记录 */ /** 新增设备点检记录 */
@ -54,5 +59,7 @@ export function deleteCheckRecord(id: number) {
/** 导出设备点检记录 */ /** 导出设备点检记录 */
export function exportCheckRecord(params: any) { export function exportCheckRecord(params: any) {
return requestClient.download('/mes/dv/check-record/export-excel', { params }); return requestClient.download('/mes/dv/check-record/export-excel', {
params,
});
} }

View File

@ -20,21 +20,30 @@ export namespace MesDvCheckRecordLineApi {
/** 查询设备点检记录明细分页 */ /** 查询设备点检记录明细分页 */
export function getCheckRecordLinePage(params: PageParam) { export function getCheckRecordLinePage(params: PageParam) {
return requestClient.get<PageResult<MesDvCheckRecordLineApi.CheckRecordLine>>('/mes/dv/check-record-line/page', { params }); return requestClient.get<PageResult<MesDvCheckRecordLineApi.CheckRecordLine>>(
'/mes/dv/check-record-line/page',
{ params },
);
} }
/** 查询设备点检记录明细详情 */ /** 查询设备点检记录明细详情 */
export function getCheckRecordLine(id: number) { export function getCheckRecordLine(id: number) {
return requestClient.get<MesDvCheckRecordLineApi.CheckRecordLine>(`/mes/dv/check-record-line/get?id=${id}`); return requestClient.get<MesDvCheckRecordLineApi.CheckRecordLine>(
`/mes/dv/check-record-line/get?id=${id}`,
);
} }
/** 新增设备点检记录明细 */ /** 新增设备点检记录明细 */
export function createCheckRecordLine(data: MesDvCheckRecordLineApi.CheckRecordLine) { export function createCheckRecordLine(
data: MesDvCheckRecordLineApi.CheckRecordLine,
) {
return requestClient.post('/mes/dv/check-record-line/create', data); return requestClient.post('/mes/dv/check-record-line/create', data);
} }
/** 修改设备点检记录明细 */ /** 修改设备点检记录明细 */
export function updateCheckRecordLine(data: MesDvCheckRecordLineApi.CheckRecordLine) { export function updateCheckRecordLine(
data: MesDvCheckRecordLineApi.CheckRecordLine,
) {
return requestClient.put('/mes/dv/check-record-line/update', data); return requestClient.put('/mes/dv/check-record-line/update', data);
} }

View File

@ -31,17 +31,24 @@ export namespace MesDvMachineryApi {
/** 查询设备分页 */ /** 查询设备分页 */
export function getMachineryPage(params: PageParam) { export function getMachineryPage(params: PageParam) {
return requestClient.get<PageResult<MesDvMachineryApi.Machinery>>('/mes/dv/machinery/page', { params }); return requestClient.get<PageResult<MesDvMachineryApi.Machinery>>(
'/mes/dv/machinery/page',
{ params },
);
} }
/** 查询设备精简列表 */ /** 查询设备精简列表 */
export function getMachinerySimpleList() { export function getMachinerySimpleList() {
return requestClient.get<MesDvMachineryApi.Machinery[]>('/mes/dv/machinery/simple-list'); return requestClient.get<MesDvMachineryApi.Machinery[]>(
'/mes/dv/machinery/simple-list',
);
} }
/** 查询设备详情 */ /** 查询设备详情 */
export function getMachinery(id: number) { export function getMachinery(id: number) {
return requestClient.get<MesDvMachineryApi.Machinery>(`/mes/dv/machinery/get?id=${id}`); return requestClient.get<MesDvMachineryApi.Machinery>(
`/mes/dv/machinery/get?id=${id}`,
);
} }
/** 新增设备 */ /** 新增设备 */
@ -72,7 +79,7 @@ export function importMachineryTemplate() {
/** 导入设备 */ /** 导入设备 */
export function importMachinery(file: File, updateSupport: boolean) { export function importMachinery(file: File, updateSupport: boolean) {
return requestClient.upload<MesDvMachineryApi.MachineryImportRespVO>( return requestClient.upload<MesDvMachineryApi.MachineryImportRespVO>(
'/mes/dv/machinery/import?updateSupport=' + updateSupport, `/mes/dv/machinery/import?updateSupport=${updateSupport}`,
{ file }, { file },
); );
} }

View File

@ -17,17 +17,24 @@ export namespace MesDvMachineryTypeApi {
/** 查询设备类型列表 */ /** 查询设备类型列表 */
export function getMachineryTypeList(params?: any) { export function getMachineryTypeList(params?: any) {
return requestClient.get<MesDvMachineryTypeApi.MachineryType[]>('/mes/dv/machinery-type/list', { params }); return requestClient.get<MesDvMachineryTypeApi.MachineryType[]>(
'/mes/dv/machinery-type/list',
{ params },
);
} }
/** 查询设备类型精简列表 */ /** 查询设备类型精简列表 */
export function getMachineryTypeSimpleList() { export function getMachineryTypeSimpleList() {
return requestClient.get<MesDvMachineryTypeApi.MachineryType[]>('/mes/dv/machinery-type/simple-list'); return requestClient.get<MesDvMachineryTypeApi.MachineryType[]>(
'/mes/dv/machinery-type/simple-list',
);
} }
/** 查询设备类型详情 */ /** 查询设备类型详情 */
export function getMachineryType(id: number) { export function getMachineryType(id: number) {
return requestClient.get<MesDvMachineryTypeApi.MachineryType>(`/mes/dv/machinery-type/get?id=${id}`); return requestClient.get<MesDvMachineryTypeApi.MachineryType>(
`/mes/dv/machinery-type/get?id=${id}`,
);
} }
/** 新增设备类型 */ /** 新增设备类型 */

View File

@ -24,12 +24,17 @@ export namespace MesDvMaintenRecordApi {
/** 查询设备保养记录分页 */ /** 查询设备保养记录分页 */
export function getMaintenRecordPage(params: PageParam) { export function getMaintenRecordPage(params: PageParam) {
return requestClient.get<PageResult<MesDvMaintenRecordApi.MaintenRecord>>('/mes/dv/mainten-record/page', { params }); return requestClient.get<PageResult<MesDvMaintenRecordApi.MaintenRecord>>(
'/mes/dv/mainten-record/page',
{ params },
);
} }
/** 查询设备保养记录详情 */ /** 查询设备保养记录详情 */
export function getMaintenRecord(id: number) { export function getMaintenRecord(id: number) {
return requestClient.get<MesDvMaintenRecordApi.MaintenRecord>(`/mes/dv/mainten-record/get?id=${id}`); return requestClient.get<MesDvMaintenRecordApi.MaintenRecord>(
`/mes/dv/mainten-record/get?id=${id}`,
);
} }
/** 新增设备保养记录 */ /** 新增设备保养记录 */
@ -54,5 +59,7 @@ export function deleteMaintenRecord(id: number) {
/** 导出设备保养记录 */ /** 导出设备保养记录 */
export function exportMaintenRecord(params: any) { export function exportMaintenRecord(params: any) {
return requestClient.download('/mes/dv/mainten-record/export-excel', { params }); return requestClient.download('/mes/dv/mainten-record/export-excel', {
params,
});
} }

View File

@ -19,21 +19,29 @@ export namespace MesDvMaintenRecordLineApi {
/** 查询设备保养记录明细分页 */ /** 查询设备保养记录明细分页 */
export function getMaintenRecordLinePage(params: PageParam) { export function getMaintenRecordLinePage(params: PageParam) {
return requestClient.get<PageResult<MesDvMaintenRecordLineApi.MaintenRecordLine>>('/mes/dv/mainten-record-line/page', { params }); return requestClient.get<
PageResult<MesDvMaintenRecordLineApi.MaintenRecordLine>
>('/mes/dv/mainten-record-line/page', { params });
} }
/** 查询设备保养记录明细详情 */ /** 查询设备保养记录明细详情 */
export function getMaintenRecordLine(id: number) { export function getMaintenRecordLine(id: number) {
return requestClient.get<MesDvMaintenRecordLineApi.MaintenRecordLine>(`/mes/dv/mainten-record-line/get?id=${id}`); return requestClient.get<MesDvMaintenRecordLineApi.MaintenRecordLine>(
`/mes/dv/mainten-record-line/get?id=${id}`,
);
} }
/** 新增设备保养记录明细 */ /** 新增设备保养记录明细 */
export function createMaintenRecordLine(data: MesDvMaintenRecordLineApi.MaintenRecordLine) { export function createMaintenRecordLine(
data: MesDvMaintenRecordLineApi.MaintenRecordLine,
) {
return requestClient.post('/mes/dv/mainten-record-line/create', data); return requestClient.post('/mes/dv/mainten-record-line/create', data);
} }
/** 修改设备保养记录明细 */ /** 修改设备保养记录明细 */
export function updateMaintenRecordLine(data: MesDvMaintenRecordLineApi.MaintenRecordLine) { export function updateMaintenRecordLine(
data: MesDvMaintenRecordLineApi.MaintenRecordLine,
) {
return requestClient.put('/mes/dv/mainten-record-line/update', data); return requestClient.put('/mes/dv/mainten-record-line/update', data);
} }

View File

@ -32,12 +32,17 @@ export namespace MesDvRepairApi {
/** 查询维修工单分页 */ /** 查询维修工单分页 */
export function getRepairPage(params: PageParam) { export function getRepairPage(params: PageParam) {
return requestClient.get<PageResult<MesDvRepairApi.Repair>>('/mes/dv/repair/page', { params }); return requestClient.get<PageResult<MesDvRepairApi.Repair>>(
'/mes/dv/repair/page',
{ params },
);
} }
/** 查询维修工单详情 */ /** 查询维修工单详情 */
export function getRepair(id: number) { export function getRepair(id: number) {
return requestClient.get<MesDvRepairApi.Repair>(`/mes/dv/repair/get?id=${id}`); return requestClient.get<MesDvRepairApi.Repair>(
`/mes/dv/repair/get?id=${id}`,
);
} }
/** 新增维修工单 */ /** 新增维修工单 */

View File

@ -20,12 +20,17 @@ export namespace MesDvRepairLineApi {
/** 查询维修工单行分页 */ /** 查询维修工单行分页 */
export function getRepairLinePage(params: PageParam) { export function getRepairLinePage(params: PageParam) {
return requestClient.get<PageResult<MesDvRepairLineApi.RepairLine>>('/mes/dv/repair-line/page', { params }); return requestClient.get<PageResult<MesDvRepairLineApi.RepairLine>>(
'/mes/dv/repair-line/page',
{ params },
);
} }
/** 查询维修工单行详情 */ /** 查询维修工单行详情 */
export function getRepairLine(id: number) { export function getRepairLine(id: number) {
return requestClient.get<MesDvRepairLineApi.RepairLine>(`/mes/dv/repair-line/get?id=${id}`); return requestClient.get<MesDvRepairLineApi.RepairLine>(
`/mes/dv/repair-line/get?id=${id}`,
);
} }
/** 新增维修工单行 */ /** 新增维修工单行 */

View File

@ -19,17 +19,24 @@ export namespace MesDvSubjectApi {
/** 查询点检保养项目分页 */ /** 查询点检保养项目分页 */
export function getSubjectPage(params: PageParam) { export function getSubjectPage(params: PageParam) {
return requestClient.get<PageResult<MesDvSubjectApi.Subject>>('/mes/dv/subject/page', { params }); return requestClient.get<PageResult<MesDvSubjectApi.Subject>>(
'/mes/dv/subject/page',
{ params },
);
} }
/** 查询点检保养项目精简列表 */ /** 查询点检保养项目精简列表 */
export function getSubjectSimpleList() { export function getSubjectSimpleList() {
return requestClient.get<MesDvSubjectApi.Subject[]>('/mes/dv/subject/simple-list'); return requestClient.get<MesDvSubjectApi.Subject[]>(
'/mes/dv/subject/simple-list',
);
} }
/** 查询点检保养项目详情 */ /** 查询点检保养项目详情 */
export function getSubject(id: number) { export function getSubject(id: number) {
return requestClient.get<MesDvSubjectApi.Subject>(`/mes/dv/subject/get?id=${id}`); return requestClient.get<MesDvSubjectApi.Subject>(
`/mes/dv/subject/get?id=${id}`,
);
} }
/** 新增点检保养项目 */ /** 新增点检保养项目 */

View File

@ -34,16 +34,12 @@ export function getAutoCodePartListByRuleId(ruleId: number) {
} }
/** 新增编码规则分段 */ /** 新增编码规则分段 */
export function createAutoCodePart( export function createAutoCodePart(data: MesMdAutoCodePartApi.AutoCodePart) {
data: MesMdAutoCodePartApi.AutoCodePart,
) {
return requestClient.post('/mes/md/auto-code-part/create', data); return requestClient.post('/mes/md/auto-code-part/create', data);
} }
/** 修改编码规则分段 */ /** 修改编码规则分段 */
export function updateAutoCodePart( export function updateAutoCodePart(data: MesMdAutoCodePartApi.AutoCodePart) {
data: MesMdAutoCodePartApi.AutoCodePart,
) {
return requestClient.put('/mes/md/auto-code-part/update', data); return requestClient.put('/mes/md/auto-code-part/update', data);
} }

View File

@ -21,9 +21,10 @@ export namespace MesMdAutoCodeRuleApi {
/** 查询编码规则分页 */ /** 查询编码规则分页 */
export function getAutoCodeRulePage(params: PageParam) { export function getAutoCodeRulePage(params: PageParam) {
return requestClient.get< return requestClient.get<PageResult<MesMdAutoCodeRuleApi.AutoCodeRule>>(
PageResult<MesMdAutoCodeRuleApi.AutoCodeRule> '/mes/md/auto-code-rule/page',
>('/mes/md/auto-code-rule/page', { params }); { params },
);
} }
/** 查询编码规则详情 */ /** 查询编码规则详情 */
@ -34,16 +35,12 @@ export function getAutoCodeRule(id: number) {
} }
/** 新增编码规则 */ /** 新增编码规则 */
export function createAutoCodeRule( export function createAutoCodeRule(data: MesMdAutoCodeRuleApi.AutoCodeRule) {
data: MesMdAutoCodeRuleApi.AutoCodeRule,
) {
return requestClient.post('/mes/md/auto-code-rule/create', data); return requestClient.post('/mes/md/auto-code-rule/create', data);
} }
/** 修改编码规则 */ /** 修改编码规则 */
export function updateAutoCodeRule( export function updateAutoCodeRule(data: MesMdAutoCodeRuleApi.AutoCodeRule) {
data: MesMdAutoCodeRuleApi.AutoCodeRule,
) {
return requestClient.put('/mes/md/auto-code-rule/update', data); return requestClient.put('/mes/md/auto-code-rule/update', data);
} }

View File

@ -30,8 +30,6 @@ export function getBatchConfigByItemId(itemId: number) {
} }
/** 保存批次属性配置 */ /** 保存批次属性配置 */
export function saveBatchConfig( export function saveBatchConfig(data: MesMdItemBatchConfigApi.BatchConfig) {
data: MesMdItemBatchConfigApi.BatchConfig,
) {
return requestClient.post('/mes/md/item-batch-config/save', data); return requestClient.post('/mes/md/item-batch-config/save', data);
} }

View File

@ -56,5 +56,7 @@ export function deleteUnitMeasure(id: number) {
/** 导出计量单位 */ /** 导出计量单位 */
export function exportUnitMeasure(params: PageParam) { export function exportUnitMeasure(params: PageParam) {
return requestClient.download('/mes/md/unit-measure/export-excel', { params }); return requestClient.download('/mes/md/unit-measure/export-excel', {
params,
});
} }

View File

@ -25,7 +25,9 @@ export namespace MesTmToolApi {
/** 查询工具台账分页 */ /** 查询工具台账分页 */
export function getToolPage(params: PageParam) { export function getToolPage(params: PageParam) {
return requestClient.get<PageResult<MesTmToolApi.Tool>>('/mes/tm/tool/page', { params }); return requestClient.get<PageResult<MesTmToolApi.Tool>>('/mes/tm/tool/page', {
params,
});
} }
/** 查询工具精简列表 */ /** 查询工具精简列表 */

View File

@ -18,17 +18,24 @@ export namespace MesTmToolTypeApi {
/** 查询工具类型分页 */ /** 查询工具类型分页 */
export function getToolTypePage(params: PageParam) { export function getToolTypePage(params: PageParam) {
return requestClient.get<PageResult<MesTmToolTypeApi.ToolType>>('/mes/tm/tool-type/page', { params }); return requestClient.get<PageResult<MesTmToolTypeApi.ToolType>>(
'/mes/tm/tool-type/page',
{ params },
);
} }
/** 查询工具类型精简列表 */ /** 查询工具类型精简列表 */
export function getToolTypeSimpleList() { export function getToolTypeSimpleList() {
return requestClient.get<MesTmToolTypeApi.ToolType[]>('/mes/tm/tool-type/simple-list'); return requestClient.get<MesTmToolTypeApi.ToolType[]>(
'/mes/tm/tool-type/simple-list',
);
} }
/** 查询工具类型详情 */ /** 查询工具类型详情 */
export function getToolType(id: number) { export function getToolType(id: number) {
return requestClient.get<MesTmToolTypeApi.ToolType>(`/mes/tm/tool-type/get?id=${id}`); return requestClient.get<MesTmToolTypeApi.ToolType>(
`/mes/tm/tool-type/get?id=${id}`,
);
} }
/** 新增工具类型 */ /** 新增工具类型 */

View File

@ -23,7 +23,7 @@ import {
TenantDropdown, TenantDropdown,
UserDropdown, UserDropdown,
} from '@vben/layouts'; } from '@vben/layouts';
import { preferences } from '@vben/preferences'; import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores'; import { useAccessStore, useUserStore } from '@vben/stores';
import { formatDateTime, openWindow } from '@vben/utils'; import { formatDateTime, openWindow } from '@vben/utils';
@ -55,6 +55,7 @@ const showDot = computed(() => unreadCount.value > 0);
const [HelpModal, helpModalApi] = useVbenModal({ const [HelpModal, helpModalApi] = useVbenModal({
connectedComponent: Help, connectedComponent: Help,
}); });
const { isDark } = usePreferences();
const menus = computed(() => [ const menus = computed(() => [
{ {
@ -201,14 +202,57 @@ onMounted(() => {
); );
}); });
const handleClick = (item: NotificationItem) => {
//
if (item.link) {
navigateTo(item.link, item.query, item.state);
}
};
function navigateTo(
link: string,
query?: Record<string, any>,
state?: Record<string, any>,
) {
if (link.startsWith('http://') || link.startsWith('https://')) {
//
window.open(link, '_blank');
} else {
// query state
router.push({
path: link,
query: query || {},
state,
});
}
}
watch( watch(
() => ({ () => ({
enable: preferences.app.watermark, enable: preferences.app.watermark,
content: preferences.app.watermarkContent, content: preferences.app.watermarkContent,
isDark: isDark.value,
}), }),
async ({ enable, content }) => { async ({ enable, content, isDark: isDarkValue }) => {
if (enable) { if (enable) {
const watermarkColor = isDarkValue
? 'rgba(255, 255, 255, 0.12)'
: 'rgba(0, 0, 0, 0.12)';
await updateWatermark({ await updateWatermark({
advancedStyle: {
colorStops: [
{
color: watermarkColor,
offset: 0,
},
{
color: watermarkColor,
offset: 1,
},
],
type: 'linear',
},
content: content:
content || content ||
`${userStore.userInfo?.id} - ${userStore.userInfo?.nickname}`, `${userStore.userInfo?.id} - ${userStore.userInfo?.nickname}`,
@ -233,6 +277,7 @@ watch(
:description="userStore.userInfo?.email" :description="userStore.userInfo?.email"
:tag-text="userStore.userInfo?.username" :tag-text="userStore.userInfo?.username"
@logout="handleLogout" @logout="handleLogout"
@clear-preferences-and-logout="handleLogout"
/> />
</template> </template>
<template #notification> <template #notification>
@ -244,6 +289,7 @@ watch(
@view-all="handleNotificationViewAll" @view-all="handleNotificationViewAll"
@open="handleNotificationOpen" @open="handleNotificationOpen"
@read="handleNotificationRead" @read="handleNotificationRead"
@on-click="handleClick"
/> />
</template> </template>
<template #header-right-1> <template #header-right-1>

View File

@ -36,8 +36,7 @@ const routes: RouteRecordRaw[] = [
title: '固件详情', title: '固件详情',
activePath: '/iot/ota', activePath: '/iot/ota',
}, },
component: () => component: () => import('#/views/iot/ota/firmware/detail/index.vue'),
import('#/views/iot/ota/firmware/detail/index.vue'),
}, },
], ],
}, },

View File

@ -126,8 +126,7 @@ export function useGridColumns(): VxeTableGridOptions<AlertRecordApi.AlertRecord
title: '设备名称', title: '设备名称',
minWidth: 120, minWidth: 120,
formatter: ({ cellValue }) => formatter: ({ cellValue }) =>
deviceList.find((device) => device.id === cellValue)?.deviceName || deviceList.find((device) => device.id === cellValue)?.deviceName || '-',
'-',
}, },
{ {
field: 'deviceMessage', field: 'deviceMessage',

View File

@ -38,10 +38,10 @@ function handleProcess(row: AlertRecordApi.AlertRecord) {
h('div', { class: 'space-y-2' }, [ h('div', { class: 'space-y-2' }, [
h('p', '请输入处理原因:'), h('p', '请输入处理原因:'),
h(Input.TextArea, { h(Input.TextArea, {
'value': processRemark.value, value: processRemark.value,
'onUpdate:value': (val: string) => (processRemark.value = val), 'onUpdate:value': (val: string) => (processRemark.value = val),
'rows': 3, rows: 3,
'placeholder': '请输入处理原因', placeholder: '请输入处理原因',
}), }),
]), ]),
async onOk() { async onOk() {
@ -106,7 +106,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
:overlay-style="{ maxWidth: '600px' }" :overlay-style="{ maxWidth: '600px' }"
> >
<template #content> <template #content>
<pre class="text-xs">{{ stringifyDeviceMessage(row.deviceMessage) }}</pre> <pre class="text-xs">{{
stringifyDeviceMessage(row.deviceMessage)
}}</pre>
</template> </template>
<Button size="small" type="link"> <Button size="small" type="link">
<IconifyIcon icon="ant-design:eye-outlined" class="mr-1" /> <IconifyIcon icon="ant-design:eye-outlined" class="mr-1" />

View File

@ -206,9 +206,7 @@ onMounted(() => {
</div> </div>
</div> </div>
<!-- 按钮组 --> <!-- 按钮组 -->
<div <div class="mt-auto flex gap-2 border-t border-border pt-3">
class="mt-auto flex gap-2 border-t border-border pt-3"
>
<Button <Button
v-if="hasAccessByCodes(['iot:device:update'])" v-if="hasAccessByCodes(['iot:device:update'])"
size="small" size="small"

View File

@ -134,7 +134,9 @@ const [Modal, modalApi] = useVbenModal({
advValues.latitude !== null && advValues.latitude !== null &&
advValues.latitude !== ''; advValues.latitude !== '';
if (hasLongitude !== hasLatitude) { if (hasLongitude !== hasLatitude) {
message.warning(hasLongitude ? '请同时填写设备纬度' : '请同时填写设备经度'); message.warning(
hasLongitude ? '请同时填写设备纬度' : '请同时填写设备经度',
);
return; return;
} }
} }

View File

@ -122,7 +122,6 @@ async function renderChartWhenReady() {
await nextTick(); await nextTick();
initChart(); initChart();
} }
</script> </script>
<template> <template>

View File

@ -17,9 +17,7 @@ export function getProductName(productId?: number): string {
if (!productId) { if (!productId) {
return '-'; return '-';
} }
return ( return productList.find((product) => product.id === productId)?.name || '-';
productList.find((product) => product.id === productId)?.name || '-'
);
} }
/** 固件详情的描述字段 */ /** 固件详情的描述字段 */

View File

@ -72,7 +72,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
pageNo: page.currentPage, pageNo: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
taskId: props.taskId, taskId: props.taskId,
status: activeTab.value === '' ? undefined : Number(activeTab.value), status:
activeTab.value === '' ? undefined : Number(activeTab.value),
}); });
}, },
}, },
@ -97,16 +98,8 @@ watch(
<template> <template>
<Card title="升级设备记录"> <Card title="升级设备记录">
<Tabs <Tabs v-model:active-key="activeTab" @change="handleTabChange" class="mb-4">
v-model:active-key="activeTab" <Tabs.TabPane v-for="tab in statusTabs" :key="tab.key" :tab="tab.label" />
@change="handleTabChange"
class="mb-4"
>
<Tabs.TabPane
v-for="tab in statusTabs"
:key="tab.key"
:tab="tab.label"
/>
</Tabs> </Tabs>
<Grid> <Grid>
<template #actions="{ row }"> <template #actions="{ row }">

View File

@ -76,9 +76,9 @@ async function copyToClipboard(text: string) {
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item <Descriptions.Item
v-if=" v-if="
([DeviceTypeEnum.DEVICE, DeviceTypeEnum.GATEWAY] as number[]).includes( (
product.deviceType!, [DeviceTypeEnum.DEVICE, DeviceTypeEnum.GATEWAY] as number[]
) ).includes(product.deviceType!)
" "
label="联网方式" label="联网方式"
> >

View File

@ -182,9 +182,7 @@ onMounted(() => {
</div> </div>
</div> </div>
<!-- 按钮组 --> <!-- 按钮组 -->
<div <div class="mt-auto flex gap-2 border-t border-border pt-3">
class="mt-auto flex gap-2 border-t border-border pt-3"
>
<Button <Button
v-if="hasAccessByCodes(['iot:product:update'])" v-if="hasAccessByCodes(['iot:product:update'])"
size="small" size="small"

View File

@ -1,7 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onMounted, ref } from 'vue'; import { computed, nextTick, onMounted, ref } from 'vue';
import { IotDeviceMessageMethodEnum, IoTThingModelTypeEnum } from '@vben/constants'; import {
IotDeviceMessageMethodEnum,
IoTThingModelTypeEnum,
} from '@vben/constants';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { Button, message, Select } from 'ant-design-vue'; import { Button, message, Select } from 'ant-design-vue';

View File

@ -55,7 +55,9 @@ onMounted(() => {
<template> <template>
<Form.Item <Form.Item
:name="['config', 'jdbcUrl']" :name="['config', 'jdbcUrl']"
:rules="[{ required: true, message: 'JDBC 连接地址不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: 'JDBC 连接地址不能为空', trigger: 'blur' },
]"
label="JDBC 地址" label="JDBC 地址"
> >
<Input <Input

View File

@ -1,6 +1,5 @@
<!--suppress HttpUrlsUsage --> <!--suppress HttpUrlsUsage -->
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -67,7 +66,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'method']" :name="['config', 'method']"
:rules="[{ required: true, message: '请求方法不能为空', trigger: 'change' }]" :rules="[
{ required: true, message: '请求方法不能为空', trigger: 'change' },
]"
label="请求方法" label="请求方法"
> >
<Select v-model:value="config.method" placeholder="请选择请求方法"> <Select v-model:value="config.method" placeholder="请选择请求方法">

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -55,7 +54,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'clientId']" :name="['config', 'clientId']"
:rules="[{ required: true, message: '客户端 ID 不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '客户端 ID 不能为空', trigger: 'blur' },
]"
label="客户端 ID" label="客户端 ID"
> >
<Input v-model:value="config.clientId" placeholder="请输入客户端 ID" /> <Input v-model:value="config.clientId" placeholder="请输入客户端 ID" />

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -37,7 +36,10 @@ onMounted(() => {
:rules="[{ required: true, message: '主机地址不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '主机地址不能为空', trigger: 'blur' }]"
label="主机地址" label="主机地址"
> >
<Input v-model:value="config.host" placeholder="请输入主机地址localhost" /> <Input
v-model:value="config.host"
placeholder="请输入主机地址localhost"
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'port']" :name="['config', 'port']"

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -34,7 +33,10 @@ onMounted(() => {
:rules="[{ required: true, message: '主机地址不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '主机地址不能为空', trigger: 'blur' }]"
label="主机地址" label="主机地址"
> >
<Input v-model:value="config.host" placeholder="请输入主机地址localhost" /> <Input
v-model:value="config.host"
placeholder="请输入主机地址localhost"
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'port']" :name="['config', 'port']"

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -32,7 +31,9 @@ onMounted(() => {
<template> <template>
<Form.Item <Form.Item
:name="['config', 'nameServer']" :name="['config', 'nameServer']"
:rules="[{ required: true, message: 'NameServer 地址不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: 'NameServer 地址不能为空', trigger: 'blur' },
]"
label="NameServer" label="NameServer"
> >
<Input <Input
@ -42,14 +43,18 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'accessKey']" :name="['config', 'accessKey']"
:rules="[{ required: true, message: 'AccessKey 不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: 'AccessKey 不能为空', trigger: 'blur' },
]"
label="AccessKey" label="AccessKey"
> >
<Input v-model:value="config.accessKey" placeholder="请输入 AccessKey" /> <Input v-model:value="config.accessKey" placeholder="请输入 AccessKey" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'secretKey']" :name="['config', 'secretKey']"
:rules="[{ required: true, message: 'SecretKey 不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: 'SecretKey 不能为空', trigger: 'blur' },
]"
label="SecretKey" label="SecretKey"
> >
<Input.Password <Input.Password

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -68,7 +67,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'connectTimeoutMs']" :name="['config', 'connectTimeoutMs']"
:rules="[{ required: true, message: '连接超时时间不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '连接超时时间不能为空', trigger: 'blur' },
]"
label="连接超时(ms)" label="连接超时(ms)"
> >
<InputNumber <InputNumber
@ -80,7 +81,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'readTimeoutMs']" :name="['config', 'readTimeoutMs']"
:rules="[{ required: true, message: '读取超时时间不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '读取超时时间不能为空', trigger: 'blur' },
]"
label="读取超时(ms)" label="读取超时(ms)"
> >
<InputNumber <InputNumber
@ -98,11 +101,16 @@ onMounted(() => {
:name="['config', 'sslCertPath']" :name="['config', 'sslCertPath']"
label="SSL 证书路径" label="SSL 证书路径"
> >
<Input v-model:value="config.sslCertPath" placeholder="请输入 SSL 证书路径" /> <Input
v-model:value="config.sslCertPath"
placeholder="请输入 SSL 证书路径"
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'dataFormat']" :name="['config', 'dataFormat']"
:rules="[{ required: true, message: '数据格式不能为空', trigger: 'change' }]" :rules="[
{ required: true, message: '数据格式不能为空', trigger: 'change' },
]"
label="数据格式" label="数据格式"
> >
<Select v-model:value="config.dataFormat" placeholder="请选择数据格式"> <Select v-model:value="config.dataFormat" placeholder="请选择数据格式">

View File

@ -1,5 +1,4 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -41,7 +40,11 @@ onMounted(() => {
<Form.Item <Form.Item
:name="['config', 'serverUrl']" :name="['config', 'serverUrl']"
:rules="[ :rules="[
{ required: true, message: 'WebSocket 服务器地址不能为空', trigger: 'blur' }, {
required: true,
message: 'WebSocket 服务器地址不能为空',
trigger: 'blur',
},
]" ]"
label="服务器地址" label="服务器地址"
> >
@ -52,7 +55,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'connectTimeoutMs']" :name="['config', 'connectTimeoutMs']"
:rules="[{ required: true, message: '连接超时时间不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '连接超时时间不能为空', trigger: 'blur' },
]"
label="连接超时(ms)" label="连接超时(ms)"
> >
<InputNumber <InputNumber
@ -64,7 +69,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'sendTimeoutMs']" :name="['config', 'sendTimeoutMs']"
:rules="[{ required: true, message: '发送超时时间不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '发送超时时间不能为空', trigger: 'blur' },
]"
label="发送超时(ms)" label="发送超时(ms)"
> >
<InputNumber <InputNumber
@ -107,7 +114,9 @@ onMounted(() => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:name="['config', 'dataFormat']" :name="['config', 'dataFormat']"
:rules="[{ required: true, message: '数据格式不能为空', trigger: 'change' }]" :rules="[
{ required: true, message: '数据格式不能为空', trigger: 'change' },
]"
label="数据格式" label="数据格式"
> >
<Select v-model:value="config.dataFormat" placeholder="请选择数据格式"> <Select v-model:value="config.dataFormat" placeholder="请选择数据格式">

View File

@ -103,7 +103,9 @@ function handleTypeChange(type: number) {
class="mx-4" class="mx-4"
> >
<Form.Item <Form.Item
:rules="[{ required: true, message: '目的名称不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '目的名称不能为空', trigger: 'blur' },
]"
label="目的名称" label="目的名称"
name="name" name="name"
> >
@ -117,13 +119,17 @@ function handleTypeChange(type: number) {
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
:rules="[{ required: true, message: '目的类型不能为空', trigger: 'change' }]" :rules="[
{ required: true, message: '目的类型不能为空', trigger: 'change' },
]"
label="目的类型" label="目的类型"
name="type" name="type"
> >
<Select <Select
:value="formData.type" :value="formData.type"
:options="getDictOptions(DICT_TYPE.IOT_DATA_SINK_TYPE_ENUM, 'number') as any" :options="
getDictOptions(DICT_TYPE.IOT_DATA_SINK_TYPE_ENUM, 'number') as any
"
placeholder="请选择目的类型" placeholder="请选择目的类型"
@change="(value: any) => handleTypeChange(value as number)" @change="(value: any) => handleTypeChange(value as number)"
/> />
@ -166,7 +172,9 @@ function handleTypeChange(type: number) {
v-model="formData.config" v-model="formData.config"
/> />
<Form.Item <Form.Item
:rules="[{ required: true, message: '目的状态不能为空', trigger: 'change' }]" :rules="[
{ required: true, message: '目的状态不能为空', trigger: 'change' },
]"
label="目的状态" label="目的状态"
name="status" name="status"
> >

View File

@ -108,9 +108,8 @@ function handleDeviceChange(deviceId?: number) {
function handleServiceChange(serviceIdentifier?: any) { function handleServiceChange(serviceIdentifier?: any) {
// //
const service = const service =
serviceList.value.find( serviceList.value.find((item) => item.identifier === serviceIdentifier) ||
(item) => item.identifier === serviceIdentifier, null;
) || null;
selectedService.value = service; selectedService.value = service;
// //

View File

@ -240,15 +240,23 @@ function removeConditionGroup() {
> >
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<!-- 连接线 --> <!-- 连接线 -->
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
<!-- 或标签 --> <!-- 或标签 -->
<div <div
class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50" class="px-[14px] py-[3px] rounded-full border border-orange-300 bg-orange-100 dark:border-orange-800 dark:bg-orange-950/50"
> >
<span class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"></span> <span
class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"
>
</span>
</div> </div>
<!-- 连接线 --> <!-- 连接线 -->
<div class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"></div> <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"
></div>
</div> </div>
</div> </div>
</div> </div>
@ -261,7 +269,10 @@ function removeConditionGroup() {
class="p-[24px] rounded-[8px] border-2 border-dashed border-orange-200 bg-orange-50/40 text-center dark:border-orange-900/40 dark:bg-orange-950/10" class="p-[24px] rounded-[8px] border-2 border-dashed border-orange-200 bg-orange-50/40 text-center dark:border-orange-900/40 dark:bg-orange-950/10"
> >
<div class="gap-[10px] flex flex-col items-center"> <div class="gap-[10px] flex flex-col items-center">
<IconifyIcon icon="lucide:plus" class="text-[28px] text-orange-400 dark:text-orange-300" /> <IconifyIcon
icon="lucide:plus"
class="text-[28px] text-orange-400 dark:text-orange-300"
/>
<div class="text-orange-600 dark:text-orange-300"> <div class="text-orange-600 dark:text-orange-300">
<p class="text-[13px] font-medium mb-[2px]">暂无子条件组</p> <p class="text-[13px] font-medium mb-[2px]">暂无子条件组</p>
<p class="text-[12px]">点击上方添加子条件组按钮开始配置</p> <p class="text-[12px]">点击上方添加子条件组按钮开始配置</p>

View File

@ -159,7 +159,9 @@ function updateConditionGroup(
> >
<span <span
class="text-[13px] font-semibold text-orange-600 dark:text-orange-300" class="text-[13px] font-semibold text-orange-600 dark:text-orange-300"
></span> >
</span>
</div> </div>
<div <div
class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800" class="w-[32px] h-[1px] bg-orange-300 dark:bg-orange-800"

View File

@ -436,79 +436,75 @@ watch(
<template #content> <template #content>
<!-- 弹出层内容 --> <!-- 弹出层内容 -->
<div class="json-params-detail-content"> <div class="json-params-detail-content">
<div class="mb-4 flex items-center gap-2"> <div class="mb-4 flex items-center gap-2">
<IconifyIcon :icon="titleIcon" class="text-lg text-primary" /> <IconifyIcon :icon="titleIcon" class="text-lg text-primary" />
<span class="text-base font-bold text-foreground"> <span class="text-base font-bold text-foreground">
{{ title }} {{ title }}
</span> </span>
</div> </div>
<div class="space-y-4"> <div class="space-y-4">
<!-- 参数列表 --> <!-- 参数列表 -->
<div v-if="paramsList.length > 0"> <div v-if="paramsList.length > 0">
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">
<IconifyIcon <IconifyIcon
:icon="paramsIcon" :icon="paramsIcon"
class="text-base text-primary" class="text-base text-primary"
/> />
<span class="text-base font-bold text-foreground"> <span class="text-base font-bold text-foreground">
{{ paramsLabel }} {{ paramsLabel }}
</span> </span>
</div> </div>
<div class="ml-6 space-y-2"> <div class="ml-6 space-y-2">
<div <div
v-for="param in paramsList" v-for="param in paramsList"
:key="param.identifier" :key="param.identifier"
class="flex items-center justify-between rounded-lg bg-card p-2" class="flex items-center justify-between rounded-lg bg-card p-2"
> >
<div class="flex-1"> <div class="flex-1">
<div class="text-base font-bold text-foreground"> <div class="text-base font-bold text-foreground">
{{ param.name }} {{ param.name }}
<Tag <Tag v-if="param.required" color="error" class="ml-1">
v-if="param.required" {{ JSON_PARAMS_INPUT_CONSTANTS.REQUIRED_TAG }}
color="error" </Tag>
class="ml-1" </div>
> <div class="text-xs text-muted-foreground">
{{ JSON_PARAMS_INPUT_CONSTANTS.REQUIRED_TAG }} {{ param.identifier }}
</div>
</div>
<div class="flex items-center gap-2">
<Tag :color="getParamTypeTag(param.dataType)">
{{ getParamTypeName(param.dataType) }}
</Tag> </Tag>
<span class="text-xs text-muted-foreground">
{{ getExampleValue(param) }}
</span>
</div> </div>
<div class="text-xs text-muted-foreground">
{{ param.identifier }}
</div>
</div>
<div class="flex items-center gap-2">
<Tag :color="getParamTypeTag(param.dataType)">
{{ getParamTypeName(param.dataType) }}
</Tag>
<span class="text-xs text-muted-foreground">
{{ getExampleValue(param) }}
</span>
</div> </div>
</div> </div>
</div>
<div class="ml-6 mt-3"> <div class="ml-6 mt-3">
<div class="mb-1 text-xs text-muted-foreground"> <div class="mb-1 text-xs text-muted-foreground">
{{ JSON_PARAMS_INPUT_CONSTANTS.COMPLETE_JSON_FORMAT }} {{ JSON_PARAMS_INPUT_CONSTANTS.COMPLETE_JSON_FORMAT }}
</div> </div>
<pre <pre
class="border-l-[3px] overflow-x-auto rounded-lg border-primary bg-card p-3 text-sm text-foreground" class="border-l-[3px] overflow-x-auto rounded-lg border-primary bg-card p-3 text-sm text-foreground"
> >
<code>{{ generateExampleJson() }}</code> <code>{{ generateExampleJson() }}</code>
</pre> </pre>
</div>
</div> </div>
</div>
<!-- 无参数提示 --> <!-- 无参数提示 -->
<div v-else> <div v-else>
<div class="py-4 text-center"> <div class="py-4 text-center">
<p class="text-sm text-muted-foreground"> <p class="text-sm text-muted-foreground">
{{ emptyMessage }} {{ emptyMessage }}
</p> </p>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<Button <Button
type="link" type="link"

View File

@ -8,7 +8,14 @@ import {
} from '@vben/constants'; } from '@vben/constants';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { DatePicker, Input, InputNumber, Select, Tag, Tooltip } from 'ant-design-vue'; import {
DatePicker,
Input,
InputNumber,
Select,
Tag,
Tooltip,
} from 'ant-design-vue';
/** 值输入组件 */ /** 值输入组件 */
defineOptions({ name: 'ValueInput' }); defineOptions({ name: 'ValueInput' });
@ -134,7 +141,7 @@ function handleDateChange(value: any) {
/** 处理数字变化事件 InputNumber 回调值类型为 ValueTypestring | number */ /** 处理数字变化事件 InputNumber 回调值类型为 ValueTypestring | number */
function handleNumberChange(value: any) { function handleNumberChange(value: any) {
localValue.value = value == null ? '' : String(value); localValue.value = value === null ? '' : String(value);
} }
/** 监听操作符变化 */ /** 监听操作符变化 */
@ -233,11 +240,7 @@ watch(
class="mt-2 flex flex-wrap items-center gap-1" class="mt-2 flex flex-wrap items-center gap-1"
> >
<span class="text-xs text-muted-foreground"> 解析结果 </span> <span class="text-xs text-muted-foreground"> 解析结果 </span>
<Tag <Tag v-for="(item, index) in listPreview" :key="index" class="m-0">
v-for="(item, index) in listPreview"
:key="index"
class="m-0"
>
{{ item }} {{ item }}
</Tag> </Tag>
</div> </div>

View File

@ -268,7 +268,9 @@ function onActionTypeChange(action: RuleSceneApi.Action, type: number) {
> >
<div class="mb-2 flex items-center gap-2"> <div class="mb-2 flex items-center gap-2">
<IconifyIcon icon="ep:warning" class="text-base text-warning" /> <IconifyIcon icon="ep:warning" class="text-base text-warning" />
<span class="font-semibold text-sm text-foreground">触发告警</span> <span class="font-semibold text-sm text-foreground">
触发告警
</span>
<Tag color="warning">自动执行</Tag> <Tag color="warning">自动执行</Tag>
</div> </div>
<div class="text-xs leading-relaxed text-muted-foreground"> <div class="text-xs leading-relaxed text-muted-foreground">

View File

@ -31,7 +31,9 @@ const formData = useVModel(props, 'modelValue', emit); // 表单数据
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<IconifyIcon icon="ep:info-filled" class="text-[18px] text-primary" /> <IconifyIcon icon="ep:info-filled" class="text-[18px] text-primary" />
<span class="text-[16px] font-semibold text-foreground">基础信息</span> <span class="text-[16px] font-semibold text-foreground">
基础信息
</span>
</div> </div>
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="formData.status" /> <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="formData.status" />

View File

@ -116,7 +116,9 @@ onMounted(() => {
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="gap-[8px] flex items-center"> <div class="gap-[8px] flex items-center">
<IconifyIcon icon="ep:lightning" class="text-[18px] text-primary" /> <IconifyIcon icon="ep:lightning" class="text-[18px] text-primary" />
<span class="text-[16px] font-semibold text-foreground">触发器配置</span> <span class="text-[16px] font-semibold text-foreground">
触发器配置
</span>
<Tag color="default"> {{ triggers.length }} 个触发器 </Tag> <Tag color="default"> {{ triggers.length }} 个触发器 </Tag>
</div> </div>
<Button type="primary" size="small" @click="addTrigger"> <Button type="primary" size="small" @click="addTrigger">

View File

@ -98,9 +98,7 @@ const selectedProperty = computed(() =>
/** 处理选择变化事件 */ /** 处理选择变化事件 */
function handleChange(value: any) { function handleChange(value: any) {
const property = propertyList.value.find( const property = propertyList.value.find((item) => item.identifier === value);
(item) => item.identifier === value,
);
if (property) { if (property) {
emit('change', { type: property.dataType, config: property }); emit('change', { type: property.dataType, config: property });
} }
@ -154,23 +152,27 @@ function parseThingModelData(
outputParams: event.outputParams, outputParams: event.outputParams,
event, event,
})); }));
const services = (tsl.services ?? []).map<PropertySelectorItem>((service) => ({ const services = (tsl.services ?? []).map<PropertySelectorItem>(
identifier: service.identifier!, (service) => ({
name: service.name!, identifier: service.identifier!,
description: service.description, name: service.name!,
dataType: 'struct', description: service.description,
type: IoTThingModelTypeEnum.SERVICE, dataType: 'struct',
callType: service.callType, type: IoTThingModelTypeEnum.SERVICE,
required: service.required, callType: service.callType,
inputParams: service.inputParams, required: service.required,
outputParams: service.outputParams, inputParams: service.inputParams,
service, outputParams: service.outputParams,
})); service,
}),
);
return [...properties, ...events, ...services]; return [...properties, ...events, ...services];
} }
/** 获取属性取值范围:数值型给 min~max枚举 / 布尔给选项列表 */ /** 获取属性取值范围:数值型给 min~max枚举 / 布尔给选项列表 */
function getPropertyRange(property: ThingModelApi.Property): string | undefined { function getPropertyRange(
property: ThingModelApi.Property,
): string | undefined {
const specs = property.dataSpecs; const specs = property.dataSpecs;
if (specs && specs.min !== undefined && specs.max !== undefined) { if (specs && specs.min !== undefined && specs.max !== undefined) {
return `${specs.min}~${specs.max}`; return `${specs.min}~${specs.max}`;
@ -225,7 +227,9 @@ watch(
:value="property.identifier" :value="property.identifier"
> >
<div class="py-[2px] flex w-full items-center justify-between"> <div class="py-[2px] flex w-full items-center justify-between">
<span class="text-[14px] font-medium flex-1 truncate text-foreground"> <span
class="text-[14px] font-medium flex-1 truncate text-foreground"
>
{{ property.name }} {{ property.name }}
</span> </span>
<Tag class="ml-[8px] flex-shrink-0"> <Tag class="ml-[8px] flex-shrink-0">
@ -248,103 +252,125 @@ watch(
<template #content> <template #content>
<!-- 弹出层内容 --> <!-- 弹出层内容 -->
<div class="property-detail-content"> <div class="property-detail-content">
<div class="gap-[8px] mb-[12px] flex items-center"> <div class="gap-[8px] mb-[12px] flex items-center">
<IconifyIcon icon="ep:info-filled" class="text-[16px] text-info" /> <IconifyIcon icon="ep:info-filled" class="text-[16px] text-info" />
<span class="text-[14px] font-medium text-foreground"> <span class="text-[14px] font-medium text-foreground">
{{ selectedProperty.name }} {{ selectedProperty.name }}
</span>
<Tag>
{{ getDataTypeName(selectedProperty.dataType) }}
</Tag>
</div>
<div class="space-y-[8px] ml-[24px]">
<div class="gap-[8px] flex items-start">
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground">
标识符
</span>
<span class="text-[12px] flex-1 text-foreground">
{{ selectedProperty.identifier }}
</span> </span>
<Tag>
{{ getDataTypeName(selectedProperty.dataType) }}
</Tag>
</div> </div>
<div <div class="space-y-[8px] ml-[24px]">
v-if="selectedProperty.description" <div class="gap-[8px] flex items-start">
class="gap-[8px] flex items-start" <span
> class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> >
描述 标识符
</span> </span>
<span class="text-[12px] flex-1 text-foreground"> <span class="text-[12px] flex-1 text-foreground">
{{ selectedProperty.description }} {{ selectedProperty.identifier }}
</span> </span>
</div> </div>
<div v-if="selectedProperty.unit" class="gap-[8px] flex items-start"> <div
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> v-if="selectedProperty.description"
单位 class="gap-[8px] flex items-start"
</span> >
<span class="text-[12px] flex-1 text-foreground"> <span
{{ selectedProperty.unit }} class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
</span> >
</div> 描述
</span>
<span class="text-[12px] flex-1 text-foreground">
{{ selectedProperty.description }}
</span>
</div>
<div v-if="selectedProperty.range" class="gap-[8px] flex items-start"> <div
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> v-if="selectedProperty.unit"
取值范围 class="gap-[8px] flex items-start"
</span> >
<span class="text-[12px] flex-1 text-foreground"> <span
{{ selectedProperty.range }} class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
</span> >
</div> 单位
</span>
<span class="text-[12px] flex-1 text-foreground">
{{ selectedProperty.unit }}
</span>
</div>
<!-- 根据属性类型显示额外信息 --> <div
<div v-if="selectedProperty.range"
v-if=" class="gap-[8px] flex items-start"
selectedProperty.type === IoTThingModelTypeEnum.PROPERTY && >
selectedProperty.accessMode <span
" class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
class="gap-[8px] flex items-start" >
> 取值范围
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> </span>
访问模式 <span class="text-[12px] flex-1 text-foreground">
</span> {{ selectedProperty.range }}
<span class="text-[12px] flex-1 text-foreground"> </span>
{{ getAccessModeLabel(selectedProperty.accessMode) }} </div>
</span>
</div>
<div <!-- 根据属性类型显示额外信息 -->
v-if=" <div
selectedProperty.type === IoTThingModelTypeEnum.EVENT && v-if="
selectedProperty.eventType selectedProperty.type === IoTThingModelTypeEnum.PROPERTY &&
" selectedProperty.accessMode
class="gap-[8px] flex items-start" "
> class="gap-[8px] flex items-start"
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> >
事件类型 <span
</span> class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
<span class="text-[12px] flex-1 text-foreground"> >
{{ getEventTypeLabel(selectedProperty.eventType) }} 访问模式
</span> </span>
</div> <span class="text-[12px] flex-1 text-foreground">
{{ getAccessModeLabel(selectedProperty.accessMode) }}
</span>
</div>
<div <div
v-if=" v-if="
selectedProperty.type === IoTThingModelTypeEnum.SERVICE && selectedProperty.type === IoTThingModelTypeEnum.EVENT &&
selectedProperty.callType selectedProperty.eventType
" "
class="gap-[8px] flex items-start" class="gap-[8px] flex items-start"
> >
<span class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"> <span
调用类型 class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
</span> >
<span class="text-[12px] flex-1 text-foreground"> 事件类型
{{ getThingModelServiceCallTypeLabel(selectedProperty.callType) }} </span>
</span> <span class="text-[12px] flex-1 text-foreground">
{{ getEventTypeLabel(selectedProperty.eventType) }}
</span>
</div>
<div
v-if="
selectedProperty.type === IoTThingModelTypeEnum.SERVICE &&
selectedProperty.callType
"
class="gap-[8px] flex items-start"
>
<span
class="text-[12px] min-w-[60px] flex-shrink-0 text-muted-foreground"
>
调用类型
</span>
<span class="text-[12px] flex-1 text-foreground">
{{
getThingModelServiceCallTypeLabel(selectedProperty.callType)
}}
</span>
</div>
</div> </div>
</div> </div>
</div>
</template> </template>
<Button <Button
type="link" type="link"

View File

@ -99,8 +99,7 @@ async function handleDelete(row: RuleSceneApi.SceneRule) {
function hasTimerTrigger(row: RuleSceneApi.SceneRule): boolean { function hasTimerTrigger(row: RuleSceneApi.SceneRule): boolean {
return ( return (
row.triggers?.some( row.triggers?.some(
(trigger) => (trigger) => trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
) || false ) || false
); );
} }
@ -154,8 +153,7 @@ function getActionSummary(row: RuleSceneApi.SceneRule): string {
/** 取定时触发器的 CRON 频率描述 */ /** 取定时触发器的 CRON 频率描述 */
function getCronFrequency(row: RuleSceneApi.SceneRule): string { function getCronFrequency(row: RuleSceneApi.SceneRule): string {
const timerTrigger = row.triggers?.find( const timerTrigger = row.triggers?.find(
(trigger) => (trigger) => trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
); );
return timerTrigger?.cronExpression return timerTrigger?.cronExpression
? CronUtils.getFrequencyDescription(timerTrigger.cronExpression) ? CronUtils.getFrequencyDescription(timerTrigger.cronExpression)
@ -165,8 +163,7 @@ function getCronFrequency(row: RuleSceneApi.SceneRule): string {
/** 取定时触发器原始 CRON 表达式 */ /** 取定时触发器原始 CRON 表达式 */
function getCronExpression(row: RuleSceneApi.SceneRule): string { function getCronExpression(row: RuleSceneApi.SceneRule): string {
const timerTrigger = row.triggers?.find( const timerTrigger = row.triggers?.find(
(trigger) => (trigger) => trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
); );
return timerTrigger?.cronExpression || ''; return timerTrigger?.cronExpression || '';
} }
@ -174,8 +171,7 @@ function getCronExpression(row: RuleSceneApi.SceneRule): string {
/** 取定时触发器下次执行时间 */ /** 取定时触发器下次执行时间 */
function getNextExecutionTime(row: RuleSceneApi.SceneRule): Date | null { function getNextExecutionTime(row: RuleSceneApi.SceneRule): Date | null {
const timerTrigger = row.triggers?.find( const timerTrigger = row.triggers?.find(
(trigger) => (trigger) => trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
trigger.type === IotRuleSceneTriggerTypeEnum.TIMER,
); );
return timerTrigger?.cronExpression return timerTrigger?.cronExpression
? CronUtils.getNextExecutionTime(timerTrigger.cronExpression) ? CronUtils.getNextExecutionTime(timerTrigger.cronExpression)
@ -327,7 +323,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
:title="row.description" :title="row.description"
placement="top" placement="top"
> >
<div class="mt-1 max-w-[200px] truncate text-xs text-muted-foreground"> <div
class="mt-1 max-w-[200px] truncate text-xs text-muted-foreground"
>
{{ row.description }} {{ row.description }}
</div> </div>
</Tooltip> </Tooltip>

View File

@ -5,7 +5,6 @@ import type { ThingModelApi } from '#/api/iot/thingmodel';
import { DICT_TYPE, getDataTypeOptionsLabel } from '@vben/constants'; import { DICT_TYPE, getDataTypeOptionsLabel } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
/** 列表的搜索表单 */ /** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [

View File

@ -2,10 +2,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { import { getDataTypeOptions, IoTDataSpecsDataTypeEnum } from '@vben/constants';
getDataTypeOptions,
IoTDataSpecsDataTypeEnum,
} from '@vben/constants';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Form, Input, Radio } from 'ant-design-vue'; import { Form, Input, Radio } from 'ant-design-vue';

View File

@ -39,8 +39,9 @@ function validateEnumValue(_rule: any, value: any, callback: any) {
callback(new Error('枚举值必须是数字')); callback(new Error('枚举值必须是数字'));
return; return;
} }
const sameCount = dataSpecsList.value.filter((it) => it.value === value) const sameCount = dataSpecsList.value.filter(
.length; (it) => it.value === value,
).length;
if (sameCount > 1) { if (sameCount > 1) {
callback(new Error('枚举值不能重复')); callback(new Error('枚举值不能重复'));
return; return;
@ -100,7 +101,10 @@ function validateEnumList(_rule: any, _value: any, callback: any) {
]" ]"
class="mb-0 flex-1" class="mb-0 flex-1"
> >
<Input v-model:value="item.value" placeholder="请输入枚举值如「0」" /> <Input
v-model:value="item.value"
placeholder="请输入枚举值如「0」"
/>
</Form.Item> </Form.Item>
<span class="mx-2">~</span> <span class="mx-2">~</span>
<Form.Item <Form.Item

View File

@ -53,9 +53,7 @@ function unitChange(unitSpecs: any) {
</Form.Item> </Form.Item>
<Form.Item label="单位"> <Form.Item label="单位">
<Select <Select
:value=" :value="dataSpecs.unit ? `${dataSpecs.unitName}-${dataSpecs.unit}` : ''"
dataSpecs.unit ? `${dataSpecs.unitName}-${dataSpecs.unit}` : ''
"
show-search show-search
placeholder="请选择单位" placeholder="请选择单位"
class="w-full" class="w-full"

View File

@ -136,11 +136,7 @@ onMounted(() => {
:wrapper-col="{ span: 18 }" :wrapper-col="{ span: 18 }"
class="mx-4" class="mx-4"
> >
<Form.Item <Form.Item :rules="ThingModelFormRules.name" label="参数名称" name="name">
:rules="ThingModelFormRules.name"
label="参数名称"
name="name"
>
<Input v-model:value="formData.name" placeholder="请输入参数名称" /> <Input v-model:value="formData.name" placeholder="请输入参数名称" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item

View File

@ -111,7 +111,9 @@ function buildEmptyFormData(): ThingModelApi.ThingModel {
} }
/** 回显数据时,规整各分支字段确保子表单可绑定 */ /** 回显数据时,规整各分支字段确保子表单可绑定 */
function normalizeFormData(result: ThingModelApi.ThingModel): ThingModelApi.ThingModel { function normalizeFormData(
result: ThingModelApi.ThingModel,
): ThingModelApi.ThingModel {
const next: any = { ...result, type: Number(result.type) }; const next: any = { ...result, type: Number(result.type) };
if (isEmpty(next.property)) { if (isEmpty(next.property)) {
next.dataType = IoTDataSpecsDataTypeEnum.INT; next.dataType = IoTDataSpecsDataTypeEnum.INT;
@ -141,46 +143,46 @@ function normalizeFormData(result: ThingModelApi.ThingModel): ThingModelApi.Thin
/** 按功能类型将子表单数据回写到顶层,并清理无关分支 */ /** 按功能类型将子表单数据回写到顶层,并清理无关分支 */
function fillExtraAttributes(data: any) { function fillExtraAttributes(data: any) {
switch (data.type) { switch (data.type) {
case IoTThingModelTypeEnum.EVENT: { case IoTThingModelTypeEnum.EVENT: {
removeDataSpecs(data.event); removeDataSpecs(data.event);
data.dataType = data.event.dataType; data.dataType = data.event.dataType;
data.event.identifier = data.identifier; data.event.identifier = data.identifier;
data.event.name = data.name; data.event.name = data.name;
if (isEmpty(data.event.outputParams)) { if (isEmpty(data.event.outputParams)) {
delete data.event.outputParams; delete data.event.outputParams;
} }
delete data.property; delete data.property;
delete data.service; delete data.service;
break; break;
}
case IoTThingModelTypeEnum.PROPERTY: {
removeDataSpecs(data.property);
data.dataType = data.property.dataType;
data.property.identifier = data.identifier;
data.property.name = data.name;
delete data.service;
delete data.event;
break;
}
case IoTThingModelTypeEnum.SERVICE: {
removeDataSpecs(data.service);
data.dataType = data.service.dataType;
data.service.identifier = data.identifier;
data.service.name = data.name;
if (isEmpty(data.service.inputParams)) {
delete data.service.inputParams;
} }
if (isEmpty(data.service.outputParams)) { case IoTThingModelTypeEnum.PROPERTY: {
delete data.service.outputParams; removeDataSpecs(data.property);
} data.dataType = data.property.dataType;
delete data.property; data.property.identifier = data.identifier;
delete data.event; data.property.name = data.name;
delete data.service;
delete data.event;
break; break;
} }
// No default case IoTThingModelTypeEnum.SERVICE: {
removeDataSpecs(data.service);
data.dataType = data.service.dataType;
data.service.identifier = data.identifier;
data.service.name = data.name;
if (isEmpty(data.service.inputParams)) {
delete data.service.inputParams;
}
if (isEmpty(data.service.outputParams)) {
delete data.service.outputParams;
}
delete data.property;
delete data.event;
break;
}
// No default
} }
} }
@ -204,11 +206,7 @@ function removeDataSpecs(val: any) {
:wrapper-col="{ span: 18 }" :wrapper-col="{ span: 18 }"
class="mx-4" class="mx-4"
> >
<Form.Item <Form.Item :rules="ThingModelFormRules.type" label="功能类型" name="type">
:rules="ThingModelFormRules.type"
label="功能类型"
name="type"
>
<Radio.Group v-model:value="formData.type"> <Radio.Group v-model:value="formData.type">
<Radio.Button <Radio.Button
v-for="dict in getDictOptions(DICT_TYPE.IOT_THING_MODEL_TYPE)" v-for="dict in getDictOptions(DICT_TYPE.IOT_THING_MODEL_TYPE)"
@ -219,11 +217,7 @@ function removeDataSpecs(val: any) {
</Radio.Button> </Radio.Button>
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Form.Item <Form.Item :rules="ThingModelFormRules.name" label="功能名称" name="name">
:rules="ThingModelFormRules.name"
label="功能名称"
name="name"
>
<Input v-model:value="formData.name" placeholder="请输入功能名称" /> <Input v-model:value="formData.name" placeholder="请输入功能名称" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item

View File

@ -135,11 +135,7 @@ function deleteParamItem(index: number) {
:wrapper-col="{ span: 18 }" :wrapper-col="{ span: 18 }"
class="mx-4" class="mx-4"
> >
<Form.Item <Form.Item :rules="ThingModelFormRules.name" label="参数名称" name="name">
:rules="ThingModelFormRules.name"
label="参数名称"
name="name"
>
<Input v-model:value="formData.name" placeholder="请输入参数名称" /> <Input v-model:value="formData.name" placeholder="请输入参数名称" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item

View File

@ -137,10 +137,7 @@ if (!props.isStructDataSpecs && !props.isParams) {
v-if="property.dataType === IoTDataSpecsDataTypeEnum.BOOL" v-if="property.dataType === IoTDataSpecsDataTypeEnum.BOOL"
label="布尔值" label="布尔值"
> >
<template <template v-for="(item, index) in property.dataSpecsList" :key="item.value">
v-for="(item, index) in property.dataSpecsList"
:key="item.value"
>
<div class="mb-[5px] flex w-full items-center justify-start"> <div class="mb-[5px] flex w-full items-center justify-start">
<span>{{ item.value }}</span> <span>{{ item.value }}</span>
<span class="mx-2">-</span> <span class="mx-2">-</span>

View File

@ -26,7 +26,9 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock(); modalApi.lock();
try { try {
// //
thingModelTSL.value = await getThingModelTSLByProductId(product?.value?.id || 0); thingModelTSL.value = await getThingModelTSLByProductId(
product?.value?.id || 0,
);
// values // values
tslString.value = JSON.stringify(thingModelTSL.value, null, 2); tslString.value = JSON.stringify(thingModelTSL.value, null, 2);
} finally { } finally {
@ -64,7 +66,9 @@ watch(tslString, (newValue) => {
v-if="viewMode === 'view'" v-if="viewMode === 'view'"
class="max-h-[600px] overflow-y-auto rounded border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800" class="max-h-[600px] overflow-y-auto rounded border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800"
> >
<pre class="m-0 whitespace-pre-wrap break-words font-mono text-[13px] leading-normal"><code>{{ formattedTSL }}</code></pre> <pre
class="m-0 whitespace-pre-wrap break-words font-mono text-[13px] leading-normal"
><code>{{ formattedTSL }}</code></pre>
</div> </div>
<!-- 编辑器视图可编辑 --> <!-- 编辑器视图可编辑 -->
<Textarea <Textarea

View File

@ -270,10 +270,7 @@ export function usePartFormSchema(): VbenFormSchema[] {
component: 'Select', component: 'Select',
componentProps: { componentProps: {
allowClear: true, allowClear: true,
options: getDictOptions( options: getDictOptions(DICT_TYPE.MES_MD_AUTO_CODE_PART_TYPE, 'number'),
DICT_TYPE.MES_MD_AUTO_CODE_PART_TYPE,
'number',
),
placeholder: '请选择分段类型', placeholder: '请选择分段类型',
}, },
rules: 'selectRequired', rules: 'selectRequired',

View File

@ -6,7 +6,7 @@ import { nextTick, ref } from 'vue';
import { CommonStatusEnum } from '@vben/constants'; import { CommonStatusEnum } from '@vben/constants';
import { message, Modal } from 'ant-design-vue'; import { Button, message, Modal } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getClientPage } from '#/api/mes/md/client'; import { getClientPage } from '#/api/mes/md/client';
@ -47,13 +47,33 @@ async function waitTableReady(): Promise<void> {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
if (!multiple.value) { for (let index = 0; index < MAX_TABLE_READY_FRAMES; index += 1) {
const selected = checked && row ? [row] : []; if (queryFinished.value) {
selectedRows.value = selected; const rows = getTableRows();
await syncSingleSelection(selected[0]); if (latestQueryRows.value.length === 0 && rows.length === 0) {
return; return;
}
if (latestQueryRows.value.length > 0 && rows.length > 0) {
return;
}
}
await waitNextFrame();
} }
selectedRows.value = records; }
/** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() {
const selectedMap = new Map<number, MesMdClientApi.Client>();
const records = [
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
...(gridApi.grid.getCheckboxRecords?.() ?? []),
] as MesMdClientApi.Client[];
records.forEach((row) => {
if (row.id !== null) {
selectedMap.set(row.id as number, row);
}
});
return [...selectedMap.values()];
} }
/** 处理勾选变化 */ /** 处理勾选变化 */
@ -92,7 +112,7 @@ async function applyPreSelection() {
// proxy fullData // proxy fullData
const rows = getTableRows(); const rows = getTableRows();
for (const row of rows) { for (const row of rows) {
if (row.id == null || !preSelectedIds.value.includes(row.id)) { if (row.id === null || !preSelectedIds.value.includes(row.id as number)) {
continue; continue;
} }
if (multiple.value) { if (multiple.value) {
@ -169,14 +189,19 @@ async function resetQueryState() {
} }
/** 打开客户选择弹窗 */ /** 打开客户选择弹窗 */
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
latestQueryRows.value = []; latestQueryRows.value = [];
queryFinished.value = false; queryFinished.value = false;
await nextTick(); await nextTick();
gridApi.setGridOptions({ columns: useClientSelectGridColumns(multiple.value) }); gridApi.setGridOptions({
columns: useClientSelectGridColumns(multiple.value),
});
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
@ -213,5 +238,9 @@ defineExpose({ open: openModal });
@cancel="closeModal" @cancel="closeModal"
> >
<Grid table-title="" /> <Grid table-title="" />
<template #footer>
<Button @click="closeModal"> </Button>
<Button type="primary" @click="handleConfirm"> </Button>
</template>
</Modal> </Modal>
</template> </template>

View File

@ -37,13 +37,18 @@ const hovering = ref(false); // 是否悬停
const selectedItem = ref<MesMdClientApi.Client>(); // const selectedItem = ref<MesMdClientApi.Client>(); //
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( // const showClear = computed(
() => props.allowClear && !props.disabled && hovering.value && props.modelValue != null, //
() =>
props.allowClear &&
!props.disabled &&
hovering.value &&
props.modelValue !== null,
); );
/** 根据客户编号回显选择器 */ /** 根据客户编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id == null) { if (id === null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -51,7 +56,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getClient(id); selectedItem.value = await getClient(id as number);
} catch (error) { } catch (error) {
console.error('[MdClientSelect] resolveItemById failed:', error); console.error('[MdClientSelect] resolveItemById failed:', error);
} }
@ -83,8 +88,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue == null ? [] : [props.modelValue]; const selectedIds = props.modelValue === null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false }); dialogRef.value?.open(selectedIds as number[], { multiple: false });
} }
/** 回填选中的客户 */ /** 回填选中的客户 */

View File

@ -144,7 +144,10 @@ async function resetQueryState() {
} }
/** 打开物料选择弹窗 */ /** 打开物料选择弹窗 */
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
@ -167,7 +170,10 @@ function handleConfirm() {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]); emit(
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }
@ -185,7 +191,10 @@ defineExpose({ open: openModal });
> >
<div class="flex h-full w-full"> <div class="flex h-full w-full">
<Card class="mr-4 h-full w-1/5"> <Card class="mr-4 h-full w-1/5">
<MdItemTypeTree ref="typeTreeRef" @node-click="handleItemTypeNodeClick" /> <MdItemTypeTree
ref="typeTreeRef"
@node-click="handleItemTypeNodeClick"
/>
</Card> </Card>
<div class="w-4/5"> <div class="w-4/5">
<Grid table-title="" /> <Grid table-title="" />

View File

@ -37,13 +37,18 @@ const hovering = ref(false); // 是否悬停
const selectedItem = ref<MesMdItemApi.Item>(); // const selectedItem = ref<MesMdItemApi.Item>(); //
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( // const showClear = computed(
() => props.allowClear && !props.disabled && hovering.value && props.modelValue != null, //
() =>
props.allowClear &&
!props.disabled &&
hovering.value &&
props.modelValue !== null,
); );
/** 根据物料编号回显选择器 */ /** 根据物料编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id == null) { if (id === null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -51,7 +56,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getItem(id); selectedItem.value = await getItem(id as number);
} catch (error) { } catch (error) {
console.error('[MdItemSelect] resolveItemById failed:', error); console.error('[MdItemSelect] resolveItemById failed:', error);
} }
@ -83,8 +88,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue == null ? [] : [props.modelValue]; const selectedIds = props.modelValue === null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false }); dialogRef.value?.open(selectedIds as number[], { multiple: false });
} }
/** 回填选中的物料 */ /** 回填选中的物料 */

View File

@ -68,7 +68,7 @@ async function openModal(itemId: number, selectedBomItemId?: number) {
list.value = await getProductBomListByItemId(itemId); list.value = await getProductBomListByItemId(itemId);
gridApi.setGridOptions({ data: list.value }); gridApi.setGridOptions({ data: list.value });
await nextTick(); await nextTick();
if (selectedBomItemId != null) { if (selectedBomItemId !== null) {
const match = list.value.find( const match = list.value.find(
(row) => row.bomItemId === selectedBomItemId, (row) => row.bomItemId === selectedBomItemId,
); );

View File

@ -39,17 +39,18 @@ const hovering = ref(false); // 是否悬停
const selectedBom = ref<MesMdProductBomApi.ProductBom>(); // BOM const selectedBom = ref<MesMdProductBomApi.ProductBom>(); // BOM
const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); // const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); //
const showClear = computed( // const showClear = computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue != null, props.modelValue !== null,
); );
/** 根据 BOM 子物料编号回显选择器 */ /** 根据 BOM 子物料编号回显选择器 */
async function resolveBomById(bomItemId: number | undefined) { async function resolveBomById(bomItemId: number | undefined) {
if (bomItemId == null || props.itemId == null) { if (bomItemId === null || props.itemId === null) {
selectedBom.value = undefined; selectedBom.value = undefined;
return; return;
} }
@ -57,7 +58,7 @@ async function resolveBomById(bomItemId: number | undefined) {
return; return;
} }
try { try {
const list = await getProductBomListByItemId(props.itemId); const list = await getProductBomListByItemId(props.itemId as number);
selectedBom.value = list.find((item) => item.bomItemId === bomItemId); selectedBom.value = list.find((item) => item.bomItemId === bomItemId);
} catch (error) { } catch (error) {
console.error('[MdProductBomSelect] resolveBomById failed:', error); console.error('[MdProductBomSelect] resolveBomById failed:', error);
@ -90,7 +91,7 @@ function clearSelected() {
/** 打开 BOM 物料选择弹窗 */ /** 打开 BOM 物料选择弹窗 */
function handleClick(event: MouseEvent) { function handleClick(event: MouseEvent) {
if (props.disabled || props.itemId == null) { if (props.disabled || props.itemId === null) {
return; return;
} }
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
@ -99,7 +100,7 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
dialogRef.value?.open(props.itemId, props.modelValue); dialogRef.value?.open(props.itemId as number, props.modelValue);
} }
/** 回填选中的 BOM 物料 */ /** 回填选中的 BOM 物料 */

View File

@ -89,7 +89,10 @@ export function useFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
component: markRaw(MdItemTypeSelect), component: markRaw(MdItemTypeSelect),
componentProps: { componentProps: {
onChange: async (itemType: any) => { onChange: async (itemType: any) => {
await formApi?.setFieldValue('itemOrProduct', itemType?.itemOrProduct); await formApi?.setFieldValue(
'itemOrProduct',
itemType?.itemOrProduct,
);
}, },
}, },
rules: 'selectRequired', rules: 'selectRequired',
@ -410,54 +413,55 @@ export function useItemSelectGridColumns(): VxeTableGridOptions<MesMdItemApi.Ite
export function useProductBomGridColumns( export function useProductBomGridColumns(
isReadOnly = false, isReadOnly = false,
): VxeTableGridOptions<MesMdProductBomApi.ProductBom>['columns'] { ): VxeTableGridOptions<MesMdProductBomApi.ProductBom>['columns'] {
const columns: VxeTableGridOptions<MesMdProductBomApi.ProductBom>['columns'] = [ const columns: VxeTableGridOptions<MesMdProductBomApi.ProductBom>['columns'] =
{ [
field: 'bomItemCode', {
title: '物料编码', field: 'bomItemCode',
minWidth: 160, title: '物料编码',
align: 'center', minWidth: 160,
}, align: 'center',
{
field: 'bomItemName',
title: '物料名称',
minWidth: 160,
align: 'center',
},
{
field: 'bomItemSpecification',
title: '规格型号',
minWidth: 140,
align: 'center',
},
{
field: 'unitMeasureName',
title: '单位',
width: 80,
align: 'center',
},
{
field: 'itemOrProduct',
title: '物料/产品',
width: 110,
align: 'center',
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_MD_ITEM_OR_PRODUCT },
}, },
}, {
{ field: 'bomItemName',
field: 'quantity', title: '物料名称',
title: '用量比例', minWidth: 160,
width: 100, align: 'center',
align: 'center', },
}, {
{ field: 'bomItemSpecification',
field: 'remark', title: '规格型号',
title: '备注', minWidth: 140,
minWidth: 140, align: 'center',
align: 'center', },
}, {
]; field: 'unitMeasureName',
title: '单位',
width: 80,
align: 'center',
},
{
field: 'itemOrProduct',
title: '物料/产品',
width: 110,
align: 'center',
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.MES_MD_ITEM_OR_PRODUCT },
},
},
{
field: 'quantity',
title: '用量比例',
width: 100,
align: 'center',
},
{
field: 'remark',
title: '备注',
minWidth: 140,
align: 'center',
},
];
if (!isReadOnly) { if (!isReadOnly) {
columns.push({ columns.push({

View File

@ -36,7 +36,9 @@ const getTitle = computed(() => {
}; };
return titles[formMode.value]; return titles[formMode.value];
}); });
const currentItemOrProduct = computed(() => formData.value?.itemOrProduct || ''); // / const currentItemOrProduct = computed(
() => formData.value?.itemOrProduct || '',
); // /
const [Form, formApi] = useVbenForm({ const [Form, formApi] = useVbenForm({
commonConfig: { commonConfig: {

View File

@ -98,29 +98,80 @@ watch(
<Button type="primary" @click="handleSave"></Button> <Button type="primary" @click="handleSave"></Button>
</div> </div>
<div class="grid grid-cols-5 gap-x-5 gap-y-3"> <div class="grid grid-cols-5 gap-x-5 gap-y-3">
<Checkbox v-model:checked="formData.produceDateFlag" :disabled="isReadOnly">生产日期</Checkbox> <Checkbox
<Checkbox v-model:checked="formData.qualityStatusFlag" :disabled="isReadOnly">质量状态</Checkbox> v-model:checked="formData.produceDateFlag"
:disabled="isReadOnly"
>
生产日期
</Checkbox>
<Checkbox
v-model:checked="formData.qualityStatusFlag"
:disabled="isReadOnly"
>
质量状态
</Checkbox>
<template v-if="itemOrProduct === MesItemOrProductEnum.ITEM.value"> <template v-if="itemOrProduct === MesItemOrProductEnum.ITEM.value">
<Checkbox v-model:checked="formData.vendorFlag" :disabled="isReadOnly">供应商</Checkbox> <Checkbox v-model:checked="formData.vendorFlag" :disabled="isReadOnly">
<Checkbox v-model:checked="formData.purchaseOrderCodeFlag" :disabled="isReadOnly"> 供应商
</Checkbox>
<Checkbox
v-model:checked="formData.purchaseOrderCodeFlag"
:disabled="isReadOnly"
>
采购订单编号 采购订单编号
</Checkbox> </Checkbox>
<Checkbox v-model:checked="formData.lotNumberFlag" :disabled="isReadOnly">生产批号</Checkbox> <Checkbox
<Checkbox v-model:checked="formData.expireDateFlag" :disabled="isReadOnly">有效期</Checkbox> v-model:checked="formData.lotNumberFlag"
<Checkbox v-model:checked="formData.receiptDateFlag" :disabled="isReadOnly">入库日期</Checkbox> :disabled="isReadOnly"
>
生产批号
</Checkbox>
<Checkbox
v-model:checked="formData.expireDateFlag"
:disabled="isReadOnly"
>
有效期
</Checkbox>
<Checkbox
v-model:checked="formData.receiptDateFlag"
:disabled="isReadOnly"
>
入库日期
</Checkbox>
</template> </template>
<template v-if="itemOrProduct === MesItemOrProductEnum.PRODUCT.value"> <template v-if="itemOrProduct === MesItemOrProductEnum.PRODUCT.value">
<Checkbox v-model:checked="formData.clientFlag" :disabled="isReadOnly">客户</Checkbox> <Checkbox v-model:checked="formData.clientFlag" :disabled="isReadOnly">
<Checkbox v-model:checked="formData.salesOrderCodeFlag" :disabled="isReadOnly"> 客户
</Checkbox>
<Checkbox
v-model:checked="formData.salesOrderCodeFlag"
:disabled="isReadOnly"
>
销售订单编号 销售订单编号
</Checkbox> </Checkbox>
<Checkbox v-model:checked="formData.workorderFlag" :disabled="isReadOnly">生产工单</Checkbox> <Checkbox
<Checkbox v-model:checked="formData.taskFlag" :disabled="isReadOnly">生产任务</Checkbox> v-model:checked="formData.workorderFlag"
<Checkbox v-model:checked="formData.workstationFlag" :disabled="isReadOnly">工作站</Checkbox> :disabled="isReadOnly"
<Checkbox v-model:checked="formData.toolFlag" :disabled="isReadOnly">工具</Checkbox> >
<Checkbox v-model:checked="formData.moldFlag" :disabled="isReadOnly">模具</Checkbox> 生产工单
</Checkbox>
<Checkbox v-model:checked="formData.taskFlag" :disabled="isReadOnly">
生产任务
</Checkbox>
<Checkbox
v-model:checked="formData.workstationFlag"
:disabled="isReadOnly"
>
工作站
</Checkbox>
<Checkbox v-model:checked="formData.toolFlag" :disabled="isReadOnly">
工具
</Checkbox>
<Checkbox v-model:checked="formData.moldFlag" :disabled="isReadOnly">
模具
</Checkbox>
</template> </template>
</div> </div>
</Spin> </Spin>

View File

@ -47,9 +47,21 @@ const [Form, formApi] = useVbenForm({
}, },
layout: 'horizontal', layout: 'horizontal',
schema: [ schema: [
{ fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, {
{ fieldName: 'itemId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, fieldName: 'id',
{ fieldName: 'bomItemId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{
fieldName: 'itemId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{
fieldName: 'bomItemId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{ {
fieldName: 'bomItemCode', fieldName: 'bomItemCode',
label: 'BOM 物料编码', label: 'BOM 物料编码',
@ -164,7 +176,9 @@ async function submitForm() {
formLoading.value = true; formLoading.value = true;
try { try {
const data = (await formApi.getValues()) as MesMdProductBomApi.ProductBom; const data = (await formApi.getValues()) as MesMdProductBomApi.ProductBom;
await (formData.value?.id ? updateProductBom(data) : createProductBom(data)); await (formData.value?.id
? updateProductBom(data)
: createProductBom(data));
formOpen.value = false; formOpen.value = false;
message.success($t('ui.actionMessage.operationSuccess')); message.success($t('ui.actionMessage.operationSuccess'));
await getList(); await getList();

View File

@ -4,7 +4,16 @@ import type { MesMdProductSopApi } from '#/api/mes/md/item/productSop';
import { computed, markRaw, ref, watch } from 'vue'; import { computed, markRaw, ref, watch } from 'vue';
import { Button, Card, Empty, Image, message, Modal, Popconfirm, Spin } from 'ant-design-vue'; import {
Button,
Card,
Empty,
Image,
message,
Modal,
Popconfirm,
Spin,
} from 'ant-design-vue';
import { useVbenForm, z } from '#/adapter/form'; import { useVbenForm, z } from '#/adapter/form';
import { import {
@ -23,9 +32,7 @@ import { ImageUpload } from '#/components/upload';
import { ProProcessSelect } from '#/views/mes/pro/process/components'; import { ProProcessSelect } from '#/views/mes/pro/process/components';
type MediaKind = 'SIP' | 'SOP'; type MediaKind = 'SIP' | 'SOP';
type MediaItem = type MediaItem = MesMdProductSipApi.ProductSip | MesMdProductSopApi.ProductSop;
| MesMdProductSipApi.ProductSip
| MesMdProductSopApi.ProductSop;
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -56,8 +63,16 @@ const [Form, formApi] = useVbenForm({
}, },
layout: 'horizontal', layout: 'horizontal',
schema: [ schema: [
{ fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, {
{ fieldName: 'itemId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, fieldName: 'id',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{
fieldName: 'itemId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{ {
fieldName: 'title', fieldName: 'title',
label: '标题', label: '标题',
@ -152,7 +167,9 @@ async function submitForm() {
formLoading.value = true; formLoading.value = true;
try { try {
const data = (await formApi.getValues()) as MediaItem; const data = (await formApi.getValues()) as MediaItem;
await (formData.value?.id ? updateApi()(data as any) : createApi()(data as any)); await (formData.value?.id
? updateApi()(data as any)
: createApi()(data as any));
formOpen.value = false; formOpen.value = false;
message.success('保存成功'); message.success('保存成功');
await getList(); await getList();
@ -181,12 +198,7 @@ watch(
<template> <template>
<div> <div>
<Button <Button v-if="!isReadOnly" class="mb-3" type="primary" @click="openForm()">
v-if="!isReadOnly"
class="mb-3"
type="primary"
@click="openForm()"
>
添加 {{ title }} 添加 {{ title }}
</Button> </Button>
<Spin :spinning="loading"> <Spin :spinning="loading">
@ -215,8 +227,13 @@ watch(
{{ item.description }} {{ item.description }}
</div> </div>
<div v-if="!isReadOnly" class="mt-2 flex justify-end"> <div v-if="!isReadOnly" class="mt-2 flex justify-end">
<Button type="link" size="small" @click="openForm(item)"></Button> <Button type="link" size="small" @click="openForm(item)">
<Popconfirm title="确认删除该数据吗?" @confirm="handleDelete(item.id!)"> 编辑
</Button>
<Popconfirm
title="确认删除该数据吗?"
@confirm="handleDelete(item.id!)"
>
<Button danger type="link" size="small">删除</Button> <Button danger type="link" size="small">删除</Button>
</Popconfirm> </Popconfirm>
</div> </div>

View File

@ -40,7 +40,8 @@ const allList = ref<MesMdItemTypeApi.ItemType[]>([]); // 物料分类列表
const itemTypeTree = ref<ItemTypeNode[]>([]); // const itemTypeTree = ref<ItemTypeNode[]>([]); //
const selectedItem = ref<MesMdItemTypeApi.ItemType>(); // const selectedItem = ref<MesMdItemTypeApi.ItemType>(); //
const selectValue = computed({ // const selectValue = computed({
//
get: () => props.modelValue, get: () => props.modelValue,
set: (value: number | undefined) => { set: (value: number | undefined) => {
emit('update:modelValue', value); emit('update:modelValue', value);
@ -48,7 +49,9 @@ const selectValue = computed({ // 选择器绑定值
}); });
/** 递归将有子节点的分支节点标记为 disabled */ /** 递归将有子节点的分支节点标记为 disabled */
function markParentsDisabled(nodes: MesMdItemTypeApi.ItemType[]): ItemTypeNode[] { function markParentsDisabled(
nodes: MesMdItemTypeApi.ItemType[],
): ItemTypeNode[] {
return nodes.map((node) => { return nodes.map((node) => {
const children = node.children?.length const children = node.children?.length
? markParentsDisabled(node.children) ? markParentsDisabled(node.children)
@ -64,7 +67,9 @@ function markParentsDisabled(nodes: MesMdItemTypeApi.ItemType[]): ItemTypeNode[]
/** 根据当前值同步 tooltip 展示的分类详情 */ /** 根据当前值同步 tooltip 展示的分类详情 */
function syncSelectedItem(value: number | undefined) { function syncSelectedItem(value: number | undefined) {
selectedItem.value = selectedItem.value =
value === undefined ? undefined : allList.value.find((item) => item.id === value); value === undefined
? undefined
: allList.value.find((item) => item.id === value);
} }
/** 除 v-model 外,额外抛出完整分类对象给业务表单使用 */ /** 除 v-model 外,额外抛出完整分类对象给业务表单使用 */

View File

@ -33,7 +33,8 @@ const allList = ref<MesMdUnitMeasureApi.UnitMeasure[]>([]); // 计量单位列
const filteredList = ref<MesMdUnitMeasureApi.UnitMeasure[]>([]); // const filteredList = ref<MesMdUnitMeasureApi.UnitMeasure[]>([]); //
const selectedItem = ref<MesMdUnitMeasureApi.UnitMeasure>(); // const selectedItem = ref<MesMdUnitMeasureApi.UnitMeasure>(); //
const selectValue = computed({ // const selectValue = computed({
//
get: () => props.modelValue, get: () => props.modelValue,
set: (value: number | undefined) => { set: (value: number | undefined) => {
emit('update:modelValue', value); emit('update:modelValue', value);
@ -46,14 +47,16 @@ function handleFilter(input: string, option: any) {
const item = option?.item as MesMdUnitMeasureApi.UnitMeasure | undefined; const item = option?.item as MesMdUnitMeasureApi.UnitMeasure | undefined;
return Boolean( return Boolean(
item?.name?.toLowerCase().includes(keyword) || item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword), item?.code?.toLowerCase().includes(keyword),
); );
} }
/** 根据当前值同步 tooltip 展示的计量单位详情 */ /** 根据当前值同步 tooltip 展示的计量单位详情 */
function syncSelectedItem(value: number | undefined) { function syncSelectedItem(value: number | undefined) {
selectedItem.value = selectedItem.value =
value === undefined ? undefined : allList.value.find((item) => item.id === value); value === undefined
? undefined
: allList.value.find((item) => item.id === value);
} }
/** 除 v-model 外,额外抛出完整计量单位对象给业务表单使用 */ /** 除 v-model 外,额外抛出完整计量单位对象给业务表单使用 */
@ -84,7 +87,9 @@ onMounted(async () => {
<div>编码{{ selectedItem.code || '-' }}</div> <div>编码{{ selectedItem.code || '-' }}</div>
<div>名称{{ selectedItem.name || '-' }}</div> <div>名称{{ selectedItem.name || '-' }}</div>
<div>是否主单位{{ selectedItem.primaryFlag ? '是' : '否' }}</div> <div>是否主单位{{ selectedItem.primaryFlag ? '是' : '否' }}</div>
<div v-if="!selectedItem.primaryFlag && selectedItem.changeRate != null"> <div
v-if="!selectedItem.primaryFlag && selectedItem.changeRate != null"
>
换算比例{{ selectedItem.changeRate }} 换算比例{{ selectedItem.changeRate }}
</div> </div>
<div v-if="selectedItem.remark">{{ selectedItem.remark }}</div> <div v-if="selectedItem.remark">{{ selectedItem.remark }}</div>

View File

@ -132,7 +132,10 @@ async function resetQueryState() {
} }
/** 打开供应商选择弹窗 */ /** 打开供应商选择弹窗 */
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
@ -155,7 +158,10 @@ function handleConfirm() {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]); emit(
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -37,13 +37,18 @@ const hovering = ref(false); // 是否悬停
const selectedItem = ref<MesMdVendorApi.Vendor>(); // const selectedItem = ref<MesMdVendorApi.Vendor>(); //
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( // const showClear = computed(
() => props.allowClear && !props.disabled && hovering.value && props.modelValue != null, //
() =>
props.allowClear &&
!props.disabled &&
hovering.value &&
props.modelValue !== null,
); );
/** 根据供应商编号回显选择器 */ /** 根据供应商编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id == null) { if (id === null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -51,7 +56,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getVendor(id); selectedItem.value = await getVendor(id as number);
} catch (error) { } catch (error) {
console.error('[MdVendorSelect] resolveItemById failed:', error); console.error('[MdVendorSelect] resolveItemById failed:', error);
} }
@ -83,8 +88,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue == null ? [] : [props.modelValue]; const selectedIds = props.modelValue === null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false }); dialogRef.value?.open(selectedIds as number[], { multiple: false });
} }
/** 回填选中的供应商 */ /** 回填选中的供应商 */

View File

@ -44,14 +44,16 @@ function handleFilter(input: string, option: any) {
const item = option?.item as MesMdWorkshopApi.Workshop | undefined; const item = option?.item as MesMdWorkshopApi.Workshop | undefined;
return Boolean( return Boolean(
item?.name?.toLowerCase().includes(keyword) || item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword), item?.code?.toLowerCase().includes(keyword),
); );
} }
/** 根据当前值同步 tooltip 展示的车间详情 */ /** 根据当前值同步 tooltip 展示的车间详情 */
function syncSelectedItem(value: number | undefined) { function syncSelectedItem(value: number | undefined) {
selectedItem.value = selectedItem.value =
value === undefined ? undefined : allList.value.find((item) => item.id === value); value === undefined
? undefined
: allList.value.find((item) => item.id === value);
} }
/** 除 v-model 外,额外抛出完整车间对象给业务表单使用 */ /** 除 v-model 外,额外抛出完整车间对象给业务表单使用 */
@ -80,7 +82,11 @@ onMounted(async () => {
<div v-if="selectedItem" class="leading-6"> <div v-if="selectedItem" class="leading-6">
<div>编码{{ selectedItem.code || '-' }}</div> <div>编码{{ selectedItem.code || '-' }}</div>
<div>名称{{ selectedItem.name || '-' }}</div> <div>名称{{ selectedItem.name || '-' }}</div>
<div>面积{{ selectedItem.area != null ? `${selectedItem.area}` : '-' }}</div> <div>
面积{{
selectedItem.area != null ? `${selectedItem.area}` : '-'
}}
</div>
<div>负责人{{ selectedItem.chargeUserName || '-' }}</div> <div>负责人{{ selectedItem.chargeUserName || '-' }}</div>
</div> </div>
</template> </template>

View File

@ -69,7 +69,11 @@ async function handleCheckboxChange({
} }
/** 处理全选变化 */ /** 处理全选变化 */
function handleCheckboxAll({ records }: { records: MesMdWorkstationApi.Workstation[] }) { function handleCheckboxAll({
records,
}: {
records: MesMdWorkstationApi.Workstation[];
}) {
if (syncingSingleSelection.value) { if (syncingSingleSelection.value) {
return; return;
} }
@ -143,7 +147,10 @@ async function resetQueryState() {
} }
/** 打开工作站选择弹窗 */ /** 打开工作站选择弹窗 */
async function openModal(selectedIds?: number[], options?: { multiple?: boolean }) { async function openModal(
selectedIds?: number[],
options?: { multiple?: boolean },
) {
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
@ -166,7 +173,10 @@ function handleConfirm() {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]); emit(
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -39,13 +39,18 @@ const hovering = ref(false); // 是否悬停
const selectedItem = ref<MesMdWorkstationApi.Workstation>(); // const selectedItem = ref<MesMdWorkstationApi.Workstation>(); //
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( // const showClear = computed(
() => props.allowClear && !props.disabled && hovering.value && props.modelValue != null, //
() =>
props.allowClear &&
!props.disabled &&
hovering.value &&
props.modelValue !== null,
); );
/** 根据工作站编号回显选择器 */ /** 根据工作站编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id == null) { if (id === null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -53,7 +58,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getWorkstation(id); selectedItem.value = await getWorkstation(id as number);
} catch (error) { } catch (error) {
console.error('[MdWorkstationSelect] resolveItemById failed:', error); console.error('[MdWorkstationSelect] resolveItemById failed:', error);
} }
@ -85,8 +90,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue == null ? [] : [props.modelValue]; const selectedIds = props.modelValue === null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds, { multiple: false }); dialogRef.value?.open(selectedIds as number[], { multiple: false });
} }
/** 回填选中的工作站 */ /** 回填选中的工作站 */

View File

@ -43,7 +43,11 @@ const [Form, formApi] = useVbenForm({
}, },
layout: 'horizontal', layout: 'horizontal',
schema: [ schema: [
{ fieldName: 'workstationId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, {
fieldName: 'workstationId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{ {
fieldName: 'machineryId', fieldName: 'machineryId',
label: '设备', label: '设备',
@ -140,7 +144,8 @@ async function submitForm() {
} }
formLoading.value = true; formLoading.value = true;
try { try {
const data = (await formApi.getValues()) as MesMdWorkstationMachineApi.WorkstationMachine; const data =
(await formApi.getValues()) as MesMdWorkstationMachineApi.WorkstationMachine;
await createWorkstationMachine(data); await createWorkstationMachine(data);
formOpen.value = false; formOpen.value = false;
message.success($t('ui.actionMessage.operationSuccess')); message.success($t('ui.actionMessage.operationSuccess'));

View File

@ -45,8 +45,16 @@ const [Form, formApi] = useVbenForm({
}, },
layout: 'horizontal', layout: 'horizontal',
schema: [ schema: [
{ fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, {
{ fieldName: 'workstationId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, fieldName: 'id',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{
fieldName: 'workstationId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{ {
fieldName: 'toolTypeId', fieldName: 'toolTypeId',
label: '工具类型', label: '工具类型',
@ -149,8 +157,11 @@ async function submitForm() {
} }
formLoading.value = true; formLoading.value = true;
try { try {
const data = (await formApi.getValues()) as MesMdWorkstationToolApi.WorkstationTool; const data =
await (formData.value?.id ? updateWorkstationTool(data) : createWorkstationTool(data)); (await formApi.getValues()) as MesMdWorkstationToolApi.WorkstationTool;
await (formData.value?.id
? updateWorkstationTool(data)
: createWorkstationTool(data));
formOpen.value = false; formOpen.value = false;
message.success($t('ui.actionMessage.operationSuccess')); message.success($t('ui.actionMessage.operationSuccess'));
await getList(); await getList();

View File

@ -45,8 +45,16 @@ const [Form, formApi] = useVbenForm({
}, },
layout: 'horizontal', layout: 'horizontal',
schema: [ schema: [
{ fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, {
{ fieldName: 'workstationId', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, fieldName: 'id',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{
fieldName: 'workstationId',
component: 'Input',
dependencies: { triggerFields: [''], show: () => false },
},
{ {
fieldName: 'postId', fieldName: 'postId',
label: '岗位', label: '岗位',
@ -145,8 +153,11 @@ async function submitForm() {
} }
formLoading.value = true; formLoading.value = true;
try { try {
const data = (await formApi.getValues()) as MesMdWorkstationWorkerApi.WorkstationWorker; const data =
await (formData.value?.id ? updateWorkstationWorker(data) : createWorkstationWorker(data)); (await formApi.getValues()) as MesMdWorkstationWorkerApi.WorkstationWorker;
await (formData.value?.id
? updateWorkstationWorker(data)
: createWorkstationWorker(data));
formOpen.value = false; formOpen.value = false;
message.success($t('ui.actionMessage.operationSuccess')); message.success($t('ui.actionMessage.operationSuccess'));
await getList(); await getList();

View File

@ -44,14 +44,16 @@ function handleFilter(input: string, option: any) {
const item = option?.item as MesProProcessApi.Process | undefined; const item = option?.item as MesProProcessApi.Process | undefined;
return Boolean( return Boolean(
item?.name?.toLowerCase().includes(keyword) || item?.name?.toLowerCase().includes(keyword) ||
item?.code?.toLowerCase().includes(keyword), item?.code?.toLowerCase().includes(keyword),
); );
} }
/** 根据当前值同步 tooltip 展示的工序详情 */ /** 根据当前值同步 tooltip 展示的工序详情 */
function syncSelectedItem(value: number | undefined) { function syncSelectedItem(value: number | undefined) {
selectedItem.value = selectedItem.value =
value === undefined ? undefined : allList.value.find((item) => item.id === value); value === undefined
? undefined
: allList.value.find((item) => item.id === value);
} }
/** 除 v-model 外,额外抛出完整工序对象给业务表单使用 */ /** 除 v-model 外,额外抛出完整工序对象给业务表单使用 */

Some files were not shown because too many files have changed in this diff Show More