Compare commits

...

3 Commits

Author SHA1 Message Date
1378012178@qq.com 345b51130f 部门管理增加省、市、区选择 2025-05-30 16:49:16 +08:00
1378012178@qq.com 53b7241cfb Merge branch 'master' of http://47.115.223.229:8888/yangjun/hldy_vue 2025-05-27 14:20:03 +08:00
1378012178@qq.com b1581fe3b5 1、服务指令文件同步
2、服务指令同步:将机构选择拆分为公共组件
2025-05-27 14:19:54 +08:00
23 changed files with 2579 additions and 200 deletions

View File

@ -2,7 +2,7 @@
VITE_USE_MOCK = true
# 发布路径
VITE_PUBLIC_PATH = /
VITE_PUBLIC_PATH = /nu
# 跨域代理,您可以配置多个 ,请注意,没有换行符

View File

@ -0,0 +1,789 @@
<template>
<div class="p-2 base-class">
<a-card @click="toggleSearchStatus = true" :class="{ 'base-con-class': toggleSearchStatus == false }">
<a-row v-show="!toggleSearchStatus && !orgSelectedCon">
<a-col :span="10">
<span style="font-size: 15px;font-weight: bold;color: #333;">机构信息:</span>
<span v-if="!orgData" class="base-info-dn" style="color: red;"> 请先选择机构</span>
<template v-else>
<span class="base-info-dn"
style="margin-left: 10px;font-size: 15px;font-weight: 400;color: #333;"> {{
orgData?.departName }}</span>
<a-button type="primary" size="small" @click.stop="handleResetOrg" style="margin-left: 10px;">
<span class="base-info-dn">重新选择</span>
</a-button>
</template>
</a-col>
<a-col :span="5">
<span v-if="hasSearchConditions" style="font-size: 16px;font-weight: bold;color:#E86A6A">
有筛选条件未重置
</span>
<span style="font-size: 16px;font-weight: bold;color:#ABACAD">点击展开更多操作区域</span>
</a-col>
</a-row>
<div class="org-info-card" v-show="(!toggleSearchStatus && orgSelectedCon) || toggleSearchStatus">
<div class="org-info-header">
<div class="org-title">
<span class="base-info-label">机构信息:</span>
<span v-if="!orgData" class="base-info-dn" style="color: #ff4d4f;"> 请先选择机构</span>
<span v-else class="org-name">{{ orgData?.departName }}</span>
</div>
<a-button v-if="!orgSelectedCon" class="reset-btn" type="primary" @click.stop="handleResetOrg">
<span>重新选择</span>
</a-button>
</div>
<div class="org-info-content">
<div class="info-row">
<div class="info-item">
<span class="label">机构编码</span>
<span class="value">{{ orgData?.orgCode || '-' }}</span>
</div>
<div class="info-item">
<span class="label">运营时间</span>
<span class="value">
{{ orgData?.operationStartTime?.substring(0, 10) || '-' }}
{{ orgData?.operationEndTime?.substring(0, 10) || '-' }}
</span>
</div>
<div class="info-item">
<span class="label">合同时间</span>
<span class="value">
{{ orgData?.contractStartTime?.substring(0, 10) || '-' }}
{{ orgData?.contractEndTime?.substring(0, 10) || '-' }}
</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="label">电话</span>
<span class="value">{{ orgData?.mobile || '-' }}</span>
</div>
<div class="info-item">
<span class="label">传真</span>
<span class="value">{{ orgData?.fax || '-' }}</span>
</div>
<div class="info-item">
<span class="label">地址</span>
<span class="value">{{ orgData?.address || '-' }}</span>
</div>
</div>
</div>
</div>
<a-divider v-show="toggleSearchStatus && orgData && !orgSelectedCon" style="margin: 0px;margin-top: 8px;" />
<a-col :span="24" v-show="toggleSearchStatus && orgData && !orgSelectedCon">
<div class="jeecg-basic-table-form-container">
<slot name="searchFormSlot"></slot>
</div>
</a-col>
</a-card>
</div>
<div class="p-2 con-class" style="margin-top: -10px;">
<a-card>
<a-card v-show="orgSelectedCon" :bordered="false" class="org-container">
<a-row :gutter="[16, 16]">
<a-col :span="8" v-for="org in orgTable" :key="org.id">
<div class="org-card" :class="{ 'active': org.id == orgData?.id }"
@click="handleOrgSelected(org)">
<div class="org-header">
<a-tag color="blue" v-if="org.id == orgData?.id">当前选择</a-tag>
<h3 class="org-title">{{ org.departName }}</h3>
</div>
<a-descriptions size="default" :column="2" bordered>
<a-descriptions-item label="机构编码">{{ org.orgCode || '-' }}</a-descriptions-item>
<a-descriptions-item label="运营时间">
{{ org.operationStartTime?.substring(0, 10) || '-' }}
{{ org.operationEndTime?.substring(0, 10) || '-' }}
</a-descriptions-item>
<a-descriptions-item label="电话">
<div class="contact-line">
<span><phone-outlined /> {{ org.mobile || '-' }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="传真">
<div class="contact-line">
<span><printer-outlined /> {{ org.fax || '-' }}</span>
</div>
</a-descriptions-item>
<a-descriptions-item label="地址">
<ellipsis :length="26" tooltip>{{ org.address || '-' }}</ellipsis>
</a-descriptions-item>
</a-descriptions>
</div>
</a-col>
</a-row>
</a-card>
<a-card v-show="orgSelectedCon && (!orgTable || orgTable?.length == 0)">
<a-empty description="暂无数据" :image="simpleImage" />
</a-card>
<div class="tab-header-container" v-show="!orgSelectedCon">
<a-tabs v-model:activeKey="tabActiveKey" class="tabs-container">
<a-tab-pane key="dataAsync" tab="数据同步">
<a-card>
<a-row>
<a-col :span="sourceScreenSpan">
<div style="height: 16px;margin-left: 15px;">
<a-tag color="#87d068">源数据</a-tag>
<a-radio-group v-model:value="viewType" size="small" style="margin-left: 10px;"
@change="emit('viewTypeChanged', viewType)">
<a-radio-button value="all">全部</a-radio-button>
<a-radio-button value="selected">已选择</a-radio-button>
<a-radio-button value="unselected">未选择</a-radio-button>
</a-radio-group>
<slot name="sourceTableSlot"></slot>
</div>
</a-col>
<a-col :span="selectedScreenSpan">
<div style="height: 31px;margin-left: 4px;">
<a-tag color="#2db7f5">
已选择
<span style="font-size: 13px;font-weight: bold;">
{{ ' ' + selectedSize + ' ' }}
</span>
</a-tag>
</div>
<a-card size="small" :bordered="false" class="selected-list-container">
<slot name="businessTableSlot">111</slot>
</a-card>
</a-col>
</a-row>
</a-card>
</a-tab-pane>
<a-tab-pane key="asyncHistory" tab="同步历史">
<AsyncListComponent ref="asyncHistoryRef"></AsyncListComponent>
</a-tab-pane>
</a-tabs>
<div class="toggle-button-container">
<a-button type="primary" size="small" @click.stop="refreshHistory()" style="margin-right: 10px;"
v-show="tabActiveKey == 'asyncHistory'">
<span>刷新</span>
</a-button>
<a-button type="warning" size="small" @click.stop="handleAsync()"
style="margin-left: 5px;margin-right: 5px;" v-show="tabActiveKey == 'dataAsync'">
<span>同步数据</span>
</a-button>
<a-divider type="vertical" style="background-color: #CDCDCF" />
<a-button type="primary" size="small" @click="handleSelectAll" v-show="tabActiveKey == 'dataAsync'"
style="margin-left: 8px">全部添加</a-button>
<a-divider type="vertical" style="background-color: #CDCDCF" v-show="tabActiveKey == 'dataAsync'" />
<a-button type="error" size="small" @click="handleRemoveAll" v-show="tabActiveKey == 'dataAsync'"
style="margin-left: 8px">全部移除</a-button>
<a-divider type="vertical" style="background-color: #CDCDCF" v-show="tabActiveKey == 'dataAsync'" />
<a-button type="primary" size="small" @click.stop="handleRestore"
v-show="tabActiveKey == 'dataAsync'" style="margin-left: 8px">
<span>还原数据</span>
</a-button>
<a-divider type="vertical" style="background-color: #CDCDCF" v-show="tabActiveKey == 'dataAsync'" />
<a-radio-group v-model:value="splitVal" button-style="solid" size="small"
@change="splitScreenChanged" style="margin-left: 5px;margin-right: 5px;"
v-show="tabActiveKey == 'dataAsync'">
<a-radio-button value="sc">源数据</a-radio-button>
<a-radio-button value="sc2sed1">分屏1</a-radio-button>
<a-radio-button value="sc1sed1">分屏2</a-radio-button>
<a-radio-button value="sc1sed2">分屏3</a-radio-button>
<a-radio-button value="sed">已选择</a-radio-button>
</a-radio-group>
<a-divider type="vertical" style="background-color: #CDCDCF" v-show="tabActiveKey == 'dataAsync'" />
<a @click="toggleSearchStatus = !toggleSearchStatus" class="toggle-button"
style="margin-left: 5px;margin-right: 5px;">
{{ toggleSearchStatus ? '拉伸' : '压缩' }}
<Icon
:icon="toggleSearchStatus ? 'humbleicons:align-objects-top' : 'humbleicons:align-objects-bottom'" />
</a>
</div>
</div>
</a-card>
</div>
<a-modal v-model:visible="showVideoModal" title="视频播放" :footer="null" @cancel="closeVideoModal"
:bodyStyle="{ padding: '0', maxHeight: '80vh', overflow: 'auto' }">
<video controls style="width: 100%; max-height: '70vh'; display: block; margin: 0 auto;">
<source :src="videoUrl">
您的浏览器不支持视频播放
</video>
</a-modal>
</template>
<script lang="ts" name="synchronization-directive" setup>
import { ref, onMounted, computed, onBeforeMount, reactive } from 'vue';
import { initDictOptions } from '/@/utils/dict';
import { queryDepartTreeSync } from '/@/api/common/api';
import { Empty } from 'ant-design-vue';
import { Modal } from 'ant-design-vue';
import { useMessage } from "/@/hooks/web/useMessage";
import AsyncListComponent from '@/components/dataAsync/AsyncMainList.vue'
import { number } from 'vue-types';
const props = defineProps({
selectedSize: 0
});
const emit = defineEmits(['changeShowJMCom', 'viewTypeChanged', 'sourceOrgCodeChanged', 'orgChanged']);
interface OrganizationData {
id?: string;
departName: string;
orgCode: string;
operationStartTime?: string;
operationEndTime?: string;
contractStartTime?: string;
contractEndTime?: string;
mobile?: string;
fax?: string;
address?: string;
}
onBeforeMount(async () => {
const dictData = await initDictOptions('org_code')
emit('sourceOrgCodeChanged', dictData.filter(d => d.text == 'shi_yan_tian')[0].value)
emit('changeShowJMCom', true)
})
const initialDataIds = ref<Array<string | number>>([]);
const { createMessage } = useMessage();
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
const toggleSearchStatus = ref<boolean>(true);
const splitVal = ref<string>('sc1sed1');
const sourceScreenSpan = ref(12);
const selectedScreenSpan = ref(12);
const orgTable = ref<OrganizationData[]>([]);
const orgData = ref<OrganizationData | null>(null);
const showVideoModal = ref(false);
const videoUrl = ref('');
const selectedItems = ref(new Map<string | number, any>());
const listComRef = ref();
const orgSelectedCon = ref(true)
const tabActiveKey = ref('dataAsync')
const asyncHistoryRef = ref(null)
const splitScreenChanged = (val) => {
let v_ = val.target.value;
if (v_ == 'sc') {
sourceScreenSpan.value = 24;
selectedScreenSpan.value = 0;
}
if (v_ == 'sc2sed1') {
sourceScreenSpan.value = 16;
selectedScreenSpan.value = 8;
}
if (v_ == 'sc1sed1') {
sourceScreenSpan.value = 12;
selectedScreenSpan.value = 12;
}
if (v_ == 'sc1sed2') {
sourceScreenSpan.value = 8;
selectedScreenSpan.value = 16;
}
if (v_ == 'sed') {
sourceScreenSpan.value = 0;
selectedScreenSpan.value = 24;
}
};
const closeVideoModal = () => {
showVideoModal.value = false;
videoUrl.value = '';
};
const handleOrgSelected = async (org) => {
if (!!orgData.value && orgData.value.id != org.id) {
Modal.confirm({
title: '变更机构提醒',
content: '是否变更机构!',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await loadOrgData(org);
},
});
} else {
await loadOrgData(org);
}
};
const loadOrgData = async (org) => {
//
orgData.value = org;
emit('orgChanged', org)
};
const handleResetOrg = () => {
orgSelectedCon.value = true
}
const resetOrgSelectedCon = (v_) => {
orgSelectedCon.value = v_
}
//
const handleAsync = () => {
Modal.confirm({
title: '数据同步',
content: '是否确认同步',
okText: '确认',
cancelText: '取消',
onOk: async () => {
let idStr = Array.from(selectedItems.value.keys()).join(',')
if (!idStr) {
createMessage.warning("未选择数据!");
return
}
let izInc = true
if (!!orgData.value?.operationStartTime) izInc = new Date() >= new Date(orgData.value?.operationStartTime)
let params = {
orgCode: orgData.value?.orgCode,
izInc,
idStr
}
await asyncFunc(params)
//
if (!orgData.value?.operationStartTime || new Date() >= new Date(orgData.value?.operationStartTime)) {
// ID
const syncedIds = Array.from(selectedItems.value.keys());
// initialDataIdsID
syncedIds.forEach(id => {
if (!initialDataIds.value.includes(id)) {
initialDataIds.value.push(id);
}
});
}
//
refreshHistory()
createMessage.success("同步结果请在同步历史中查看!");
},
});
}
//
const refreshHistory = () => {
}
//
const viewType = ref('all')
//
const handleSelectAll = async () => {
// if (!orgData.value) {
// createMessage.warning("");
// return;
// }
// try {
// //
// const allData = await listComRef.value?.getAllData?.(queryParam.value);
// if (allData && allData.length > 0) {
// //
// const newItems = allData.filter(item => !selectedItems.value.has(item.id));
// if (newItems.length === 0) {
// createMessage.info("");
// return;
// }
// //
// newItems.forEach(item => {
// selectedItems.value.set(item.id, item);
// });
// //
// listComRef.value?.updateSelection?.(Array.from(selectedItems.value.values()));
// createMessage.success(` ${newItems.length} `);
// } else {
// createMessage.info("");
// }
// } catch (error) {
// createMessage.error("");
// console.error(":", error);
// }
};
//
const handleRemoveAll = () => {
if (selectedItems.value.size === 0) {
createMessage.info("当前没有已选数据");
return;
}
Modal.confirm({
title: '确认移除',
content: '确定要移除所有已选数据吗?',
okText: '确认',
cancelText: '取消',
onOk: () => {
//
if (!orgData.value?.operationStartTime || new Date() >= new Date(orgData.value?.operationStartTime)) {
// Map
const newSelectedItems = new Map();
//
selectedItems.value.forEach((value, key) => {
if (initialDataIds.value.includes(key)) {
newSelectedItems.set(key, value);
}
});
selectedItems.value = newSelectedItems;
listComRef.value?.updateSelection?.(Array.from(newSelectedItems.values()));
} else {
selectedItems.value.clear();
listComRef.value?.updateSelection?.([]);
}
createMessage.success("已移除所有已选数据");
}
});
};
//
const handleRestore = async () => {
if (!orgData.value) {
createMessage.warning("请先选择机构");
return;
}
Modal.confirm({
title: '确认还原',
content: '确定要将已选数据还原为机构初始状态吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
//
await loadOrgData(orgData.value);
createMessage.success("已成功还原数据");
} catch (error) {
console.error("还原数据失败:", error);
createMessage.error("还原数据失败");
}
}
});
};
//
const hasSearchConditions = computed(() => {
return true
// return Object.keys(queryParam.value).some(key =>
// key !== 'viewType' && queryParam.value[key] !== undefined && queryParam.value[key] !== ''
// );
});
onMounted(() => {
queryDepartTreeSync({ platType: 'ywjg' }).then(res => {
orgTable.value = res;
});
});
defineExpose({
orgData,
resetOrgSelectedCon
});
</script>
<style lang="less" scoped>
.base-con-class {
background: white;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
border: 1px solid rgba(0, 0, 0, 0.05);
}
.selected-list-container {
height: calc(100vh - 350px);
overflow: auto;
:deep(.ant-table) {
width: 100% !important;
max-width: 100%;
}
}
/* 新增的机构卡片样式 */
.org-cards-container {
max-height: 75vh;
overflow-y: auto;
padding: 8px;
}
.org-card {
margin-bottom: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
height: 100%;
&:hover {
transform: translateY(-4px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
background: radial-gradient(circle at center, #c7e6ff 0%, #d4eeff 70%, #e4f0ff 100%);
}
:deep(.ant-card-head) {
border-bottom: 1px solid #f0f0f0;
padding: 0 16px;
min-height: 48px;
.ant-card-head-title {
padding: 12px 0;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.org-info-item {
display: flex;
margin-bottom: 12px;
font-size: 13px;
line-height: 1.4;
&:last-child {
margin-bottom: 0;
}
.label {
font-weight: 500;
color: #666;
min-width: 70px;
flex-shrink: 0;
}
.value {
color: #333;
word-break: break-word;
flex: 1;
&.address-text {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.selected {
position: relative;
border: 1px solid #1890ff;
&::after {
content: '';
position: absolute;
right: 8px;
bottom: 8px;
width: 24px;
height: 24px;
background: url("@/assets/icons/success.svg") no-repeat center;
background-size: contain;
}
}
.tab-header-container {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.tabs-container {
width: 100%;
:deep(.ant-tabs-content) {
height: calc(100% - 40px); // tab
overflow: auto;
}
}
.toggle-button-container {
position: absolute;
right: 16px;
top: 8px;
z-index: 1;
}
.toggle-button {
display: inline-flex;
align-items: center;
gap: 4px;
color: rgba(0, 0, 0, 0.65);
transition: all 0.3s;
&:hover {
color: #1890ff;
}
}
.con-class {
:deep(.ant-card .ant-card-body) {
// padding-left: 10px !important;
// padding-right: 10px !important;
// padding-bottom: 0px !important;
// padding-bottom: 0px !important;
padding: 3px 10px 3px 10px !important;
}
}
.base-class {
:deep(.ant-card .ant-card-body) {
padding-top: 10px !important;
padding-bottom: 10px !important;
}
}
.con-class {
:deep(.css-dev-only-do-not-override-9m98ij .ant-table-wrapper .ant-table-pagination.ant-pagination) {
margin: 10px !important;
}
}
.org-info-card {
background: linear-gradient(135deg, #f8f9fa 0%, #d0eaff 100%);
border-radius: 8px;
padding: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
border: 1px solid #e8e8e8;
transition: all 0.3s ease;
// &:hover {
// box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
// transform: translateY(-1px);
// }
}
.org-info-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
padding-bottom: 5px;
border-bottom: 1px solid #f0f0f0;
}
.org-title {
display: flex;
align-items: center;
.base-info-label {
font-weight: 600;
color: #595959;
font-size: 15px;
}
.org-name {
margin-left: 10px;
font-weight: 600;
color: #262626;
font-size: 16px;
}
}
.reset-btn {
background-color: #1890ff;
border-color: #1890ff;
height: 32px;
border-radius: 4px;
font-weight: 500;
// &:hover {
// background-color: #40a9ff;
// border-color: #40a9ff;
// }
}
.org-info-content {
.info-row {
display: flex;
margin-bottom: 5px;
&:last-child {
margin-bottom: 0;
}
}
.info-item {
flex: 1;
padding: 0 8px;
.label {
color: #8c8c8c;
font-size: 13px;
margin-right: 8px;
}
.value {
color: #262626;
font-size: 13px;
word-break: break-all;
}
}
}
.org-container {
background: transparent;
.org-card {
padding: 16px;
border: 1px solid #f0f0f0;
border-radius: 4px;
transition: all 0.3s;
background: white;
&:hover {
border-color: #1890ff;
box-shadow: 0 2px 12px rgba(24, 144, 255, 0.2);
}
&.active {
border: 1px solid #1890ff;
background: #f0f8ff;
}
.org-header {
display: flex;
align-items: center;
margin-bottom: 12px;
.org-title {
margin: 0 0 0 8px;
font-size: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.contact-line {
display: flex;
justify-content: space-between;
>span {
display: inline-flex;
align-items: center;
.anticon {
margin-right: 4px;
color: #8c8c8c;
}
}
}
:deep(.ant-descriptions) {
.ant-descriptions-item-label {
width: 80px;
color: #666;
}
}
}
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div class="media-detail">
<a-descriptions bordered :column="1">
<a-descriptions-item label="资源名称">{{ record.name }}</a-descriptions-item>
<a-descriptions-item label="文件类型">{{ record.fileType }}</a-descriptions-item>
<a-descriptions-item label="备注">{{ record.descr || '无' }}</a-descriptions-item>
<a-descriptions-item label="预览">
<PreviewComponent
:file-type="record.fileType"
:file-path="record.filePath"
/>
</a-descriptions-item>
<a-descriptions-item label="创建时间">{{ record.createTime }}</a-descriptions-item>
<a-descriptions-item label="更新时间">{{ record.updateTime }}</a-descriptions-item>
</a-descriptions>
</div>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
import PreviewComponent from './PreviewPickerComponent.vue';
const props = defineProps({
record: {
type: Object,
default: () => ({})
}
});
const { record } = props;
</script>
<style scoped>
.media-detail {
padding: 10px;
}
</style>

View File

@ -0,0 +1,72 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/mediamanage/mediaManage/list',
save='/mediamanage/mediaManage/add',
edit='/mediamanage/mediaManage/edit',
deleteOne = '/mediamanage/mediaManage/delete',
deleteBatch = '/mediamanage/mediaManage/deleteBatch',
importExcel = '/mediamanage/mediaManage/importExcel',
exportXls = '/mediamanage/mediaManage/exportXls',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}

View File

@ -0,0 +1,234 @@
<template>
<a-modal
:visible="visible"
:width="1000"
:title="title"
:footer="null"
@cancel="handleCancel"
>
<div class="resource-picker-container">
<!-- 搜索区域 -->
<div class="search-area">
<a-form layout="inline" :model="searchParams">
<a-form-item label="资源名称">
<a-input v-model:value="searchParams.name" placeholder="请输入资源名称" />
</a-form-item>
<a-form-item label="文件类型">
<j-dict-select-tag
v-model:value="searchParams.fileType"
dictCode="file_type"
placeholder="请选择文件类型"
allowClear
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button style="margin-left: 8px" @click="resetSearch">重置</a-button>
</a-form-item>
</a-form>
</div>
<!-- 表格区域 -->
<div class="table-area">
<a-table
:columns="columns"
:data-source="dataSource"
:pagination="pagination"
:loading="loading"
rowKey="id"
@change="handleTableChange"
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'filePath'">
<PreviewComponent
:file-type="record.fileType"
:file-path="record.filePath"
/>
</template>
<template v-if="column.key === 'action'">
<a-button type="link" @click="handleViewDetail(record)">详情</a-button>
</template>
</template>
</a-table>
</div>
<!-- 底部操作按钮 -->
<div class="footer-area">
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleConfirm" :disabled="!selectedRowKeys.length" style="margin-left: 8px">
确定
</a-button>
</div>
</div>
<!-- 详情弹窗 -->
<a-modal
v-model:visible="detailVisible"
:title="detailTitle"
:width="800"
:footer="null"
>
<MediaDetail :record="currentRecord" />
</a-modal>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, defineProps, defineEmits, watch } from 'vue';
import { list } from './MediaManagePicker.api';
import PreviewComponent from './PreviewComponent.vue';
import MediaDetail from './MediaDetail.vue';
const props = defineProps({
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '选择媒体资源'
},
multiple: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:visible', 'confirm']);
const visible = ref(props.visible);
const loading = ref(false);
const dataSource = ref([]);
const selectedRowKeys = ref([]);
const selectedRows = ref([]);
const detailVisible = ref(false);
const currentRecord = ref({});
const detailTitle = ref('资源详情');
const searchParams = reactive({
name: '',
fileType: ''
});
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: ['10', '20', '30', '50'],
showTotal: total => `${total}`
});
const columns = [
{
title: '资源名称',
dataIndex: 'name',
key: 'name'
},
{
title: '文件类型',
dataIndex: 'fileType',
key: 'fileType'
},
{
title: '预览',
key: 'filePath'
},
{
title: '操作',
key: 'action'
}
];
// visible
watch(() => props.visible, (val) => {
visible.value = val;
if (val) {
fetchData();
}
});
//
const fetchData = async () => {
try {
loading.value = true;
const params = {
...searchParams,
pageNo: pagination.current,
pageSize: pagination.pageSize
};
const res = await list(params);
dataSource.value = res.records || [];
pagination.total = res.total || 0;
} catch (error) {
console.error('获取数据失败:', error);
} finally {
loading.value = false;
}
};
//
const handleTableChange = (pag, filters, sorter) => {
pagination.current = pag.current;
pagination.pageSize = pag.pageSize;
fetchData();
};
//
const handleSearch = () => {
pagination.current = 1;
fetchData();
};
//
const resetSearch = () => {
searchParams.name = '';
searchParams.fileType = '';
handleSearch();
};
//
const onSelectChange = (selectedKeys, selectedRows) => {
selectedRowKeys.value = selectedKeys;
selectedRows.value = selectedRows;
};
//
const handleViewDetail = (record) => {
currentRecord.value = record;
detailTitle.value = `资源详情 - ${record.name}`;
detailVisible.value = true;
};
//
const handleConfirm = () => {
emit('confirm', props.multiple ? selectedRows.value : selectedRows.value[0]);
handleCancel();
};
//
const handleCancel = () => {
selectedRowKeys.value = [];
selectedRows.value = [];
emit('update:visible', false);
};
defineExpose({
fetchData
});
</script>
<style scoped>
.resource-picker-container {
padding: 10px;
}
.search-area {
margin-bottom: 16px;
}
.footer-area {
margin-top: 16px;
text-align: right;
}
</style>

View File

@ -0,0 +1,57 @@
<template>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="fileType === 'image' && filePath" class="preview-item">
<a-image :width="200" :src="getFullPath(filePath)" />
</div>
<!-- 视频预览 -->
<div v-if="fileType === 'video' && filePath" class="preview-item">
<video controls :src="getFullPath(filePath)" style="width: 200px; height: auto;"></video>
</div>
<!-- 音频预览 -->
<div v-if="fileType === 'audio' && filePath" class="preview-item">
<audio controls :src="getFullPath(filePath)"></audio>
</div>
<!-- 文档预览 -->
<div v-if="fileType === 'document' && filePath" class="preview-item">
<a-button @click="handlePreviewDocument">预览文档</a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
const props = defineProps({
fileType: {
type: String,
default: ''
},
filePath: {
type: String,
default: ''
}
});
const getFullPath = (path) => {
return getFileAccessHttpUrl(path);
};
const handlePreviewDocument = () => {
window.open(getFullPath(props.filePath), '_blank');
};
</script>
<style scoped>
.preview-container {
margin-top: 10px;
}
.preview-item {
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,72 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/mediamanage/mediaManage/list',
save='/mediamanage/mediaManage/add',
edit='/mediamanage/mediaManage/edit',
deleteOne = '/mediamanage/mediaManage/delete',
deleteBatch = '/mediamanage/mediaManage/deleteBatch',
importExcel = '/mediamanage/mediaManage/importExcel',
exportXls = '/mediamanage/mediaManage/exportXls',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}

View File

@ -0,0 +1,36 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { getWeekMonthQuarterYear } from '/@/utils';
//列表数据
export const columns: BasicColumn[] = [
{
title: '名称',
align: "center",
dataIndex: 'name'
},
{
title: '备注',
align: "center",
dataIndex: 'descr'
},
{
title: '文件类型',
align: "center",
dataIndex: 'fileType_dictText'
},
{
title: '文件预览',
align: "center",
dataIndex: 'filePath'
},
];
// 高级查询数据
export const superQuerySchema = {
name: {title: '名称',order: 0,view: 'text', type: 'string',},
descr: {title: '备注',order: 1,view: 'textarea', type: 'string',},
fileType: {title: '文件类型',order: 2,view: 'text', type: 'string',},
filePath: {title: '文件预览',order: 3,view: 'text', type: 'string',},
};

View File

@ -0,0 +1,264 @@
<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="name">
<template #label><span title="名称">名称</span></template>
<JInput v-model:value="queryParam.name" placeholder="请输入名称"/>
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="fileType">
<template #label><span title="文件类型">文件类型</span></template>
<j-dict-select-tag type='list' v-model:value="queryParam.fileType" dictCode="file_type"
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>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'mediamanage:nu_media_manage:add'" @click="handleAdd"
preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button v-auth="'mediamanage:nu_media_manage:deleteBatch'">批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template>
<template v-slot:bodyCell="{ column, record, index, text }">
</template>
</BasicTable>
<!-- 表单区域 -->
<MediaManageModal ref="registerModal" @success="handleSuccess"></MediaManageModal>
</div>
</template>
<script lang="ts" name="mediamanage-mediaManage" setup>
import { ref, reactive } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, superQuerySchema } from './MediaManage.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './MediaManage.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import MediaManageModal from './components/MediaManageModal.vue'
import { useUserStore } from '/@/store/modules/user';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import JSelectMultiple from '/@/components/Form/src/jeecg/components/JSelectMultiple.vue';
import JInput from "/@/components/Form/src/jeecg/components/JInput.vue";
const formRef = ref();
const queryParam = reactive<any>({});
const toggleSearchStatus = ref<boolean>(false);
const registerModal = ref();
const userStore = useUserStore();
//table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: {
title: '媒体资源管理',
api: list,
columns,
canResize: false,
useSearchForm: false,
actionColumn: {
width: 150,
fixed: 'right',
},
beforeFetch: async (params) => {
return Object.assign(params, queryParam);
},
},
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 superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
searchQuery();
}
/**
* 新增事件
*/
function handleAdd() {
registerModal.value.disableSubmit = false;
registerModal.value.add();
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
registerModal.value.disableSubmit = false;
registerModal.value.edit(record);
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
registerModal.value.disableSubmit = true;
registerModal.value.edit(record);
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({ id: record.id }, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'mediamanage:nu_media_manage:edit'
}, {
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
auth: 'mediamanage:nu_media_manage:delete'
}
];
}
/**
* 下拉操作栏
*/
function getDropDownAction(record) {
return [
]
}
/**
* 查询
*/
function searchQuery() {
reload();
}
/**
* 重置
*/
function searchReset() {
formRef.value.resetFields();
selectedRowKeys.value = [];
//
reload();
}
</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%;
}
}
</style>

View File

@ -0,0 +1,237 @@
<template>
<a-spin :spinning="confirmLoading">
<JFormContainer :disabled="disabled">
<template #detail>
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol"
name="MediaManageForm">
<a-row>
<a-col :span="24">
<a-form-item label="名称" v-bind="validateInfos.name" id="MediaManageForm-name" name="name">
<a-input v-model:value="formData.name" placeholder="请输入名称" allow-clear></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="备注" v-bind="validateInfos.descr" id="MediaManageForm-descr" name="descr">
<a-textarea v-model:value="formData.descr" :rows="4" placeholder="请输入备注" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="文件类型" v-bind="validateInfos.fileType" id="MediaManageForm-fileType" name="fileType">
<j-dict-select-tag type='list' v-model:value="formData.fileType" dictCode="file_type"
placeholder="请选择文件类型" allowClear @change="handleFileTypeChange" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="文件上传" v-bind="validateInfos.filePath" id="MediaManageForm-filePath">
<!-- 未选择文件类型时的提示 -->
<div v-if="!formData.fileType" class="upload-tip">
<span style="color: red; line-height: 32px;">请先选择文件类型</span>
</div>
<!-- 图片上传组件 -->
<j-image-upload v-if="formData.fileType === 'image'" v-model:value="formData.filePath" :number="1"
multiple="1" :fileSize="10" accept=".png,.jpg,.jpeg" @change="handleFileChange">
</j-image-upload>
<!-- 视频上传组件 -->
<template v-if="formData.fileType === 'video'">
<j-upload v-model:value="formData.filePath" :fileSize="100" :multiple="false" :maxCount="1"
accept=".mp4" @change="handleFileChange">
</j-upload>
<!-- 视频预览 -->
<div v-if="formData.filePath" class="media-preview">
<video controls style="max-width: 100%; max-height: 300px;">
<source :src="getFileAccessHttpUrl(formData.filePath)" type="video/mp4">
您的浏览器不支持视频播放
</video>
</div>
</template>
<!-- 音频上传组件 -->
<template v-if="formData.fileType === 'audio'">
<j-upload v-model:value="formData.filePath" :fileSize="50" :multiple="false" :maxCount="1"
accept=".mp3" @change="handleFileChange">
</j-upload>
<!-- 音频预览 -->
<div v-if="formData.filePath" class="media-preview">
<audio controls style="width: 100%;">
<source :src="getFileAccessHttpUrl(formData.filePath)" type="audio/mpeg">
您的浏览器不支持音频播放
</audio>
</div>
</template>
<!-- 文档上传组件 -->
<j-upload v-if="formData.fileType === 'document'" v-model:value="formData.filePath" :fileSize="20"
:multiple="false" :maxCount="1" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt"
@change="handleFileChange">
</j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
</JFormContainer>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
import { getValueType } from '/@/utils';
import { saveOrUpdate } from '../MediaManage.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true }
});
const formRef = ref();
const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']);
const formData = reactive<Record<string, any>>({
id: '',
name: '',
descr: '',
fileType: '',
filePath: '',
});
const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//
const validatorRules = reactive({
fileType: [{ required: true, message: '请输入文件类型!' },],
filePath: [{ required: true, message: '请输入文件预览!' },],
});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
//
const handleFileTypeChange = () => {
//
formData.filePath = '';
};
//
const handleFileChange = (value) => {
formData.filePath = value;
};
//
const disabled = computed(() => {
if (props.formBpm === true) {
if (props.formData.disabled === false) {
return false;
} else {
return true;
}
}
return props.formDisabled;
});
/**
* 新增
*/
function add() {
edit({});
}
/**
* 编辑
*/
function edit(record) {
nextTick(() => {
resetFields();
const tmpData = {};
Object.keys(formData).forEach((key) => {
if (record.hasOwnProperty(key)) {
tmpData[key] = record[key]
}
})
//
Object.assign(formData, tmpData);
});
}
/**
* 提交数据
*/
async function submitForm() {
try {
//
await validate();
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
}
confirmLoading.value = true;
const isUpdate = ref<boolean>(false);
//
let model = formData;
if (model.id) {
isUpdate.value = true;
}
//
for (let data in model) {
//
if (model[data] instanceof Array) {
let valueType = getValueType(formRef.value.getProps, data);
//
if (valueType === 'string') {
model[data] = model[data].join(',');
}
}
}
await saveOrUpdate(model, isUpdate.value)
.then((res) => {
if (res.success) {
createMessage.success(res.message);
emit('ok');
} else {
createMessage.warning(res.message);
}
})
.finally(() => {
confirmLoading.value = false;
});
}
defineExpose({
add,
edit,
submitForm,
});
</script>
<style lang="less" scoped>
.antd-modal-form {
padding: 14px;
}
.upload-tip {
display: flex;
align-items: center;
height: 32px;
}
.media-preview {
margin-top: 16px;
padding: 8px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
background-color: #fafafa;
}
</style>

View File

@ -0,0 +1,77 @@
<template>
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<MediaManageForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></MediaManageForm>
</j-modal>
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import MediaManageForm from './MediaManageForm.vue'
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
const width = ref<number>(800);
const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false);
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
/**
* 新增
*/
function add() {
title.value = '新增';
visible.value = true;
nextTick(() => {
registerForm.value.add();
});
}
/**
* 编辑
* @param record
*/
function edit(record) {
title.value = disableSubmit.value ? '详情' : '编辑';
visible.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
}
/**
* 确定按钮点击事件
*/
function handleOk() {
registerForm.value.submitForm();
}
/**
* form保存回调事件
*/
function submitCallback() {
handleCancel();
emit('success');
}
/**
* 取消按钮回调事件
*/
function handleCancel() {
visible.value = false;
}
defineExpose({
add,
edit,
disableSubmit,
});
</script>
<style lang="less">
/**隐藏样式-modal确定按钮 */
.jee-hidden {
display: none !important;
}
</style>
<style lang="less" scoped></style>

View File

@ -0,0 +1,57 @@
<template>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="fileType === 'image' && filePath" class="preview-item">
<a-image :width="200" :src="getFullPath(filePath)" />
</div>
<!-- 视频预览 -->
<div v-if="fileType === 'video' && filePath" class="preview-item">
<video controls :src="getFullPath(filePath)" style="width: 200px; height: auto;"></video>
</div>
<!-- 音频预览 -->
<div v-if="fileType === 'audio' && filePath" class="preview-item">
<audio controls :src="getFullPath(filePath)"></audio>
</div>
<!-- 文档预览 -->
<div v-if="fileType === 'document' && filePath" class="preview-item">
<a-button @click="handlePreviewDocument">预览文档</a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
const props = defineProps({
fileType: {
type: String,
default: ''
},
filePath: {
type: String,
default: ''
}
});
const getFullPath = (path) => {
return getFileAccessHttpUrl(path);
};
const handlePreviewDocument = () => {
window.open(getFullPath(props.filePath), '_blank');
};
</script>
<style scoped>
.preview-container {
margin-top: 10px;
}
.preview-item {
margin-bottom: 10px;
}
</style>

View File

@ -12,7 +12,7 @@
</a-col>
<a-col :span="24">
<a-form-item label="键名" v-bind="validateInfos.configKey" id="SysConfigForm-configKey" name="configKey">
<a-input v-model:value="formData.configKey" placeholder="请输入键名" allow-clear></a-input>
<a-input v-model:value="formData.configKey" placeholder="请输入键名" allow-clear :disabled="!!formData.id"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">

View File

@ -16,7 +16,7 @@
</a-col>
<a-col :span="5">
<span v-if="hasSearchConditions" style="font-size: 16px;font-weight: bold;color:#E86A6A">
有筛选条件未重置,{{ activeSearchConditions }}
有筛选条件未重置
</span>
<span style="font-size: 16px;font-weight: bold;color:#ABACAD">点击展开更多操作区域</span>
</a-col>

View File

@ -0,0 +1,327 @@
<template>
<SyncComponent ref="syncComRef" :selectedSize="Array.from(selectedItems.values()).length"
@changeShowJMCom="changeShowJMCom" @sourceOrgCodeChanged="sourceOrgCodeChanged"
@viewTypeChanged="viewTypeChanged" @orgChanged="orgChanged">
<!-- 表单 -->
<template #searchFormSlot>
<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="categoryId">
<template #label><span title="服务类别">服务类别</span></template>
<j-dict-select-tag type="list" v-model:value="queryParam.categoryId" v-if="showJMCom"
:orgCode="sourceOrgCode"
:dictCode="`nu_config_service_category,category_name,id,del_flag = 0 order by sort asc`"
:ignoreDisabled="true" placeholder="请选择服务类别" allow-clear />
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="typeId">
<template #label><span title="服务类型">服务类型</span></template>
<j-dict-select-tag type="list" v-model:value="queryParam.typeId" v-if="showJMCom"
:orgCode="sourceOrgCode"
:dictCode="`nu_config_service_type,type_name,id,del_flag = 0 order by sort asc`"
placeholder="请选择服务类型" :ignoreDisabled="true" allowClear />
</a-form-item>
</a-col>
<!-- <template v-if="toggleSearchStatus"> -->
<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 :lg="6">
<a-form-item name="instructionTagId">
<template #label><span title="分类标签">分类标签</span></template>
<j-dict-select-tag v-model:value="queryParam.instructionTagId" v-if="showJMCom"
:orgCode="sourceOrgCode" dictCode="instruction_tag" :ignoreDisabled="true"
placeholder="请选分类标签" allowClear />
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="bodyTags">
<template #label><span title="体型标签">体型标签</span></template>
<JSelectMultiple type="list" v-model:value="queryParam.bodyTags" v-if="showJMCom"
:orgCode="sourceOrgCode"
:dictCode="`nu_config_body_tag,tag_name,id,del_flag = '0' order by sort asc`"
:ignoreDisabled="true" placeholder="请选择体型标签" allowClear />
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="emotionTags">
<template #label><span title="情绪标签">情绪标签</span></template>
<JSelectMultiple type="list" v-model:value="queryParam.emotionTags" v-if="showJMCom"
:orgCode="sourceOrgCode"
:dictCode="`nu_config_emotion_tag,tag_name,id,del_flag = '0' order by sort asc`"
:ignoreDisabled="true" 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>
</template>
<!-- 试验田数据列表 -->
<template #sourceTableSlot>
<ConfigServiceDirectiveList @select-change="handleSelectChange" ref="listComRef" :queryParams="queryParam"
:initialDataIds="initialDataIds" />
</template>
<!-- 需要同步的机构的数据列表 -->
<template #businessTableSlot>
<a-table size="small" :columns="selectedColumns" :dataSource="Array.from(selectedItems.values())"
:pagination="false" :scroll="{ y: '55vh' }">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'action'">
<a-popconfirm placement="left" ok-text="确认" cancel-text="取消"
@confirm="handleRemoveFromRight(record.id)">
<template #title>
<span>是否确认移除</span>
</template>
<a-button type="link" danger size="small"
:disabled="initialDataIds.includes(record.id)">移除</a-button>
</a-popconfirm>
</template>
<template v-else-if="column.dataIndex === 'mp3File'">
<span v-if="!record.mp3File" style="font-size: 12px;font-style: italic;">无文件</span>
<audio controls v-else style="width: 100%; max-width: 300px; height: 40px;">
<source :src="getFileAccessHttpUrl(record.mp3File)">
</audio>
</template>
<template v-else-if="column.dataIndex === 'mp4File'">
<span v-if="!record.mp4File" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else type="primary" size="small" @click="openVideoModal(record.mp4File)">
播放视频
</a-button>
</template>
<template v-else-if="column.dataIndex === 'bodyTagList'">
<span v-if="!record.bodyTagList || record.bodyTagList.length === 0"
style="font-size: 12px;font-style: italic;">-</span>
<template v-else>
{{record.bodyTagList.map(item => item.tagName).join('、')}}
</template>
</template>
<template v-else-if="column.dataIndex === 'emotionTagList'">
<span v-if="!record.emotionTagList || record.emotionTagList.length === 0"
style="font-size: 12px;font-style: italic;">-</span>
<template v-else>
{{record.emotionTagList.map(item => item.tagName).join('、')}}
</template>
</template>
<template v-else-if="column.dataIndex === 'previewFile'">
<span v-if="!record.previewFile" style="font-size: 12px;font-style: italic;">无图片</span>
<img v-else :src="getFileAccessHttpUrl(record.previewFile)"
style="max-width: 100px; max-height: 60px;" />
</template>
<template v-else-if="column.dataIndex === 'immediateFile'">
<span v-if="!record.immediateFile" style="font-size: 12px;font-style: italic;">无图片</span>
<img v-else :src="getFileAccessHttpUrl(record.immediateFile)"
style="max-width: 100px; max-height: 60px;" />
</template>
</template>
</a-table>
</template>
</SyncComponent>
<a-modal v-model:visible="showVideoModal" title="视频播放" :footer="null" @cancel="closeVideoModal"
:bodyStyle="{ padding: '0', maxHeight: '80vh', overflow: 'auto' }">
<video controls style="width: 100%; max-height: '70vh'; display: block; margin: 0 auto;">
<source :src="videoUrl">
您的浏览器不支持视频播放
</video>
</a-modal>
</template>
<script setup name="synchronization-directive2" lang="ts">
//
import { ref, reactive } from 'vue'
import SyncComponent from '/@/components/Sync/SyncComponent.vue'
import JInput from "/@/components/Form/src/jeecg/components/JInput.vue";
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import JSelectMultiple from '/@/components/Form/src/jeecg/components/JSelectMultiple.vue';
import { useMessage } from "/@/hooks/web/useMessage";
//
//
import { list, asyncFunc } from '@/views/services/serviceDirective/ConfigServiceDirective.api';
import ConfigServiceDirectiveList from '@/views/services/serviceDirective/ConfigServiceDirectiveList.vue'
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils'
import { selectedColumns } from './org.data';
//
// >>>>>>>>>>>>>
const { createMessage } = useMessage();
const syncComRef = ref(null)
const sourceOrgCode = ref('')
const showJMCom = ref(false)
const labelCol = reactive({
xs: 24,
sm: 4,
xl: 6,
xxl: 4
});
const wrapperCol = reactive({
xs: 24,
sm: 20,
});
/**
* 获取到了试验田的机构编码
* @param v_
*/
const sourceOrgCodeChanged = (v_) => {
sourceOrgCode.value = v_
}
/**
* 是否展示设计dict的cj框架封装的组件因为需要获取的是对应试验田的数据字典当接口数据请求未返回时如果展示组件会报错
* 跟sourceOrgCodeChanged方法相配合
* @param v_
*/
const changeShowJMCom = (v_) => {
showJMCom.value = v_
}
// <<<<<<<<<<<<<
/**
* 机构变更
*/
const orgChanged = async (org) => {
//
selectedItems.value.clear();
//
initialDataIds.value = [];
try {
//
const res = await list({ dataSourceCode: org.orgCode, pageNo: 1, pageSize: -1 });
//
if (res.records && res.records.length > 0) {
const newRecords = res.records;
newRecords.forEach(record => {
selectedItems.value.set(record.id, record);
if (!syncComRef.value.orgData.value?.operationStartTime || new Date() >= new Date(syncComRef.value.orgData.value?.operationStartTime)) {
initialDataIds.value.push(record.id);
}
});
//
listComRef.value?.updateSelection?.(newRecords);
} else {
//
selectedItems.value.clear();
listComRef.value?.updateSelection?.([]);
}
//
syncComRef.value.resetOrgSelectedCon(false);
console.log("🌊 ~ orgChanged ~ selectedItems.value:", selectedItems.value)
} catch (err) {
console.error('机构数据查询失败:', err);
createMessage.error('机构数据查询失败');
//
selectedItems.value.clear();
listComRef.value?.updateSelection?.([]);
}
}
// >>>>>>>>>>>>>
// <<<<<<<<<<<<<
// >>>>>>>>>>>>>
const listComRef = ref();
const selectedItems = ref(new Map<string | number, any>());
const queryParam = ref({ viewType: 'all' })//
const initialDataIds = ref<string[]>([]);
const showVideoModal = ref(false);
const videoUrl = ref('');
/**
* 查询
*/
function searchQuery() {
listComRef.value.reload();
}
/**
* 重置
*/
function searchReset() {
let vt = queryParam.value.viewType
queryParam.value = { viewType: vt }
listComRef.value?.reload();
}
/**
* 源数据的全部已选择未选择变更时触发
* @param v_ allselectedunselected
*/
const viewTypeChanged = (v_) => {
queryParam.value.viewType = v_
//queryParam
}
const handleSelectChange = (items: Map<string | number, any>) => {
selectedItems.value = new Map(items);
};
const handleRemoveFromRight = (key: string | number) => {
selectedItems.value.delete(key);
listComRef.value?.removeSelectedItem?.(key);
};
const openVideoModal = (url: string) => {
videoUrl.value = getFileAccessHttpUrl(url);
showVideoModal.value = true;
};
const closeVideoModal = () => {
showVideoModal.value = false;
videoUrl.value = '';
};
// <<<<<<<<<<<<<
</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%;
}
}
.selected-list-container {
:deep(.ant-table) {
width: 100% !important;
max-width: 100%;
}
}
</style>

View File

@ -65,7 +65,7 @@ export const saveOrUpdateDict = (params, isUpdate) => {
*
* @param params
*/
export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData, params });
export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData,timeout:100*1000, params });
/**
*
* @param params

View File

@ -64,4 +64,10 @@ export const formSchema: FormSchema[] = [
required: true,
component: 'Input',
},
{
label: '编码(非必要无需填写)',
field: 'code',
required: false,
component: 'Input',
},
];

View File

@ -1,5 +1,5 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose width="550px" :title="getTitle" @ok="handleSubmit">
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose width="50vw" :title="getTitle" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
@ -21,11 +21,11 @@
showActionButtonGroup: false,
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
sm: { span: 15 },
},
});
//

View File

@ -147,6 +147,9 @@
* 成功回调
*/
async function handleSuccess({ isUpdate,isSubAdd, values, expandedArr }) {
reload();
return
// 🤡
if (isUpdate) {
//
updateTableDataRecord(values.id, values);

View File

@ -1,96 +1,142 @@
<template>
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
<BasicForm @register="registerForm" />
<BasicForm @register="registerForm">
<!-- 省份 -->
<template #province="{ model, field }">
<j-dict-select-tag @change="provinceChanged(model)" v-model:value="model[field]"
:dictCode="getProvinceDictCode()" placeholder="请选择所在省份" :disabled="model.orgCategory != '1'" />
</template>
<!-- 城市 -->
<template #city="{ model, field }">
<j-dict-select-tag @change="cityChanged(model)" v-model:value="model[field]" :dictCode="getCityDictCode(model)"
placeholder="请选择所在城市" :disabled="model.orgCategory != '1' && !model.province" />
</template>
<!-- 区县 -->
<template #district="{ model, field }">
<j-dict-select-tag v-model:value="model[field]" :dictCode="getDistrictDictCode(model)" placeholder="请选择所在区县"
:disabled="model.orgCategory != '1' && (!model.province || !model.city)" />
</template>
</BasicForm>
</BasicModal>
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
const emit = defineEmits(['success', 'register']);
const props = defineProps({
rootTreeData: { type: Array, default: () => [] },
});
const prefixCls = inject('prefixCls');
//
const isUpdate = ref<boolean>(false);
//
const model = ref<object>({});
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
const emit = defineEmits(['success', 'register']);
const props = defineProps({
rootTreeData: { type: Array, default: () => [] },
});
const prefixCls = inject('prefixCls');
//
const isUpdate = ref<boolean>(false);
//
const model = ref<object>({});
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
isUpdate.value = unref(data?.isUpdate);
//
let isChild = unref(data?.isChild);
let categoryOptions = isChild ? orgCategoryOptions.child : orgCategoryOptions.root;
//
updateSchema([
{
field: 'parentId',
show: isChild,
componentProps: {
//
disabled: isChild,
treeData: props.rootTreeData,
},
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
isUpdate.value = unref(data?.isUpdate);
//
let isChild = unref(data?.isChild);
let categoryOptions = isChild ? orgCategoryOptions.child : orgCategoryOptions.root;
//
updateSchema([
{
field: 'parentId',
show: isChild,
componentProps: {
//
disabled: isChild,
treeData: props.rootTreeData,
},
{
field: 'platType',
show: !isChild
},
{
field: 'orgCode',
show: false,
},
{
field: 'orgCategory',
componentProps: { options: categoryOptions },
},
]);
},
{
field: 'platType',
show: !isChild
},
{
field: 'orgCode',
show: false,
},
{
field: 'orgCategory',
componentProps: { options: categoryOptions },
},
]);
let record = unref(data?.record);
if (typeof record !== 'object') {
record = {};
}
//
record = Object.assign(
{
departOrder: 0,
orgCategory: categoryOptions[0].value,
},
record
);
model.value = record;
await setFieldsValue({ ...record });
});
//
async function handleOk() {
try {
setModalProps({ confirmLoading: true });
let values = await validate();
//
await saveOrUpdateDepart(values, isUpdate.value);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
let record = unref(data?.record);
if (typeof record !== 'object') {
record = {};
}
//
record = Object.assign(
{
departOrder: 0,
orgCategory: categoryOptions[0].value,
},
record
);
model.value = record;
await setFieldsValue({ ...record });
});
//
async function handleOk() {
try {
setModalProps({ confirmLoading: true });
let values = await validate();
//
await saveOrUpdateDepart(values, isUpdate.value);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
/**
* 省份字典
*/
function getProvinceDictCode() {
return 'sys_category,name,id,pid = \'xzqhdm\' order by code asc'
}
function provinceChanged(model) {
model.city = null
model.district = null
}
/**
* 城市字典
* @param v_
*/
function getCityDictCode(v_) {
return 'sys_category,name,id,pid = \'' + v_.province + '\' order by code asc'
}
function cityChanged(model) {
model.district = null
}
/**
* 区县字典
* @param v_
*/
function getDistrictDictCode(v_) {
return 'sys_category,name,id,pid = \'' + v_.city + '\' order by code asc'
}
</script>

View File

@ -1,6 +1,22 @@
<template>
<a-spin :spinning="loading">
<BasicForm @register="registerForm" />
<BasicForm @register="registerForm">
<!-- 省份 -->
<template #province="{ model, field }">
<j-dict-select-tag @change="provinceChanged(model)" v-model:value="model[field]"
:dictCode="getProvinceDictCode()" placeholder="请选择所在省份" :disabled="model.orgCategory != '1'" />
</template>
<!-- 城市 -->
<template #city="{ model, field }">
<j-dict-select-tag @change="cityChanged(model)" v-model:value="model[field]" :dictCode="getCityDictCode(model)"
placeholder="请选择所在城市" :disabled="model.orgCategory != '1' && !model.province" />
</template>
<!-- 区县 -->
<template #district="{ model, field }">
<j-dict-select-tag v-model:value="model[field]" :dictCode="getDistrictDictCode(model)" placeholder="请选择所在区县"
:disabled="model.orgCategory != '1' && (!model.province || !model.city)" />
</template>
</BasicForm>
<div class="j-box-bottom-button offset-20" style="margin-top: 30px">
<div class="j-box-bottom-button-float" :class="[`${prefixCls}`]">
<a-button preIcon="ant-design:sync-outlined" @click="onReset">重置</a-button>
@ -11,118 +27,148 @@
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import { useDesign } from '/@/hooks/web/useDesign';
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import { useDesign } from '/@/hooks/web/useDesign';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
const { prefixCls } = useDesign('j-depart-form-content');
const { prefixCls } = useDesign('j-depart-form-content');
const emit = defineEmits(['success']);
const props = defineProps({
data: { type: Object, default: () => ({}) },
rootTreeData: { type: Array, default: () => [] },
});
const loading = ref<boolean>(false);
//
const isUpdate = ref<boolean>(true);
//
const model = ref<object>({});
const emit = defineEmits(['success']);
const props = defineProps({
data: { type: Object, default: () => ({}) },
rootTreeData: { type: Array, default: () => [] },
});
const loading = ref<boolean>(false);
//
const isUpdate = ref<boolean>(true);
//
const model = ref<object>({});
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
const categoryOptions = computed(() => {
if (!!props?.data?.parentId) {
return orgCategoryOptions.child;
} else {
return orgCategoryOptions.root;
}
});
onMounted(() => {
//
updateSchema([
{ field: 'parentId', componentProps: { disabled: true } },
{ field: 'orgCode', componentProps: { disabled: true } },
]);
// data
watch(
() => props.data,
async () => {
let record = unref(props.data);
if (typeof record !== 'object') {
record = {};
}
model.value = record;
await resetFields();
await setFieldsValue({ ...record });
},
{ deep: true, immediate: true }
);
//
watch(
() => props.rootTreeData,
async () => {
updateSchema([
{
field: 'parentId',
componentProps: { treeData: props.rootTreeData },
},
]);
},
{ deep: true, immediate: true }
);
// orgCategory options
watch(
categoryOptions,
async () => {
updateSchema([
{
field: 'orgCategory',
componentProps: { options: categoryOptions.value },
},
]);
},
{ immediate: true }
);
});
//
async function onReset() {
await resetFields();
await setFieldsValue({ ...model.value });
const categoryOptions = computed(() => {
if (!!props?.data?.parentId) {
return orgCategoryOptions.child;
} else {
return orgCategoryOptions.root;
}
});
//
async function onSubmit() {
try {
loading.value = true;
let values = await validate();
values = Object.assign({}, model.value, values);
//
await saveOrUpdateDepart(values, isUpdate.value);
//
emit('success');
Object.assign(model.value, values);
} finally {
loading.value = false;
}
onMounted(() => {
//
updateSchema([
{ field: 'parentId', componentProps: { disabled: true } },
{ field: 'orgCode', componentProps: { disabled: true } },
]);
// data
watch(
() => props.data,
async () => {
let record = unref(props.data);
if (typeof record !== 'object') {
record = {};
}
model.value = record;
await resetFields();
await setFieldsValue({ ...record });
},
{ deep: true, immediate: true }
);
//
watch(
() => props.rootTreeData,
async () => {
updateSchema([
{
field: 'parentId',
componentProps: { treeData: props.rootTreeData },
},
]);
},
{ deep: true, immediate: true }
);
// orgCategory options
watch(
categoryOptions,
async () => {
updateSchema([
{
field: 'orgCategory',
componentProps: { options: categoryOptions.value },
},
]);
},
{ immediate: true }
);
});
//
async function onReset() {
await resetFields();
await setFieldsValue({ ...model.value });
}
//
async function onSubmit() {
try {
loading.value = true;
let values = await validate();
values = Object.assign({}, model.value, values);
//
await saveOrUpdateDepart(values, isUpdate.value);
//
emit('success');
Object.assign(model.value, values);
} finally {
loading.value = false;
}
}
/**
* 省份字典
*/
function getProvinceDictCode() {
return 'sys_category,name,id,pid = \'xzqhdm\' order by code asc'
}
function provinceChanged(model) {
model.city = null
model.district = null
}
/**
* 城市字典
* @param v_
*/
function getCityDictCode(v_) {
return 'sys_category,name,id,pid = \'' + v_.province + '\' order by code asc'
}
function cityChanged(model) {
model.district = null
}
/**
* 区县字典
* @param v_
*/
function getDistrictDictCode(v_) {
return 'sys_category,name,id,pid = \'' + v_.city + '\' order by code asc'
}
</script>
<style lang="less">
// update-begin-author:liusq date:20230625 for: [issues/563]
// update-begin-author:liusq date:20230625 for: [issues/563]
@prefix-cls: ~'@{namespace}-j-depart-form-content';
/*begin 兼容暗夜模式*/
.@{prefix-cls} {
background: @component-background;
border-top: 1px solid @border-color-base;
}
/*end 兼容暗夜模式*/
// update-end-author:liusq date:20230625 for: [issues/563]
</style>
@prefix-cls: ~'@{namespace}-j-depart-form-content';
/*begin 兼容暗夜模式*/
.@{prefix-cls} {
background: @component-background;
border-top: 1px solid @border-color-base;
}
/*end 兼容暗夜模式*/
// update-end-author:liusq date:20230625 for: [issues/563]</style>

View File

@ -36,6 +36,24 @@ export function useBasicFormSchema() {
component: 'RadioButtonGroup',
componentProps: { options: [] },
},
{
field: 'province',
label: '省份',
component: 'Input',
slot: 'province',
},
{
field: 'city',
label: '城市',
component: 'Input',
slot: 'city',
},
{
field: 'district',
label: '区县',
component: 'Input',
slot: 'district',
},
{
field: 'platType',
label: '业务平台类型',
@ -49,12 +67,6 @@ export function useBasicFormSchema() {
message: '请选择平台类型'
}]
},
{
field: 'departOrder',
label: '排序',
component: 'InputNumber',
componentProps: {},
},
{
field: 'operationStartTime',
label: '运营开始时间',
@ -131,6 +143,12 @@ export function useBasicFormSchema() {
placeholder: '请输入备注',
},
},
{
field: 'departOrder',
label: '排序',
component: 'InputNumber',
componentProps: {},
},
{
field: 'picUrl',
label: '机构图片',

View File

@ -37,6 +37,7 @@
<a-modal v-model:open="showAsyncResult" title="同步结果" @ok="showAsyncResult = false" width="70vw">
<a-tabs v-model:activeKey="activeTabKey" style="margin-left: 20px;margin-right: 20px;margin-bottom: 20px;">
<template #rightExtra>
<span style="color:red;font-weight: bold;margin-right: 10px;">同步为增量同步不会修改删除业务系统数据</span>
<a-button type="primary" @click="AsyncResultFunc({ id: opeDictId })" size="small"
preIcon="ant-design:sync-outlined" style="margin-right: 10px;">刷新</a-button>
</template>