nursing_unit_vue/src/views/services/serviceDirective/ConfigServiceDirectiveList.vue

1271 lines
48 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="p-2">
<!--查询区域-->
<div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol"
:wrapper-col="wrapperCol">
<a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="directiveName">
<template #label><span title="服务指令">服务指令</span></template>
<JInput v-model:value="queryParam.directiveName" placeholder="请输入服务指令名称" allowClear />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset"
style="margin-left: 8px">重置</a-button>
</a-col>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div>
<div
style="width:350px;float: left;height:80vh; background: white; overflow:auto;position: relative;margin-right: 14px;border-radius: 8px;">
<div
style="position: absolute; top: 8px; right: 8px; z-index: 1; background: white; padding: 0px; border-radius: 4px;">
<a-radio-group v-model:value="filterIzEnabled" @change="searchQuery">
<a-radio-button value="all">全部</a-radio-button>
<a-radio-button value="enabled">启用</a-radio-button>
</a-radio-group>
</div>
<a-empty v-if="!treeLoading && treeLoading" />
<a-button v-if="!treeLoading && treeData.length < 1" type="link" class="btnPrivate" @click="addInstruction"
preIcon="ant-design:plus-outlined">新增分类标签</a-button>
<a-menu v-model:openKeys="openKeys" v-model:selectedKeys="selectedKeys" style="width: 100%;margin-top: 50px;"
mode="inline" @openChange="onOpenChange">
<template v-for="item in treeData">
<!-- 第一层判断是否有下级 -->
<a-sub-menu v-if="item.children && item.children.length > 0" :key="item.key"
@titleClick="handleTreeSelect([], { node: item })">
<template #icon>
<Icon :icon="item.icon" :size="20" v-if="item.icon" />
<Icon icon="ant-design:appstore-add-outlined" :size="20" v-else />
</template>
<template #title>
<span @mouseenter="item.showContent = true" @mouseleave="item.showContent = false">{{ item?.title }}
<span v-if="item?.izEnabled == 'N' && item.level != 5" style="color:red;">(已停用)</span>
<span v-show="item.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[item?.key]?.open" @openChange="onMenuOpenChange(item.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(item)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(item) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon v-show="item.showContent" style="color:#1890FF;" :icon="iconClass(item.level)"
class="action-icon" @mouseenter="onNodeIconEnter(item, item.children)"
@mouseleave="onNodeIconLeave(item)" />
</a-dropdown>
</span>
</span>
</template>
<!-- 第二层判断是否有下级 -->
<template v-for="child in item.children">
<a-sub-menu :key="child.key" v-if="child.children && child.children.length > 0"
@titleClick="handleTreeSelect([], { node: child })">
<template #title>
<span @mouseenter="child.showContent = true" @mouseleave="child.showContent = false">{{ child?.title
}}
<span v-if="child?.izEnabled == 'N' && child.level != 5" style="color:red;">(已停用)</span>
<span v-show="child.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[child?.key]?.open"
@openChange="onMenuOpenChange(child.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(child)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(child) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon style="color:#1890FF;" :icon="iconClass(child.level)" class="action-icon"
@mouseenter="onNodeIconEnter(child, child.children)" @mouseleave="onNodeIconLeave(child)" />
</a-dropdown>
</span>
</span>
</template>
<!-- 第三层判断是否有下级 -->
<template v-for="childThree in child.children">
<a-sub-menu :key="childThree.key" v-if="childThree.children && childThree.children.length > 0"
@titleClick="handleTreeSelect([], { node: childThree })">
<template #title>
<span @mouseenter="childThree.showContent = true"
@mouseleave="childThree.showContent = false">{{ childThree?.title }}
<span v-if="childThree?.izEnabled == 'N' && childThree.level != 5"
style="color:red;">(已停用)</span>
<span v-show="childThree.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[childThree?.key]?.open"
@openChange="onMenuOpenChange(childThree.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(childThree)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(childThree) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon style="color:#1890FF;" :icon="iconClass(childThree.level)" class="action-icon"
@mouseenter="onNodeIconEnter(childThree, childThree.children)"
@mouseleave="onNodeIconLeave(childThree)" />
</a-dropdown>
</span>
</span>
</template>
<!-- 第四层判断是否有下级 -->
<a-menu-item :key="childFour.key" v-for="childFour in childThree.children"
@click="handleTreeSelect([], { node: childFour })">
<span @mouseenter="childFour.showContent = true" @mouseleave="childFour.showContent = false"
class="auto-wrap">{{ childFour?.title + '(' + childFour?.cycleTypeName + ')' }}
<span v-if="childFour?.izEnabled == 'N' && childFour.level != 5"
style="color:red;">(已停用)</span>
<span v-show="childFour.showContent">
<a-dropdown :open="menuState[childFour?.key]?.open"
@openChange="onMenuOpenChange(childFour.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(childFour)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(childFour) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<Icon style="color:#1890FF;" :icon="iconClass(childFour.level)" class="action-icon"
@mouseenter="onNodeIconEnter(childFour, childFour.children)"
@mouseleave="onNodeIconLeave(childFour)" />
</a-dropdown>
</span>
</span>
</a-menu-item>
</a-sub-menu>
<a-menu-item :key="childThree.key" v-if="!childThree.children || childThree.children.length < 1"
@click="handleTreeSelect([], { node: childThree })">
<span @mouseenter="childThree.showContent = true" @mouseleave="childThree.showContent = false">{{
childThree?.title }}
<span v-if="childThree?.izEnabled == 'N' && childThree.level != 5"
style="color:red;">(已停用)</span>
<span v-show="childThree.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[childThree?.key]?.open"
@openChange="onMenuOpenChange(childThree.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(childThree)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(childThree) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon style="color:#1890FF;" :icon="iconClass(childThree.level)" class="action-icon"
@mouseenter="onNodeIconEnter(childThree, childThree.children)"
@mouseleave="onNodeIconLeave(childThree)" />
</a-dropdown>
</span>
</span>
</a-menu-item>
</template>
</a-sub-menu>
<a-menu-item :key="child.key" v-if="!child.children || child.children.length < 1"
@click="handleTreeSelect([], { node: child })">
<span @mouseenter="child.showContent = true" @mouseleave="child.showContent = false">{{ child?.title
}}
<span v-if="child?.izEnabled == 'N' && child.level != 5" style="color:red;">(已停用)</span>
<span v-show="child.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[child?.key]?.open" @openChange="onMenuOpenChange(child.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(child)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(child) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon style="color:#1890FF;" :icon="iconClass(child.level)" class="action-icon"
@mouseenter="onNodeIconEnter(child, child.children)" @mouseleave="onNodeIconLeave(child)" />
</a-dropdown>
</span>
</span>
</a-menu-item>
</template>
</a-sub-menu>
<a-menu-item :key="item.key" v-if="!item.children || item.children.length < 1"
@click="handleTreeSelect([], { node: item })">
<template #icon>
<Icon :icon="item.icon" :size="20" v-if="item.icon" />
<Icon icon="ant-design:appstore-add-outlined" :size="20" v-else />
</template>
<span @mouseenter="item.showContent = true" @mouseleave="item.showContent = false">{{ item?.title }}
<span v-if="item?.izEnabled == 'N' && item.level != 5" style="color:red;">(已停用)</span>
<span v-show="item.showContent">
<!-- 下拉菜单 -->
<a-dropdown :open="menuState[item?.key]?.open" @openChange="onMenuOpenChange(item.key, $event)">
<template #overlay>
<a-menu>
<a-menu-item v-for="itemMenu in menuItems(item)" :key="itemMenu.key"
:disabled="!itemMenu.canAdd" class="nu-menu-item"
@click="() => { closeAllMenus(); itemMenu.action(item) }">
<Icon style="color:#1890FF;" :icon="itemMenu.icon" class="action-icon"></Icon>
<span style=" color:#1890FF;margin-left: 5px;">{{ itemMenu.label }}</span>
</a-menu-item>
</a-menu>
</template>
<!-- 图标 -->
<Icon v-show="item.showContent" style="color:#1890FF;" :icon="iconClass(item.level)"
class="action-icon" @mouseenter="onNodeIconEnter(item, item.children)"
@mouseleave="onNodeIconLeave(item)" />
</a-dropdown>
</span>
</span>
</a-menu-item>
</template>
</a-menu>
</div>
<div style="width:calc(100% - 370px);float: left; background-color: white;border-radius: 8px;"
class="container-height">
<!--引用表格-->
<BasicTable @register="registerTable">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="directiveSyncCodeMangeFunc" v-show="!!ownOrgCode"
preIcon="ant-design:setting-outlined">镜像码管理</a-button>
<a-button type="primary" @click="handleDirectiveMainOpen" preIcon="ant-design:profile-outlined">
服务指令库
</a-button>
<span style="margin-top: -3px;margin-left: -3px;z-index: 999;">
<a-tooltip placement="right">
<template #title>
<span>
通过分享服务指令库的镜像码,可将本平台的服务指令拉取至其他平台同步使用。
<br />
也可复制其他平台的镜像码,将其他平台的服务指令拉到至本平台同步使用。
</span>
</template>
<QuestionCircleOutlined style="color: red;" />
</a-tooltip>
</span>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<template v-slot:bodyCell="{ column, record, index, text }">
<!-- 音频播放控制 -->
<template v-if="column.dataIndex === 'mp3File'">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else type="link" class="btnPrivate" @click="openAudioModal(text)">播放</a-button>
</template>
<template v-if="column.dataIndex === 'mp4File'">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else type="link" class="btnPrivate" @click="openVideoModal(text)">播放</a-button>
</template>
<template v-if="column.dataIndex === 'bodyTagList'">
<span :title="text.map((item) => item.tagName).join('、')">{{text.map((item) =>
item.tagName).join('、')}}</span>
</template>
<template v-if="column.dataIndex === 'emotionTagList'">
<span :title="text.map((item) => item.tagName).join('、')">{{text.map((item) =>
item.tagName).join('、')}}</span>
</template>
</template>
</BasicTable>
</div>
</div>
<!-- 表单区域 -->
<ConfigServiceDirectiveModal ref="registerModal" @success="handleSuccess" :isMain="true">
</ConfigServiceDirectiveModal>
</div>
<!-- 分类标签 -->
<a-drawer title="分类标签" width="60vw" :open="insTagOpen" @close="onInsTagClose">
<template #footer>
<a-button type="primary" @click="onInsTagClose" style="float: right;">关闭</a-button>
</template>
<InstructionTag v-if="insTagOpen"></InstructionTag>
</a-drawer>
<!-- 服务类别 -->
<a-drawer title="服务类别" width="60vw" :open="categoryOpen" @close="onCategoryClose">
<template #footer>
<a-button type="primary" @click="onCategoryClose" style="float: right;">关闭</a-button>
</template>
<ConfigServiceCategoryList v-if="categoryOpen"></ConfigServiceCategoryList>
</a-drawer>
<!-- 服务类型 -->
<a-drawer title="服务类型" width="60vw" :open="typeOpen" @close="onTypeClose">
<template #footer>
<a-button type="primary" @click="onTypeClose" style="float: right;">关闭</a-button>
</template>
<ConfigServiceTypeList v-if="typeOpen"></ConfigServiceTypeList>
</a-drawer>
<!-- 批量新增 -->
<a-drawer title="批量新增" width="80vw" :open="batchAddOpen" @close="onBatchAddClose"
:footer-style="{ textAlign: 'right' }" :bodyStyle="{ padding: '0' }">
<template #footer>
<a-button type="primary" @click="onBatchAddClose" style="margin-right: 10px;">关闭</a-button>
<a-button type="primary" @click="onBatchAddSubmit">确认</a-button>
</template>
<DirectiveMediaList ref="dmRef" :ownOrgCode="ownOrgCode" v-if="batchAddOpen"></DirectiveMediaList>
</a-drawer>
<!-- 停用指令 -->
<a-drawer title="停用指令" width="85vw" :open="abnormalListOpen" @close="onAbnormalListClose"
:footer-style="{ textAlign: 'right' }" :bodyStyle="{ padding: '0' }">
<template #footer>
<a-button type="primary" @click="onAbnormalListClose" style="margin-right: 10px;">关闭</a-button>
</template>
<AbnormalDirectiveList ref="abnormalListRef" v-if="abnormalListOpen"></AbnormalDirectiveList>
</a-drawer>
<!-- 差异指令 -->
<a-drawer v-model:visible="newDirectiveVisible" title="差异指令" width="85vw" :footer-style="{ textAlign: 'right' }"
:bodyStyle="{ height: '80vh', display: 'flex', padding: '0', flexDirection: 'column', overflow: 'auto' }"
wrapClassName="org-list-modal" @cancel="handleCancelNewDirective">
<template #footer>
<a-button @click="handleCancelNewDirective" type="primary">关闭</a-button>
<!-- <a-button @click="handleAsyncNewDirective" type="primary">同步</a-button> -->
</template>
<div style="padding:0px 8px;">
<CanAddDirectiveList ref="canAddDirectiveRef" :directiveMainOrgInfo="directiveMainOrgInfo"
:existDirectiveIds="existDirectiveIds" @refreshExistIds="refreshDMExistedIds"></CanAddDirectiveList>
</div>
</a-drawer>
<!-- 镜像码管理 -->
<a-drawer v-model:visible="syncCodeVisible" title="镜像码管理" width="40vw" :footer-style="{ textAlign: 'right' }"
:bodyStyle="{ height: '100%', display: 'flex', padding: '14px', flexDirection: 'column', overflow: 'auto' }"
wrapClassName="org-list-modal" @cancel="syncCodeVisible = false">
<template #footer>
<a-button @click="syncCodeVisible = false" type="primary">关闭</a-button>
</template>
<a-spin :spinning="!syncCode">
<div style="padding: 14px; background-color: white; display: flex; align-items: center;">
<a-row style="width: 100%;">
<a-col :span="16" style="display: flex; align-items: center;">
<span style="font-weight: bold;">镜像码:{{ syncCode }}</span><span></span>
</a-col>
<a-col :push="3" :span="5" style="display: flex; align-items: center;">
<a-button @click="copySyncCodeFunc()" type="primary" style="margin-right: 8px;">复制</a-button>
<a-button @click="updateSyncCodeFunc()" type="primary">更新</a-button>
</a-col>
</a-row>
</div>
</a-spin>
</a-drawer>
<!-- 音频播放 -->
<a-modal v-model:visible="showAudioModal" title="音频播放" :footer="null" @cancel="closeAudioModal"
:bodyStyle="{ padding: '0', maxHeight: '80vh', overflow: 'auto' }" :keyboard="true">
<audio ref="audioPlayer" controls style="width: 100%; display: block; margin: 20px auto;">
<source :src="audioUrl">
您的浏览器不支持音频播放。
</audio>
</a-modal>
<!-- 视频播放 -->
<a-modal v-model:visible="showVideoModal" title="视频播放" :footer="null" @cancel="closeVideoModal"
:bodyStyle="{ padding: '0', maxHeight: '80vh', overflow: 'auto' }">
<video ref="videoPlayer" controls style="width: 100%; max-height: 70vh; display: block; margin: 0 auto;">
<source :src="videoUrl">
您的浏览器不支持视频播放
</video>
</a-modal>
<!-- 分类标签 -->
<InstructionTagModal ref="insRegisterModal" @success="reloadTree"></InstructionTagModal>
<ConfigServiceCategoryModal ref="catRegisterModal" @success="reloadTree"></ConfigServiceCategoryModal>
<ConfigServiceTypeModal ref="typRegisterModal" @success="reloadTree"></ConfigServiceTypeModal>
</template>
<script lang="ts" name="serviceDirective-configServiceDirective" setup>
import { ref, reactive, watch, onMounted, computed, nextTick } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, superQuerySchema } from './ConfigServiceDirective.data';
import { list, batchAdd, deleteOne, batchDelete, getImportUrl, getExportUrl, tree, getSyncCode, updateSyncCode } from './ConfigServiceDirective.api';
import ConfigServiceDirectiveModal from './components/ConfigServiceDirectiveModal.vue'
import JInput from "/@/components/Form/src/jeecg/components/JInput.vue";
import { cloneDeep } from "lodash-es";
import ConfigServiceCategoryList from '../serviceCategory/ConfigServiceCategoryList.vue';
import InstructionTag from '../instructiontag/InstructionTag.vue';
import ConfigServiceTypeList from '../serviceType/ConfigServiceTypeList.vue';
import { Empty } from 'ant-design-vue';
import InstructionTagModal from '/@/views/services/InstructionTag/components/InstructionTagModal.vue'
import ConfigServiceCategoryModal from '/@/views/services/serviceCategory/components//ConfigServiceCategoryModal.vue'
import ConfigServiceTypeModal from '/@/views/services/serviceType/components//ConfigServiceTypeModal.vue'
import DirectiveMediaList from '/@/views/services/directivemedia/DirectiveMediaList.vue'
import { getOrgInfo } from '@/api/common/api'
import { useMessage } from '/@/hooks/web/useMessage';
import AbnormalDirectiveList from './components/AbnormalDirectiveList.vue'
import CompareDirectiveList from './components/CompareDirectiveList.vue'
import { idListByDS } from './ConfigServiceDirective.api';
import CanAddDirectiveList from '/@/views/services/canadddirective/CanAddDirectiveList.vue'
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
const { createMessage, createConfirm } = useMessage();
const opeMediaAddress = import.meta.env.VITE_OPE_MEDIA_ADDRESS
const canAddDirectiveRef = ref()
const existDirectiveIds = ref([])//指令库已存在指令id
const insRegisterModal = ref();
const catRegisterModal = ref();
const typRegisterModal = ref();
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
const formRef = ref();
const batchAddOpen = ref(false)
const dmRef = ref()
const abnormalListOpen = ref(false)
const abnormalListRef = ref()
const newDirectiveVisible = ref(false)
const compareListOpen = ref(false)
const syncCodeVisible = ref(false)
const queryParam = reactive<any>({
instructionTagId: '',
categoryId: '',
typeId: '',
izEnabled: 'Y',
});
watch(
() => queryParam.instructionTagId,
() => {
queryParam.categoryId = ''
queryParam.typeId = ''
}
)
// 当服务类别变动,清空服务类型
watch(
() => queryParam.categoryId,
() => {
queryParam.typeId = ''
}
)
const registerModal = ref();
const treeData = ref<any>([]);
const selectedKeys = ref<any>([]);
const openKeys = ref<any>([]);
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '服务指令',
api: list,
columns,
canResize: false,
useSearchForm: false,
showIndexColumn: true,
scroll: { y: '58vh' },
pagination: {
current: 1,
pageSize: 15,
pageSizeOptions: ['15', '50', '70', '100'],
},
actionColumn: {
width: 120,
fixed: 'right',
},
beforeFetch: async (params) => {
params.column = 'createTime'
params.order = 'desc'
let rangerQuery = await setRangeQuery();
return Object.assign(params, rangerQuery, filterIzEnabled.value == 'enabled' ? { izEnabled: 'Y' } : {});
},
},
exportConfig: {
name: "服务指令",
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
const labelCol = reactive({
xs: 24,
sm: 4,
xl: 6,
xxl: 4
});
const wrapperCol = reactive({
xs: 24,
sm: 20,
});
const insTagOpen = ref(false)//分类标签抽屉
const categoryOpen = ref(false)//服务类别抽屉
const typeOpen = ref(false)//服务类型抽屉
const ownOrgCode = ref('') //本机构编码
const ownOrgName = ref('') //本机构名称
const filterIzEnabled = ref('enabled')//筛选全部/已启用
const directiveMainOrgInfo = ref()
function onOpenChange(record: string[]) {
selectedKeys.value = record
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
registerModal.value.disableSubmit = true;
registerModal.value.opeType = 'look';
registerModal.value.edit(record);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
reloadTree()
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
},
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
{
label: '编辑指令',
onClick: editDirective.bind(null, { key: record.id }),
},
{
label: '编辑资源',
onClick: editMedia.bind(null, { key: record.id }),
},
{
label: '启用指令',
onClick: usingDirective.bind(null, { key: record.id }),
ifShow: record.izEnabled == 'N'
},
{
label: '停用指令',
onClick: stopDirective.bind(null, { key: record.id }),
ifShow: record.izEnabled == 'Y'
},
]
}
function expandTreeNodeToLevel4(directiveData: any) {
console.log("🚀 ~ expandTreeNodeToLevel4 ~ directiveData:", directiveData)
selectedKeys.value = [directiveData.instructionTagId, directiveData.categoryId, directiveData.typeId, directiveData.id]
openKeys.value = [directiveData.instructionTagId, directiveData.categoryId, directiveData.typeId, directiveData.id]
}
/**
* 查询
*/
function searchQuery(reloadTree = true, resetId = true) {
if (!!queryParam.directiveName || !!queryParam.bodyTags || !!queryParam.emotionTags) {
queryParam.instructionTagId = '';
queryParam.categoryId = '';
queryParam.typeId = '';
}
if (resetId) {
queryParam.id = ''
}
if (filterIzEnabled.value == 'enabled') {
queryParam.izEnabled = 'Y'
} else {
queryParam.izEnabled = ''
}
reload().then(async res => {
if (reloadTree) {
await initTree();
}
if (res.length == 1) {
expandTreeNodeToLevel4(res[0])
}
})
}
/**
* 重置
*/
function searchReset() {
formRef.value.resetFields(); // 重置表单字段
selectedRowKeys.value = []; // 清空选中的行
// 清空 queryParam 中相关字段
queryParam.instructionTagId = '';
queryParam.categoryId = '';
queryParam.typeId = '';
queryParam.directiveName = ''; // 如果你有其他需要清除的字段,也可以加到这里
queryParam.bodyTags = '';
queryParam.emotionTags = '';
queryParam.id = '';
// 刷新数据
reload().then(() => {
initTree();
});
}
let rangeField = 'tollPrice,comPrice,'
/**
* 设置范围查询条件
*/
async function setRangeQuery() {
let queryParamClone = cloneDeep(queryParam);
if (rangeField) {
let fieldsValue = rangeField.split(',');
fieldsValue.forEach(item => {
if (queryParamClone[item]) {
let range = queryParamClone[item];
queryParamClone[item + '_begin'] = range[0];
queryParamClone[item + '_end'] = range[1];
delete queryParamClone[item];
} else {
queryParamClone[item + '_begin'] = '';
queryParamClone[item + '_end'] = '';
}
})
}
return queryParamClone;
}
//分类标签抽屉关闭
function onInsTagClose() {
insTagOpen.value = false
}
//服务类别抽屉关闭
function onCategoryClose() {
categoryOpen.value = false
}
//服务类型抽屉关闭
function onTypeClose() {
typeOpen.value = false
}
const showAudioModal = ref(false); // 控制音频模态框显示
const audioUrl = ref(''); // 音频 URL
const audioPlayer = ref(null);
// 打开音频模态框
const openAudioModal = (url) => {
audioUrl.value = opeMediaAddress + url;
showAudioModal.value = true;
};
// 关闭音频模态框
const closeAudioModal = () => {
if (audioPlayer.value) {
audioPlayer.value.pause(); // 暂停音频播放
audioPlayer.value.currentTime = 0; // 可选:重置播放进度
}
showAudioModal.value = false;
audioUrl.value = '';
};
const showVideoModal = ref(false); // 控制模态框显示
const videoUrl = ref(''); // 视频 URL
const videoPlayer = ref(null);
// 打开视频模态框
const openVideoModal = (url) => {
videoUrl.value = opeMediaAddress + url;
showVideoModal.value = true;
};
// 关闭视频模态框
const closeVideoModal = () => {
if (videoPlayer.value) {
videoPlayer.value.pause(); // 暂停视频播放
videoPlayer.value.currentTime = 0; // 可选:将播放进度重置到开始
}
showVideoModal.value = false;
videoUrl.value = '';
};
// 添加以下响应式变量
const isPlaying = ref(false);
const expandedKeys = ref<string[]>([]);
// 为每个 node 维护一个菜单状态和 hover 定时器
const menuState = reactive<Record<string, { timer?: number, openedByClick: boolean, open: boolean }>>({
})
function onNodeIconEnter(node, level) {
// 检查当前节点是否已经展开,如果是,则跳过关闭所有菜单的操作
const key = node.key;
const currentMenuState = menuState[key];
if (currentMenuState?.open) {
// 当前菜单已经展开,直接返回
return;
}
// 如果没有菜单状态,则初始化
if (!menuState[key]) {
menuState[key] = { openedByClick: false, open: false };
}
// 只有前三级需要自动展开
if (level <= 3 && !menuState[key].openedByClick) {
menuState[key].timer = window.setTimeout(() => {
menuState[key].open = true;
}, 2000);
}
}
function onNodeIconLeave(node) {
const s = menuState[node.key]
if (s?.timer) {
clearTimeout(s.timer)
delete s.timer
}
}
// 当 Dropdown 自身触发 openChange比如点击外部关闭同步状态
function onMenuOpenChange(key: string, open: boolean) {
if (!menuState[key]) menuState[key] = { open: false, openedByClick: false }
menuState[key].open = open
// 如果是外部关闭,则重置 clicked 状态
if (!open) menuState[key].openedByClick = false
}
// 关闭所有菜单工具函数
function closeAllMenus() {
Object.keys(menuState).forEach(k => {
menuState[k].open = false
menuState[k].openedByClick = false
})
}
// 根据层级返回图标 class
function iconClass(level: number) {
switch (level) {
case 1: return 'ant-design:setting-twotone'
case 2: return 'ant-design:setting-twotone'
case 3: return 'ant-design:setting-twotone'
case 4: return 'ant-design:setting-outlined'
default: return 'ant-design:setting-outlined'
}
}
//新增分类标签
function addInstruction() {
insRegisterModal.value.disableSubmit = false;
insRegisterModal.value.add();
}
//启用分类标签
function usingInstruction(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时启用分类标签“' + data.title + '”下所有服务类别、服务类型、服务指令!' + '是否确认启用分类标签“' + data.title + '”!',
okText: '启用',
cancelText: '取消',
onOk: () => {
insRegisterModal.value.usingOrStop(data.instructionId, 'Y', false)
catRegisterModal.value.usingOrStopByCascade(data.instructionId, 'Y', false)
typRegisterModal.value.usingOrStopByCascade(data.instructionId, '', 'Y', false)
registerModal.value.usingOrStopByCascade(data.instructionId, '', '', 'Y', true)
}
});
}
//停用分类标签
function stopInstruction(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时停用分类标签“' + data.title + '”下所有服务类别、服务类型、服务指令!' + '是否确认停用分类标签“' + data.title + '”!',
okText: '停用',
cancelText: '取消',
onOk: () => {
insRegisterModal.value.usingOrStop(data.instructionId, 'N', false)
catRegisterModal.value.usingOrStopByCascade(data.instructionId, 'N', false)
typRegisterModal.value.usingOrStopByCascade(data.instructionId, '', 'N', false)
registerModal.value.usingOrStopByCascade(data.instructionId, '', '', 'N', true)
}
});
}
function editInstruction(data) {
insRegisterModal.value.disableSubmit = false;
insRegisterModal.value.editIcon(data);
}
//新增服务类别
function addCategory(data) {
catRegisterModal.value.disableSubmit = false;
catRegisterModal.value.add({ instructionId: data.instructionId });
}
//启用服务类别
function usingCategory(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时启用服务类别“' + data.title + '”下所有服务类型、服务指令!' + '是否确认启用服务类别“' + data.title + '”!',
okText: '启用',
cancelText: '取消',
onOk: () => {
catRegisterModal.value.usingOrStop(data.categoryId, 'Y', false)
typRegisterModal.value.usingOrStopByCascade('', data.categoryId, 'Y', false)
registerModal.value.usingOrStopByCascade('', data.categoryId, '', 'Y', true)
}
});
}
//停用服务类别
function stopCategory(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时停用服务类别“' + data.title + '”下所有服务类型、服务指令!' + '是否确认停用服务类别“' + data.title + '”!',
okText: '停用',
cancelText: '取消',
onOk: () => {
catRegisterModal.value.usingOrStop(data.categoryId, 'N', false)
typRegisterModal.value.usingOrStopByCascade('', data.categoryId, 'N', false)
registerModal.value.usingOrStopByCascade('', data.categoryId, '', 'N', true)
}
});
}
//修改类别图片
function editCategoryAnimationPath(data) {
catRegisterModal.value.disableSubmit = false;
catRegisterModal.value.edit({ id: data.key });
}
//新增服务类型
function addType(data) {
typRegisterModal.value.disableSubmit = false;
typRegisterModal.value.add({ instructionId: data.instructionId, categoryId: data.categoryId });
}
//启用服务类型
function usingType(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时启用服务类型“' + data.title + '”下所有服务指令!' + '是否确认启用服务类型“' + data.title + '”!',
okText: '启用',
cancelText: '取消',
onOk: () => {
typRegisterModal.value.usingOrStop(data.typeId, 'Y', false)
registerModal.value.usingOrStopByCascade('', '', data.typeId, 'Y', true)
}
});
}
//停用服务类型
function stopType(data) {
createConfirm({
iconType: 'warning',
title: '操作确认',
content: '此操作会同时停用服务类型“' + data.title + '”下所有服务指令!' + '是否确认停用服务类型“' + data.title + '”!',
okText: '停用',
cancelText: '取消',
onOk: () => {
typRegisterModal.value.usingOrStop(data.typeId, 'N', false)
registerModal.value.usingOrStopByCascade('', '', data.typeId, 'N', true)
}
});
}
//修改类别图片
function editTypeAnimationPath(data) {
typRegisterModal.value.disableSubmit = false;
typRegisterModal.value.edit({ id: data.key });
}
//新增服务指令
function addDirective(data) {
registerModal.value.disableSubmit = false;
registerModal.value.opeType = 'add';
registerModal.value.edit({ instructionTagId: data.instructionId, categoryId: data.categoryId, typeId: data.typeId });
}
//编辑务指令
function editDirective(data) {
registerModal.value.disableSubmit = false;
registerModal.value.opeType = 'edit';
registerModal.value.queryByIdFunc(data.key);
}
//指令资源
function editMedia(data) {
registerModal.value.disableSubmit = false;
registerModal.value.opeType = 'editMedia';
registerModal.value.queryAndEditMedia(data.key);
}
//启用服务指令
function usingDirective(data) {
registerModal.value.usingOrStop(data.key, 'Y', true);
}
//停用服务指令
function stopDirective(data) {
registerModal.value.usingOrStop(data.key, 'N', true);
}
// 根据层级返回菜单项
function menuItems(data) {
if (data.level === 1) {
const items = [
{ key: 'addIns', label: '新增分类标签', icon: 'ant-design:plus-outlined', canAdd: true, action: addInstruction }
]
if (data.canAdd) {
items.push({ key: 'addCat', label: '新增服务类别', icon: 'ant-design:plus-outlined', canAdd: data.canAdd, action: addCategory })
}
if (data.izEnabled === 'N') {
items.push({ key: 'usingIns', label: '启用分类标签', icon: 'ant-design:check-circle-outlined', canAdd: true, action: usingInstruction })
} else if (data.izEnabled === 'Y') {
items.push({ key: 'stopIns', label: '停用分类标签', icon: 'ant-design:stop-outlined', canAdd: true, action: stopInstruction })
}
items.push({ key: 'editIns', label: '修改图标', icon: 'ant-design:edit-outlined', canAdd: true, action: editInstruction },)
return items
}
else if (data.level === 2) {
const items = [
{ key: 'addCat', label: '新增服务类别', icon: 'ant-design:plus-outlined', canAdd: data.parentLevelEnabled, action: addCategory },
]
if (data.canAdd) {
items.push({ key: 'addTyp', label: '新增服务类型', icon: 'ant-design:plus-outlined', canAdd: data.canAdd, action: addType })
}
if (data.izEnabled === 'N') {
items.push({ key: 'usingCat', label: '启用服务类别', icon: 'ant-design:check-circle-outlined', canAdd: data.parentLevelEnabled, action: usingCategory })
} else if (data.izEnabled === 'Y') {
items.push({ key: 'stopCat', label: '停用服务类别', icon: 'ant-design:stop-outlined', canAdd: data.parentLevelEnabled, action: stopCategory })
}
items.push({ key: 'editCatImg', label: '修改类别图片', icon: 'ant-design:plus-outlined', canAdd: true, action: editCategoryAnimationPath },)
return items
}
else if (data.level === 3) {
const items = [
{ key: 'addTyp', label: '新增服务类型', icon: 'ant-design:plus-outlined', canAdd: data.parentLevelEnabled, action: addType },
]
if (data.canAdd) {
items.push({ key: 'addDir', label: '新增服务指令', icon: 'ant-design:plus-outlined', canAdd: data.canAdd, action: addDirective })
}
if (data.izEnabled === 'N') {
items.push({ key: 'usingTyp', label: '启用服务类型', icon: 'ant-design:check-circle-outlined', canAdd: data.parentLevelEnabled, action: usingType })
} else if (data.izEnabled === 'Y') {
items.push({ key: 'stopTyp', label: '停用服务类型', icon: 'ant-design:stop-outlined', canAdd: data.parentLevelEnabled, action: stopType })
}
items.push({ key: 'editTypeImg', label: '修改类型图片', icon: 'ant-design:plus-outlined', canAdd: true, action: editTypeAnimationPath },)
return items
}
return []
}
const treeLoading = ref(false)
function reloadTree() {
tree({ filterIzEnabled: filterIzEnabled.value }).then(res => {
treeData.value = res;
})
}
function handleTreeSelect(selectedKeys: string[], { node }: any) {
const level = node.level;
// 清空不必要的查询条件
queryParam.directiveName = '';
queryParam.bodyTags = '';
queryParam.emotionTags = '';
queryParam.id = ''
// queryParam.izEnabled = '';
// 根据节点级别设置查询条件
switch (level) {
case 1: // 分类标签
queryParam.instructionTagId = node.instructionId;
queryParam.categoryId = '';
queryParam.typeId = '';
break;
case 2: // 服务类别
queryParam.instructionTagId = node.instructionId;
queryParam.categoryId = node.categoryId;
queryParam.typeId = '';
break;
case 3: // 服务类型
queryParam.instructionTagId = node.instructionId;
queryParam.categoryId = node.categoryId;
queryParam.typeId = node.typeId;
break;
// case 4: // 服务指令
// // 四级节点特殊处理:设置指令名称
// queryParam.instructionTagId = node.instructionId;
// queryParam.categoryId = node.categoryId;
// queryParam.typeId = node.typeId;
// queryParam.directiveName = node.title;
// queryParam.id = node.key
// break;
}
if (level == 5) return
// 触发查询
searchQuery(false, level != 4);
}
async function initTree() {
treeLoading.value = true;
try {
const res = await tree({ filterIzEnabled: filterIzEnabled.value });
treeData.value = res;
expandedKeys.value = [];
// 默认展开每级第一个
// setDefaultExpanded(res);
} finally {
treeLoading.value = false;
}
}
/**
* 查看指令库
*/
function handleDirectiveMainOpen() {
registerModal.value?.openDM()
}
async function getDirectiveMainOrgCode() {
let { orgCode, orgName } = await getOrgInfo()
ownOrgCode.value = orgCode
ownOrgName.value = orgName
}
function onBatchAddClose() {
batchAddOpen.value = false
}
function onBatchAddSubmit() {
batchAdd(dmRef.value?.selectedRows).then(res => {
createMessage.success('操作成功')
// filterIzEnabled.value = 'all'
searchQuery(true, true)
onBatchAddClose()
})
}
function onAbnormalListClose() {
abnormalListOpen.value = false
searchQuery(true, true)
}
//刷新已有指令库
async function refreshDMExistedIds(dmOrgInfo, izReset = false, izQuery = true) {
let res = await idListByDS({ dataSourceCode: 'master' })
existDirectiveIds.value = res.records
if (izReset) {
canAddDirectiveRef.value?.searchReset()
} else {
canAddDirectiveRef.value?.reload()
}
}
/**
* 关闭新增指令
*/
function handleCancelNewDirective() {
newDirectiveVisible.value = false
}
const syncCode = ref('')
//打开镜像码管理
async function directiveSyncCodeMangeFunc() {
syncCode.value = ''
syncCodeVisible.value = true
if (!syncCode.value) {
let res = await getSyncCode({ 'orgCode': ownOrgCode.value })
syncCode.value = res.result
}
}
async function updateSyncCodeFunc() {
createConfirm({
iconType: 'warning',
title: '更新镜像码',
content: '更新后旧的镜像码无法继续使用,是否确认更新?',
onOk: async () => {
syncCode.value = ''
let res = await updateSyncCode({ 'orgCode': ownOrgCode.value })
syncCode.value = res.result
},
onCancel() { },
});
}
function copySyncCodeFunc() {
// 创建临时文本域
const textArea = document.createElement('textarea');
textArea.value = syncCode.value;
document.body.appendChild(textArea);
textArea.select();
try {
// 执行复制命令
const successful = document.execCommand('copy');
if (successful) {
createMessage.success('复制成功');
} else {
createMessage.error('复制失败');
}
} catch (err) {
console.error('复制失败:', err);
createMessage.error('复制失败');
} finally {
// 清理DOM
document.body.removeChild(textArea);
}
}
// 添加音频结束监听
onMounted(() => {
if (audioPlayer.value) {
audioPlayer.value.addEventListener('ended', () => {
isPlaying.value = false;
});
}
initTree()
getDirectiveMainOrgCode()
});
</script>
<style lang="less" scoped>
.jeecg-basic-table-form-container {
padding: 0;
.table-page-search-submitButtons {
display: block;
margin-bottom: 24px;
white-space: nowrap;
}
.query-group-cust {
min-width: 100px !important;
}
.query-group-split-cust {
width: 30px;
display: inline-block;
text-align: center
}
.ant-form-item:not(.ant-form-item-with-help) {
margin-bottom: 16px;
height: 32px;
}
:deep(.ant-picker),
:deep(.ant-input-number) {
width: 100%;
}
}
audio::-webkit-media-controls-timeline {
display: none;
}
audio::-webkit-media-controls-current-time-display,
audio::-webkit-media-controls-time-remaining-display {
display: none;
}
.btnPrivate {
height: 40px;
margin-left: 4px;
}
.node-title {
display: inline-flex;
align-items: center;
}
.action-icon {
margin-left: 4px;
cursor: pointer;
}
:deep(.centered-dropdown) {
position: fixed;
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
max-height: 80vh;
overflow-y: auto;
.ant-dropdown-menu {
max-height: 70vh;
overflow-y: auto;
}
}
.container-height {
height: 77vh;
}
@media screen and (min-width: 1600px) and (min-height: 900px) {
.container-height {
height: 81.5vh;
}
}
:deep(.ant-menu-item-selected) {
color: #4b4b4b !important;
}
:deep(.ant-menu-submenu-title) {
color: #4b4b4b !important;
}
.auto-wrap {
word-wrap: break-word;
/* 长单词/URL换行 */
word-break: break-all;
/* 更激进的换行策略 */
white-space: normal;
/* 默认换行行为 */
line-height: 20px !important;
display: inline flow-root;
padding-top: 10px;
}
</style>