Merge branch 'dev' into dev-hub
This commit is contained in:
commit
af8f951ed8
|
@ -164,5 +164,6 @@ function syncTriggerClass(
|
|||
.j-ellipsis-line-clamp {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
|
@ -45,6 +45,7 @@ initAMapApiLoader({
|
|||
|
||||
interface EmitProps {
|
||||
(e: 'update:point', data: string): void;
|
||||
(e: 'change', data: string): void;
|
||||
}
|
||||
const props = defineProps({
|
||||
point: { type: [Number, String], default: '' },
|
||||
|
@ -59,6 +60,7 @@ const inputPoint = computed({
|
|||
set: (val: any) => {
|
||||
mapPoint.value = val;
|
||||
emit('update:point', val);
|
||||
emit('change', val);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<template v-if="isPermission">
|
||||
<template v-if="popConfirm">
|
||||
<j-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled">
|
||||
<j-popconfirm :overlayStyle='{minWidth: "180px"}' v-bind="popConfirm" :disabled="!isPermission || props.disabled">
|
||||
<j-tooltip v-if="tooltip" v-bind="tooltip">
|
||||
<slot v-if="noButton"></slot>
|
||||
<j-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<GeoComponent
|
||||
v-else-if="typeMap.get(itemType) === 'geoPoint'"
|
||||
v-model:point="myValue"
|
||||
@change='inputChange'
|
||||
/>
|
||||
<j-input
|
||||
v-else-if="typeMap.get(itemType) === 'file'"
|
||||
|
@ -172,6 +173,7 @@ const objectValue = ref<string>('');
|
|||
const handleItemModalSubmit = () => {
|
||||
myValue.value = objectValue.value.replace(/[\r\n]\s*/g, '');
|
||||
modalVis.value = false;
|
||||
emit('change', objectValue.value)
|
||||
};
|
||||
|
||||
// 文件上传
|
||||
|
|
|
@ -120,7 +120,7 @@ export interface SearchItemData {
|
|||
export const handleParamsToString = (terms:SearchItemData[] = []) => {
|
||||
const _terms: any[] = [
|
||||
{ terms: [null,null,null]},
|
||||
{ terms: [null,null,null]}
|
||||
{ terms: [null,null,null], type: 'and'}
|
||||
]
|
||||
let termsIndex = 0
|
||||
let termsStar = 0
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import axios from 'axios'
|
||||
import {BASE_API_PATH, TOKEN_KEY} from '@/utils/variable'
|
||||
import { notification as Notification } from 'ant-design-vue'
|
||||
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'
|
||||
import { notification as Notification } from 'ant-design-vue'
|
||||
import router from '@/router'
|
||||
import { LoginPath } from '@/router/menu'
|
||||
import {LocalStore} from "@/utils/comm";
|
||||
import { cleanToken, getToken, LocalStore } from '@/utils/comm'
|
||||
import type { AxiosInstance, AxiosResponse } from 'axios'
|
||||
|
||||
interface AxiosResponseRewrite<T = any[]> extends AxiosResponse<T, any> {
|
||||
result: T
|
||||
success: boolean
|
||||
result: T
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export const SUCCESS_CODE = 200 // 成功代码
|
||||
|
||||
export const request = axios.create({
|
||||
withCredentials: false,
|
||||
baseURL: BASE_API_PATH,
|
||||
timeout: 1000 * 60 * 5
|
||||
withCredentials: false,
|
||||
baseURL: BASE_API_PATH,
|
||||
timeout: 1000 * 60 * 5
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -30,15 +30,15 @@ export const request = axios.create({
|
|||
}
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const post = function<T>(url: string, data = {}, params = {}, ext={}) {
|
||||
ext = typeof ext === 'string' ? { responseType: ext } : ext
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
...ext,
|
||||
params,
|
||||
method: 'POST',
|
||||
url,
|
||||
data
|
||||
})
|
||||
export const post = function <T>(url: string, data = {}, params = {}, ext = {}) {
|
||||
ext = typeof ext === 'string' ? { responseType: ext } : ext
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
...ext,
|
||||
params,
|
||||
method: 'POST',
|
||||
url,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,12 +47,12 @@ export const post = function<T>(url: string, data = {}, params = {}, ext={}) {
|
|||
* @param {Object} [data]
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const put = function<T>(url: string, data = {},) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'PUT',
|
||||
url,
|
||||
data
|
||||
})
|
||||
export const put = function <T>(url: string, data = {}) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'PUT',
|
||||
url,
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,12 +61,12 @@ export const put = function<T>(url: string, data = {},) {
|
|||
* @param {Object} [data]
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const patch = function<T>(url: string, data = {}) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'PATCH',
|
||||
url,
|
||||
data
|
||||
})
|
||||
export const patch = function <T>(url: string, data = {}) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'PATCH',
|
||||
url,
|
||||
data
|
||||
})
|
||||
}
|
||||
/**
|
||||
* GET method request
|
||||
|
@ -75,13 +75,13 @@ export const patch = function<T>(url: string, data = {}) {
|
|||
* @param {Object} [ext] 扩展参数
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const get = function<T>(url: string, params = {}, ext?: any) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'GET',
|
||||
url,
|
||||
params,
|
||||
...ext
|
||||
})
|
||||
export const get = function <T>(url: string, params = {}, ext?: any) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'GET',
|
||||
url,
|
||||
params,
|
||||
...ext
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,13 +91,13 @@ export const get = function<T>(url: string, params = {}, ext?: any) {
|
|||
* @param {Object} [ext] 扩展参数
|
||||
* @returns {AxiosInstance}
|
||||
*/
|
||||
export const remove = function<T>(url: string, params = {}, ext?: any) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'DELETE',
|
||||
url,
|
||||
params,
|
||||
...ext
|
||||
})
|
||||
export const remove = function <T>(url: string, params = {}, ext?: any) {
|
||||
return request<any, AxiosResponseRewrite<T>>({
|
||||
method: 'DELETE',
|
||||
url,
|
||||
params,
|
||||
...ext
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,25 +107,25 @@ export const remove = function<T>(url: string, params = {}, ext?: any) {
|
|||
* @return {*}
|
||||
*/
|
||||
export const getStream = function(url: string, params = {}) {
|
||||
return get<any>(url, params, {
|
||||
responseType: 'arraybuffer' // 设置请求数据类型,返回blob可解析类型
|
||||
})
|
||||
return get<any>(url, params, {
|
||||
responseType: 'arraybuffer' // 设置请求数据类型,返回blob可解析类型
|
||||
})
|
||||
}
|
||||
|
||||
export const postStream = function(url: string, data={}, params = {}) {
|
||||
return post<any>(url, data, params, {
|
||||
responseType: 'arraybuffer' // 设置请求数据类型,返回blob可解析类型
|
||||
})
|
||||
export const postStream = function(url: string, data = {}, params = {}) {
|
||||
return post<any>(url, data, params, {
|
||||
responseType: 'arraybuffer' // 设置请求数据类型,返回blob可解析类型
|
||||
})
|
||||
}
|
||||
|
||||
const showNotification = ( message: string, description: string, key?: string, show: boolean = true ) => {
|
||||
if (show) {
|
||||
Notification.error({
|
||||
key,
|
||||
message,
|
||||
description
|
||||
})
|
||||
}
|
||||
const showNotification = (message: string, description: string, key?: string, show: boolean = true) => {
|
||||
if (show) {
|
||||
Notification.error({
|
||||
key,
|
||||
message,
|
||||
description
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,87 +134,79 @@ const showNotification = ( message: string, description: string, key?: string, s
|
|||
* @returns {Promise<never>}
|
||||
*/
|
||||
const errorHandler = (error: any) => {
|
||||
if (error.response) {
|
||||
const data = error.response.data
|
||||
const status = error.response.status
|
||||
if (status === 403) {
|
||||
showNotification( 'Forbidden', (data.message + '').substr(0, 90), '403')
|
||||
} else if (status === 500) {
|
||||
showNotification( 'Server Side Error', (data.message + '').substr(0, 90), '500')
|
||||
} else if (status === 400) {
|
||||
showNotification( 'Request Error', (data.message + '').substr(0, 90), '400')
|
||||
} else if (status === 401) {
|
||||
showNotification( 'Unauthorized', '用户未登录', '401')
|
||||
console.log('showNotification')
|
||||
setTimeout(() => {
|
||||
location.href = `/#${LoginPath}`
|
||||
}, 0)
|
||||
}
|
||||
} else if (error.response === undefined) {
|
||||
showNotification( error.message, (error.stack + '').substr(0, 90), undefined)
|
||||
if (error.response) {
|
||||
const data = error.response.data
|
||||
const status = error.response.status
|
||||
if (status === 403) {
|
||||
showNotification('Forbidden', (data.message + '').substr(0, 90), '403')
|
||||
} else if (status === 500) {
|
||||
showNotification('Server Side Error', (data.message + '').substr(0, 90), '500')
|
||||
} else if (status === 400) {
|
||||
showNotification('Request Error', (data.message + '').substr(0, 90), '400')
|
||||
} else if (status === 401) {
|
||||
showNotification('Unauthorized', '用户未登录', '401')
|
||||
setTimeout(() => {
|
||||
cleanToken()
|
||||
router.replace({
|
||||
path: LoginPath
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
} else if (error.response === undefined) {
|
||||
showNotification(error.message, (error.stack + '').substr(0, 90), undefined)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
// request interceptor
|
||||
request.interceptors.request.use(config => {
|
||||
// 如果 token 存在
|
||||
// 让每个请求携带自定义 token 请根据实际情况自行修改
|
||||
const token = LocalStore.get(TOKEN_KEY)
|
||||
// const token = store.$state.tokenAlias
|
||||
if (!token) {
|
||||
// setTimeout(() => {
|
||||
// router.replace({
|
||||
// path: LoginPath
|
||||
// })
|
||||
// }, 0)
|
||||
setTimeout(() => {
|
||||
location.href = `/#${LoginPath}`
|
||||
}, 0)
|
||||
return config
|
||||
}
|
||||
|
||||
config.headers![TOKEN_KEY] = token
|
||||
|
||||
// 如果 token 存在
|
||||
// 让每个请求携带自定义 token 请根据实际情况自行修改
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
setTimeout(() => {
|
||||
cleanToken()
|
||||
router.replace({
|
||||
path: LoginPath
|
||||
})
|
||||
}, 0)
|
||||
return config
|
||||
}
|
||||
|
||||
config.headers![TOKEN_KEY] = token
|
||||
|
||||
return config
|
||||
}, errorHandler)
|
||||
|
||||
/**
|
||||
* response interceptor
|
||||
*/
|
||||
request.interceptors.response.use(response => {
|
||||
if (response.data instanceof ArrayBuffer) {
|
||||
return response
|
||||
} else {
|
||||
const { status, message } = response.data
|
||||
// 增加业务接口处理成功判断方式,只需要判断返回参数包含:success为true
|
||||
if (typeof response.data === 'object' && typeof response.data.success === 'undefined') {
|
||||
response.data.success = status === SUCCESS_CODE
|
||||
}
|
||||
|
||||
// 统一显示异常业务信息
|
||||
if (status !== SUCCESS_CODE && message) {
|
||||
Notification.error({
|
||||
message: 'Server Errors: ' + status,
|
||||
description: message
|
||||
})
|
||||
}
|
||||
// 如果返回的的是文件流,那么return值则为response
|
||||
if (response.headers['content-type'] === 'application/octet-stream; charset=UTF-8' || response.headers['content-type'] === 'application/vnd.ms-excel;charset=UTF-8') {
|
||||
return response.data
|
||||
} else {
|
||||
return response.data
|
||||
}
|
||||
if (response.data instanceof ArrayBuffer) {
|
||||
return response
|
||||
} else {
|
||||
const { status, message } = response.data
|
||||
// 增加业务接口处理成功判断方式,只需要判断返回参数包含:success为true
|
||||
if (typeof response.data === 'object' && typeof response.data.success === 'undefined') {
|
||||
response.data.success = status === SUCCESS_CODE
|
||||
}
|
||||
|
||||
// 如果返回的的是文件流,那么return值则为response
|
||||
if (response.headers['content-type'] === 'application/octet-stream; charset=UTF-8' || response.headers['content-type'] === 'application/vnd.ms-excel;charset=UTF-8') {
|
||||
return response.data
|
||||
} else {
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
}, errorHandler)
|
||||
|
||||
export default {
|
||||
request: axios,
|
||||
post,
|
||||
get,
|
||||
patch,
|
||||
put,
|
||||
remove,
|
||||
getStream,
|
||||
postStream
|
||||
request: axios,
|
||||
post,
|
||||
get,
|
||||
patch,
|
||||
put,
|
||||
remove,
|
||||
getStream,
|
||||
postStream
|
||||
}
|
|
@ -84,6 +84,7 @@ import TopCard from '@/views/device/DashBoard/components/TopCard.vue';
|
|||
import { useMenuStore } from '@/store/menu';
|
||||
import Amap from './components/Amap.vue';
|
||||
import { useSystem } from '@/store/system';
|
||||
import dayjs from 'dayjs'
|
||||
const system = useSystem();
|
||||
const AmapKey = system.$state.configInfo.amap?.apiKey;
|
||||
let productTotal = ref(0);
|
||||
|
@ -206,6 +207,9 @@ getDeviceData();
|
|||
* 获取在线数量
|
||||
*/
|
||||
const getOnline = () => {
|
||||
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
const endTime = dayjs().subtract(0, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
dashboard([
|
||||
{
|
||||
dashboard: 'device',
|
||||
|
@ -215,26 +219,34 @@ const getOnline = () => {
|
|||
group: 'aggOnline',
|
||||
params: {
|
||||
state: 'online',
|
||||
limit: 15,
|
||||
from: 'now-15d',
|
||||
time: '1d',
|
||||
format: 'yyyy-MM-dd',
|
||||
limit: 24,
|
||||
from: startTime,
|
||||
to: endTime,
|
||||
time: '1h',
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
},
|
||||
},
|
||||
]).then((res) => {
|
||||
if (res.status == 200) {
|
||||
const x = res.result
|
||||
.map((item: any) => item.data.timeString)
|
||||
.reverse();
|
||||
const y = res.result.map((item: any) => item.data.value);
|
||||
// const x = res.result
|
||||
// .map((item: any) => item.data.timeString)
|
||||
// .reverse();
|
||||
// const y = res.result.map((item: any) => item.data.value);
|
||||
const x: string[] = [];
|
||||
const y: number[] = [];
|
||||
(res.result as any)?.forEach((item: any) => {
|
||||
x.push(item.data.timeString)
|
||||
y.push(item.data.value)
|
||||
})
|
||||
x.reverse()
|
||||
const onlineYdata = y;
|
||||
onlineYdata.reverse();
|
||||
setOnlineChartOpition(x, onlineYdata);
|
||||
setOnlineChartOption(x, onlineYdata);
|
||||
onlineFooter.value[0].value = y?.[1];
|
||||
}
|
||||
});
|
||||
};
|
||||
const setOnlineChartOpition = (x: Array<any>, y: Array<number>): void => {
|
||||
const setOnlineChartOption = (x: Array<any>, y: Array<number>): void => {
|
||||
onlineOptions.value = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
|
|
|
@ -61,26 +61,10 @@
|
|||
"
|
||||
:itemType="record.type"
|
||||
:options="
|
||||
record.type === 'enum'
|
||||
? (
|
||||
record?.options
|
||||
?.elements || []
|
||||
).map((item:any) => ({
|
||||
label: item.text,
|
||||
value: item.value,
|
||||
}))
|
||||
: record.type === 'boolean'
|
||||
? [
|
||||
{
|
||||
label: '是',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
label: '否',
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
(record?.options || []).map((item:any) => ({
|
||||
label: item.text,
|
||||
value: item.value,
|
||||
}))
|
||||
"
|
||||
/>
|
||||
</j-form-item>
|
||||
|
@ -107,7 +91,10 @@
|
|||
</j-col>
|
||||
<j-col :span="9">
|
||||
<h6>执行结果:</h6>
|
||||
<span :ref="`result${func.id}Ref`" class="execute-result">
|
||||
<span
|
||||
:ref="`result${func.id}Ref`"
|
||||
class="execute-result"
|
||||
>
|
||||
{{ func.executeResult }}
|
||||
</span>
|
||||
</j-col>
|
||||
|
@ -192,7 +179,7 @@ const newFunctions = computed(() => {
|
|||
executeResult: '',
|
||||
});
|
||||
});
|
||||
// console.log('newFunctions: ', result)
|
||||
// console.log('newFunctions: ', result);
|
||||
return result;
|
||||
});
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
)
|
||||
: record.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
{ label: record?.dataType?.trueText, value: record?.dataType?.trueValue },
|
||||
{ label: record?.dataType?.falseText, value: record?.dataType?.falseValue },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="选择产品" v-bind="validateInfos.copy" v-if="formModel.type === 'copy'">
|
||||
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label"></j-select>
|
||||
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label" showSearch></j-select>
|
||||
</j-form-item>
|
||||
<j-form-item label="物模型类型" v-bind="validateInfos.metadata" v-if="type === 'device' || formModel.type === 'import'">
|
||||
<j-select v-model:value="formModel.metadata">
|
||||
|
@ -209,19 +209,20 @@ const handleImport = async () => {
|
|||
const res = await convertMetadata('from', 'alink', data.import).catch(err => err)
|
||||
if (res.status === 200) {
|
||||
const metadata = operateLimits(res.result)
|
||||
let result;
|
||||
if (props?.type === 'device') {
|
||||
await saveMetadata(id as string, metadata).catch(err => err)
|
||||
// instanceStore.setCurrent(JSON.parse(metadata || '{}'))
|
||||
result = await saveMetadata(id as string, metadata).catch(err => err)
|
||||
} else {
|
||||
await modify(id as string, { metadata: metadata }).catch(err => err)
|
||||
// productStore.setCurrent(JSON.parse(metadata || '{}'))
|
||||
result = await modify(id as string, { metadata: metadata }).catch(err => err)
|
||||
}
|
||||
if (result.success) {
|
||||
message.success('导入成功')
|
||||
}
|
||||
loading.value = false
|
||||
// MetadataAction.insert(JSON.parse(metadata || '{}'));
|
||||
message.success('导入成功')
|
||||
} else {
|
||||
loading.value = false
|
||||
message.error('发生错误!')
|
||||
// message.error('物模型数据不正确!')
|
||||
return
|
||||
}
|
||||
if (props?.type === 'device') {
|
||||
instanceStore.refresh(id as string)
|
||||
|
|
|
@ -176,7 +176,7 @@
|
|||
<div class="card-item-content-text">
|
||||
绑定设备
|
||||
</div>
|
||||
<div>{{ slotProps.deviceName }}</div>
|
||||
<Ellipsis>{{ slotProps.deviceName }}</Ellipsis>
|
||||
</j-col>
|
||||
</j-row>
|
||||
<j-divider style="margin: 12px 0" />
|
||||
|
|
|
@ -25,7 +25,14 @@
|
|||
<div class="tool-item" @click.stop="handleRefresh">
|
||||
刷新
|
||||
</div>
|
||||
<div class="tool-item" @click.stop="handleReset">重置</div>
|
||||
<div class="tool-item">
|
||||
<j-popconfirm
|
||||
title="重置将断开直播, 可能会影响其他播放者"
|
||||
@confirm="() => handleReset"
|
||||
>
|
||||
重置
|
||||
</j-popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
<LivePlayer
|
||||
ref="player"
|
||||
|
|
|
@ -156,11 +156,11 @@
|
|||
{{ getProviderTxt(slotProps.type, slotProps.provider) }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- <template #description="slotProps">
|
||||
<template #description="slotProps">
|
||||
<Ellipsis>
|
||||
{{ slotProps.description }}
|
||||
</Ellipsis>
|
||||
</template> -->
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<j-space :size="16">
|
||||
<template
|
||||
|
@ -194,7 +194,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import ConfigApi from '@/api/notice/config';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import type { ActionsType } from '@/views/device/Instance/typings';
|
||||
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
|
@ -380,18 +380,6 @@ const getActions = (
|
|||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
},
|
||||
icon: 'BarsOutlined',
|
||||
onClick: () => {
|
||||
logVis.value = true;
|
||||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
text: '删除',
|
||||
|
@ -439,6 +427,18 @@ const getActions = (
|
|||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
},
|
||||
icon: 'BarsOutlined',
|
||||
onClick: () => {
|
||||
logVis.value = true;
|
||||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -447,7 +447,7 @@ const getActions = (
|
|||
data.provider !== 'dingTalkMessage' &&
|
||||
data.provider !== 'corpMessage'
|
||||
)
|
||||
others.children.splice(1, 1);
|
||||
others.children?.splice(1, 1);
|
||||
actions.splice(actions.length - 1, 0, others);
|
||||
return actions;
|
||||
} else {
|
||||
|
@ -455,7 +455,7 @@ const getActions = (
|
|||
data.provider !== 'dingTalkMessage' &&
|
||||
data.provider !== 'corpMessage'
|
||||
)
|
||||
others.children.splice(1, 1);
|
||||
others.children?.splice(1, 1);
|
||||
actions.splice(actions.length - 1, 0, ...others.children);
|
||||
return actions;
|
||||
}
|
||||
|
|
|
@ -90,24 +90,65 @@
|
|||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:popConfirm="item.popConfirm"
|
||||
:tooltip="{
|
||||
...item.tooltip,
|
||||
}"
|
||||
@click="item.onClick"
|
||||
:hasPermission="'notice/Template:' + item.key"
|
||||
<j-tooltip
|
||||
v-bind="item.tooltip"
|
||||
:title="item.disabled && item.tooltip.title"
|
||||
>
|
||||
<AIcon
|
||||
type="DeleteOutlined"
|
||||
v-if="item.key === 'delete'"
|
||||
/>
|
||||
<j-dropdown
|
||||
placement="bottomRight"
|
||||
v-if="item.key === 'others'"
|
||||
>
|
||||
<j-button>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</j-button>
|
||||
<template #overlay>
|
||||
<j-menu>
|
||||
<j-menu-item
|
||||
v-for="(o, i) in item.children"
|
||||
:key="i"
|
||||
>
|
||||
<PermissionButton
|
||||
type="link"
|
||||
@click="o.onClick"
|
||||
:hasPermission="`notice/Template:${o.key}`"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon :type="o.icon" />
|
||||
</template>
|
||||
<span>{{ o.text }}</span>
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
</template>
|
||||
</j-dropdown>
|
||||
<j-popconfirm
|
||||
v-else-if="item.key === 'delete'"
|
||||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
:hasPermission="`notice/Template:${item.key}`"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</j-popconfirm>
|
||||
<template v-else>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item?.text }}</span>
|
||||
<PermissionButton
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
:hasPermission="`notice/Template:${item.key}`"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon :type="item.icon" />
|
||||
</template>
|
||||
<span>{{ item.text }}</span>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</PermissionButton>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
|
@ -119,11 +160,11 @@
|
|||
{{ getProviderTxt(slotProps.type, slotProps.provider) }}
|
||||
</span>
|
||||
</template>
|
||||
<!-- <template #description="slotProps">
|
||||
<template #description="slotProps">
|
||||
<Ellipsis>
|
||||
{{ slotProps.description }}
|
||||
</Ellipsis>
|
||||
</template> -->
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<j-space :size="16">
|
||||
<template
|
||||
|
@ -340,29 +381,6 @@ const getActions = (
|
|||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
},
|
||||
icon: 'ArrowDownOutlined',
|
||||
onClick: () => {
|
||||
downloadObject(data, `通知配置`);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
},
|
||||
icon: 'BarsOutlined',
|
||||
onClick: () => {
|
||||
logVis.value = true;
|
||||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
text: '删除',
|
||||
|
@ -381,6 +399,41 @@ const getActions = (
|
|||
icon: 'DeleteOutlined',
|
||||
},
|
||||
];
|
||||
|
||||
const others: ActionsType = {
|
||||
key: 'others',
|
||||
text: '其他',
|
||||
icon: 'EllipsisOutlined',
|
||||
children: [
|
||||
{
|
||||
key: 'export',
|
||||
text: '导出',
|
||||
tooltip: {
|
||||
title: '导出',
|
||||
},
|
||||
icon: 'ArrowDownOutlined',
|
||||
onClick: () => {
|
||||
downloadObject(data, `通知配置`);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'log',
|
||||
text: '通知记录',
|
||||
tooltip: {
|
||||
title: '通知记录',
|
||||
},
|
||||
icon: 'BarsOutlined',
|
||||
onClick: () => {
|
||||
logVis.value = true;
|
||||
currentConfig.value = data;
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
type === 'card'
|
||||
? actions.splice(actions.length - 1, 0, others)
|
||||
: actions.splice(actions.length - 1, 0, ...others.children);
|
||||
return actions;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<j-col :span="12">
|
||||
<j-form-item
|
||||
name="properties"
|
||||
label="读取属性"
|
||||
:rules="[{ required: true, message: '请选择读取属性' }]"
|
||||
label="设置属性"
|
||||
:rules="[{ required: true, message: '请选择设置属性' }]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
|
@ -18,7 +18,7 @@
|
|||
@change="onChange"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
v-for="item in (metadata?.properties || [])?.filter(i => i?.expands?.type?.includes('write')) || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
|
@ -33,7 +33,6 @@
|
|||
:rules="[{ required: true, message: '请选择' }]"
|
||||
>
|
||||
<ParamsDropdown
|
||||
placeholder="请选择"
|
||||
:options="handleOptions"
|
||||
:tabsOptions="tabOptions"
|
||||
:metricOptions="upperOptions"
|
||||
|
@ -42,7 +41,7 @@
|
|||
@select="onValueChange"
|
||||
>
|
||||
<template v-slot="{ label }">
|
||||
<j-input :value="label" />
|
||||
<j-input :value="label" placeholder="请选择" />
|
||||
</template>
|
||||
</ParamsDropdown>
|
||||
</j-form-item>
|
||||
|
@ -73,7 +72,7 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
const emit = defineEmits(['update:value', 'change']);
|
||||
|
||||
const propertyFormRef = ref();
|
||||
|
||||
|
@ -83,6 +82,7 @@ const propertyModelRef = reactive({
|
|||
source: 'fixed',
|
||||
});
|
||||
|
||||
|
||||
const getType = computed(() => {
|
||||
return props.metadata.properties.find(
|
||||
(item: any) => item.id === propertyModelRef.properties,
|
||||
|
@ -154,7 +154,7 @@ const handleOptions = computed(() => {
|
|||
|
||||
const onChange = () => {
|
||||
propertyModelRef.propertiesValue = undefined;
|
||||
propertyModelRef.source = 'fixed'
|
||||
propertyModelRef.source = 'fixed';
|
||||
emit('update:value', {
|
||||
[`${propertyModelRef.properties}`]: {
|
||||
value: propertyModelRef?.propertiesValue,
|
||||
|
@ -169,8 +169,9 @@ const onValueChange = () => {
|
|||
value: propertyModelRef?.propertiesValue,
|
||||
source: propertyModelRef?.source,
|
||||
},
|
||||
}
|
||||
};
|
||||
emit('update:value', obj);
|
||||
emit('change', propertyModelRef?.propertiesValue)
|
||||
};
|
||||
|
||||
watch(
|
||||
|
@ -187,4 +188,19 @@ watch(
|
|||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
const onSave = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
propertyFormRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
resolve(true);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ onSave });
|
||||
</script>
|
|
@ -22,7 +22,7 @@
|
|||
showSearch
|
||||
placeholder="请选择功能"
|
||||
v-model:value="modelRef.message.functionId"
|
||||
@select='functionSelect'
|
||||
@select="functionSelect"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.functions || []"
|
||||
|
@ -56,7 +56,9 @@
|
|||
v-model:value="modelRef.message.properties[0]"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
v-for="item in metadata?.properties.filter((i) =>
|
||||
i?.expands?.type?.includes('read'),
|
||||
) || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
|
@ -66,9 +68,11 @@
|
|||
</template>
|
||||
<template v-else-if="deviceMessageType === 'WRITE_PROPERTY'">
|
||||
<WriteProperty
|
||||
ref="writeFormRef"
|
||||
v-model:value="modelRef.message.properties"
|
||||
:metadata="metadata"
|
||||
:builtInList="builtInList"
|
||||
@change="onWriteChange"
|
||||
/>
|
||||
</template>
|
||||
</j-form>
|
||||
|
@ -83,7 +87,7 @@ import EditTable from './EditTable.vue';
|
|||
import WriteProperty from './WriteProperty.vue';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getParams } from '../../../util'
|
||||
import { getParams } from '../../../util';
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
|
@ -137,26 +141,39 @@ const modelRef = reactive({
|
|||
properties: undefined,
|
||||
inputs: [],
|
||||
},
|
||||
propertiesValue: '',
|
||||
});
|
||||
|
||||
const functionSelect = () => {
|
||||
modelRef.message.inputs = []
|
||||
}
|
||||
const writeFormRef = ref();
|
||||
|
||||
const functionRules = [{
|
||||
validator(_: string, value: any) {
|
||||
if (!value?.length && functions.value.length) {
|
||||
return Promise.reject('请输入功能值')
|
||||
} else {
|
||||
const hasValue = value.find((item: { name: string, value: any}) => !item.value)
|
||||
if (hasValue) {
|
||||
const functionItem = functions.value.find((item: any) => item.id === hasValue.name)
|
||||
return Promise.reject(functionItem?.name ? `请输入${functionItem.name}值` : '请输入功能值')
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}]
|
||||
const functionSelect = () => {
|
||||
modelRef.message.inputs = [];
|
||||
};
|
||||
|
||||
const functionRules = [
|
||||
{
|
||||
validator(_: string, value: any) {
|
||||
if (!value?.length && functions.value.length) {
|
||||
return Promise.reject('请输入功能值');
|
||||
} else {
|
||||
const hasValue = value.find(
|
||||
(item: { name: string; value: any }) => !item.value,
|
||||
);
|
||||
if (hasValue) {
|
||||
const functionItem = functions.value.find(
|
||||
(item: any) => item.id === hasValue.name,
|
||||
);
|
||||
return Promise.reject(
|
||||
functionItem?.name
|
||||
? `请输入${functionItem.name}值`
|
||||
: '请输入功能值',
|
||||
);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const metadata = ref<{
|
||||
functions: any[];
|
||||
|
@ -205,15 +222,15 @@ const queryBuiltIn = async () => {
|
|||
action: props.name - 1,
|
||||
};
|
||||
const _data = await getParams(_params, unref(data));
|
||||
builtInList.value = _data
|
||||
builtInList.value = _data;
|
||||
};
|
||||
|
||||
const onMessageTypeChange = (val: string) => {
|
||||
const flag = ['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(val)
|
||||
const flag = ['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(val);
|
||||
modelRef.message = {
|
||||
messageType: val,
|
||||
functionId: undefined,
|
||||
properties:(flag ? undefined : []) as any,
|
||||
properties: (flag ? undefined : []) as any,
|
||||
inputs: [],
|
||||
};
|
||||
if (flag) {
|
||||
|
@ -251,7 +268,11 @@ watch(
|
|||
(newVal) => {
|
||||
if (newVal?.messageType) {
|
||||
modelRef.message = newVal;
|
||||
if (['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(newVal.messageType)) {
|
||||
if (
|
||||
['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(
|
||||
newVal.messageType,
|
||||
)
|
||||
) {
|
||||
queryBuiltIn();
|
||||
}
|
||||
}
|
||||
|
@ -259,11 +280,21 @@ watch(
|
|||
{ immediate: true },
|
||||
);
|
||||
|
||||
const onWriteChange = (val: string) => {
|
||||
modelRef.propertiesValue = val;
|
||||
};
|
||||
|
||||
const onFormSave = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then((_data: any) => {
|
||||
.then(async (_data: any) => {
|
||||
if (writeFormRef.value) {
|
||||
const _val = await writeFormRef.value?.onSave();
|
||||
if (!_val) {
|
||||
reject(false);
|
||||
}
|
||||
}
|
||||
// 处理三种情况的值的格式
|
||||
const obj = {
|
||||
message: {
|
||||
|
@ -273,6 +304,7 @@ const onFormSave = () => {
|
|||
deviceMessageType.value === 'INVOKE_FUNCTION'
|
||||
? _function.value?.name
|
||||
: _property.value?.name,
|
||||
propertiesValue: modelRef.propertiesValue,
|
||||
},
|
||||
};
|
||||
resolve(obj);
|
||||
|
|
|
@ -77,12 +77,12 @@ type Emit = {
|
|||
|
||||
const actionRef = ref();
|
||||
const params = ref({
|
||||
terms: []
|
||||
terms: [],
|
||||
});
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array as PropType<any>,
|
||||
default: []
|
||||
default: [],
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
|
@ -90,8 +90,8 @@ const props = defineProps({
|
|||
},
|
||||
productId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
@ -143,9 +143,9 @@ const handleSearch = (p: any) => {
|
|||
...p,
|
||||
terms: [
|
||||
...p.terms,
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
]
|
||||
}
|
||||
{ terms: [{ column: 'productId', value: props?.productId }] },
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const deviceQuery = (p: any) => {
|
||||
|
@ -162,19 +162,26 @@ const deviceQuery = (p: any) => {
|
|||
};
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:value', [{ value: detail.id, name: detail.name }]);
|
||||
emit('change', detail);
|
||||
if (props.value?.[0]?.value === detail.id) {
|
||||
emit('update:value', undefined);
|
||||
emit('change', {});
|
||||
} else {
|
||||
emit('update:value', [{ value: detail.id, name: detail.name }]);
|
||||
emit('change', detail);
|
||||
}
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
params.value = {
|
||||
...params.value,
|
||||
terms: params.value?.terms ? [
|
||||
...(params.value.terms || []),
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
] : [{terms: [{ column: 'productId', value: props?.productId }]}]
|
||||
}
|
||||
})
|
||||
terms: params.value?.terms
|
||||
? [
|
||||
...(params.value.terms || []),
|
||||
{ terms: [{ column: 'productId', value: props?.productId }] },
|
||||
]
|
||||
: [{ terms: [{ column: 'productId', value: props?.productId }] }],
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
<div class="'way-item-title">
|
||||
<span class="way-item-label">{{ item.label }}</span>
|
||||
<j-popover v-if="item.tip">
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
class="way-item-icon"
|
||||
/>
|
||||
<j-tooltip :title="item.tip">
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
class="way-item-icon"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="way-item-image">
|
||||
|
@ -54,7 +56,7 @@ const _value = ref(props?.value || '');
|
|||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
_value.value = newValue || ""
|
||||
_value.value = newValue || '';
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
@ -63,7 +65,7 @@ const onSelect = (_type: string) => {
|
|||
if (!props.disabled) {
|
||||
_value.value = _type;
|
||||
emits('update:value', _type);
|
||||
emits('change', _type)
|
||||
emits('change', _type);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -13,17 +13,18 @@
|
|||
@change="onSelectorChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
<!-- <j-form-item
|
||||
v-if="modelRef.selector === 'fixed'"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择设备' }]"
|
||||
>
|
||||
<Device
|
||||
:productId="values.productDetail.id"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
@change="onDeviceChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
> -->
|
||||
<Device
|
||||
v-if="modelRef.selector === 'fixed'"
|
||||
:productId="values.productDetail.id"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
@change="onDeviceChange"
|
||||
/>
|
||||
<!-- </j-form-item> -->
|
||||
<j-form-item
|
||||
v-else-if="modelRef.selector === 'relation'"
|
||||
label="关系"
|
||||
|
@ -78,7 +79,7 @@
|
|||
import { useSceneStore } from '@/store/scene';
|
||||
import TopCard from './TopCard.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { getImage, onlyMessage } from '@/utils/comm';
|
||||
import NoticeApi from '@/api/notice/config';
|
||||
import Device from './Device.vue';
|
||||
import Tag from './Tag.vue';
|
||||
|
@ -318,7 +319,19 @@ const onFormSave = () => {
|
|||
formRef.value
|
||||
.validate()
|
||||
.then(async (_data: any) => {
|
||||
resolve(_data);
|
||||
if(modelRef.selector === 'fixed'){
|
||||
if(!modelRef?.selectorValues?.[0]?.value){
|
||||
onlyMessage('请选择设备', 'error')
|
||||
reject(false);
|
||||
} else {
|
||||
resolve({
|
||||
..._data,
|
||||
selectorValues: modelRef.selectorValues
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resolve(_data);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
reject(err);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
v-if="current === 0"
|
||||
v-model:rowKey="DeviceModel.productId"
|
||||
v-model:detail="DeviceModel.productDetail"
|
||||
@change="onProductChange"
|
||||
/>
|
||||
<Device
|
||||
v-else-if="current === 1"
|
||||
|
@ -173,9 +174,9 @@ const onSave = (_data: any) => {
|
|||
_options.type = '设置';
|
||||
_options.properties = _data.message.propertiesName;
|
||||
_options.propertiesValue =
|
||||
typeof DeviceModel.propertiesValue === 'object'
|
||||
? JSON.stringify(DeviceModel.propertiesValue)
|
||||
: `${DeviceModel.propertiesValue}`;
|
||||
typeof _data.message.propertiesValue === 'object'
|
||||
? JSON.stringify(_data.message.propertiesValue)
|
||||
: `${_data.message.propertiesValue}`;
|
||||
_options.columns = DeviceModel.columns;
|
||||
_options.otherColumns = DeviceModel.columns;
|
||||
const cur: any = Object.values(_data.message.properties)?.[0];
|
||||
|
@ -184,13 +185,6 @@ const onSave = (_data: any) => {
|
|||
}
|
||||
}
|
||||
if (_options.selector === 'tag') {
|
||||
// const arr = _data.map((item: any) => {
|
||||
// return {
|
||||
// column: item.name,
|
||||
// type: item.type,
|
||||
// value: item.value,
|
||||
// };
|
||||
// });
|
||||
_options.taglist = DeviceModel.tagList.map((it) => ({
|
||||
name: it.column || it.name,
|
||||
type: it.type ? (it.type === 'and' ? '并且' : '或者') : '',
|
||||
|
@ -203,6 +197,11 @@ const onSave = (_data: any) => {
|
|||
emit('save', item, _options);
|
||||
};
|
||||
|
||||
const onProductChange = () => {
|
||||
DeviceModel.selectorValues = undefined
|
||||
DeviceModel.message = {}
|
||||
}
|
||||
|
||||
const save = async (step?: number) => {
|
||||
let _step = step !== undefined ? step : current.value;
|
||||
if (_step === 0) {
|
||||
|
@ -213,8 +212,10 @@ const save = async (step?: number) => {
|
|||
if (deviceRef.value) {
|
||||
await deviceRef.value?.onFormSave();
|
||||
current.value = 2;
|
||||
} else if (DeviceModel.selectorValues.length) {
|
||||
} else if (DeviceModel.selectorValues?.length) {
|
||||
current.value = 2;
|
||||
} else {
|
||||
onlyMessage('请选择设备', 'error')
|
||||
}
|
||||
} else {
|
||||
if (actionRef.value) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<CardSelect v-model:value="formModel.type" :options="options"/>
|
||||
<CardSelect v-model:value="formModel.type" :options="options.filter(item => !(item.value === 'delay' && parallel))"/>
|
||||
</j-form-item>
|
||||
<ActionTypeComponent
|
||||
v-bind="props"
|
||||
|
@ -69,36 +69,36 @@ const props = defineProps({
|
|||
const emit = defineEmits(['cancel', 'save']);
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: '设备输出',
|
||||
value: 'device',
|
||||
iconUrl: getImage('/scene/device-type.png'),
|
||||
subLabel: '配置设备调用功能、读取属性、设置属性规则',
|
||||
},
|
||||
{
|
||||
label: '消息通知',
|
||||
value: 'notify',
|
||||
iconUrl: getImage('/scene/message-type.png'),
|
||||
subLabel: '配置向指定用户发邮件、钉钉、微信、短信等通知',
|
||||
},
|
||||
{
|
||||
label: '延迟执行',
|
||||
value: 'delay',
|
||||
iconUrl: getImage('/scene/delay-type.png'),
|
||||
subLabel: '等待一段时间后,再执行后续动作',
|
||||
},
|
||||
{
|
||||
label: '触发告警',
|
||||
value: 'trigger',
|
||||
iconUrl: getImage('/scene/trigger-type.png'),
|
||||
subLabel: '配置触发告警规则,需配合“告警配置”使用',
|
||||
},
|
||||
{
|
||||
label: '解除告警',
|
||||
value: 'relieve',
|
||||
iconUrl: getImage('/scene/cancel-type.png'),
|
||||
subLabel: '配置解除告警规则,需配合“告警配置”使用',
|
||||
},
|
||||
{
|
||||
label: '设备输出',
|
||||
value: 'device',
|
||||
iconUrl: getImage('/scene/device-type.png'),
|
||||
subLabel: '配置设备调用功能、读取属性、设置属性规则',
|
||||
},
|
||||
{
|
||||
label: '消息通知',
|
||||
value: 'notify',
|
||||
iconUrl: getImage('/scene/message-type.png'),
|
||||
subLabel: '配置向指定用户发邮件、钉钉、微信、短信等通知',
|
||||
},
|
||||
{
|
||||
label: '延迟执行',
|
||||
value: 'delay',
|
||||
iconUrl: getImage('/scene/delay-type.png'),
|
||||
subLabel: '等待一段时间后,再执行后续动作',
|
||||
},
|
||||
{
|
||||
label: '触发告警',
|
||||
value: 'trigger',
|
||||
iconUrl: getImage('/scene/trigger-type.png'),
|
||||
subLabel: '配置触发告警规则,需配合“告警配置”使用',
|
||||
},
|
||||
{
|
||||
label: '解除告警',
|
||||
value: 'relieve',
|
||||
iconUrl: getImage('/scene/cancel-type.png'),
|
||||
subLabel: '配置解除告警规则,需配合“告警配置”使用',
|
||||
},
|
||||
];
|
||||
|
||||
const actionForm = ref();
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
:rules="[
|
||||
{
|
||||
validator: (_rule, value) => checkValue(_rule, value, item),
|
||||
trigger: ['change', 'blur']
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -22,6 +22,7 @@
|
|||
:notify="notify"
|
||||
v-if="getType(item) === 'user'"
|
||||
v-model:value="modelRef[item.id]"
|
||||
@change="(val) => onChange(val, 'user')"
|
||||
/>
|
||||
<Org
|
||||
:notify="notify"
|
||||
|
@ -78,7 +79,7 @@ const formRef = ref();
|
|||
const modelRef = reactive({});
|
||||
|
||||
watchEffect(() => {
|
||||
Object.assign(modelRef, props.value);
|
||||
Object.assign(modelRef, props?.value);
|
||||
});
|
||||
|
||||
const getType = (item: any) => {
|
||||
|
@ -102,34 +103,41 @@ const checkValue = (_rule: any, value: any, item: any) => {
|
|||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
if (value?.source === 'upper') {
|
||||
if (!value.upperKey) {
|
||||
if (!value?.upperKey) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
} else {
|
||||
if (!value.value) {
|
||||
if (!value?.value) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value?.source === 'fixed' && !value.value) {
|
||||
} else if (value?.source === 'fixed' && !value?.value) {
|
||||
return Promise.reject(new Error('请输入' + item.name));
|
||||
} else if (value?.source === 'relation' && !value.value && !value.relation) {
|
||||
} else if (
|
||||
value?.source === 'relation' &&
|
||||
!value?.value &&
|
||||
!value?.relation
|
||||
) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else if (value?.source === 'upper' && !value.upperKey) {
|
||||
return Promise.reject(new Error('请选择' + item.name));
|
||||
} else if (type === 'user') {
|
||||
if (props.notify.notifyType === 'email' && value?.source !== 'relation') {
|
||||
if (Array.isArray(value.value)) {
|
||||
if (!value.value.length) {
|
||||
if (
|
||||
props.notify.notifyType === 'email' &&
|
||||
value?.source !== 'relation'
|
||||
) {
|
||||
if (Array.isArray(value?.value)) {
|
||||
if (!value?.value.length) {
|
||||
return Promise.reject(new Error('请输入收件人'));
|
||||
}
|
||||
const reg =
|
||||
/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
|
||||
const flag = value.value.every((it: string) => {
|
||||
const flag = value?.value.every((it: string) => {
|
||||
return reg.test(it);
|
||||
});
|
||||
if (!flag) {
|
||||
|
@ -143,10 +151,11 @@ const checkValue = (_rule: any, value: any, item: any) => {
|
|||
}
|
||||
if (
|
||||
props.notify.notifyType &&
|
||||
['sms', 'voice'].includes(props?.notify?.notifyType) && value?.source !== 'relation'
|
||||
['sms', 'voice'].includes(props?.notify?.notifyType) &&
|
||||
value?.source !== 'relation'
|
||||
) {
|
||||
const reg = /^[1][3-9]\d{9}$/;
|
||||
if (!reg.test(value.value)) {
|
||||
if (!reg.test(value?.value)) {
|
||||
return Promise.reject(new Error('请输入正确的手机号码'));
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
|
@ -161,12 +170,14 @@ const onChange = (val: any, type: any) => {
|
|||
emit('change', { orgName: val.join(',') });
|
||||
} else if (type === 'tag') {
|
||||
emit('change', { tagName: val });
|
||||
} else if (type === 'user') {
|
||||
emit('change', { sendTo: val });
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = () =>
|
||||
new Promise((resolve) => {
|
||||
formRef.value.validate().then(async (_data: any) => {
|
||||
formRef.value?.validate().then(async (_data: any) => {
|
||||
resolve(_data);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<j-modal
|
||||
title="执行动作"
|
||||
visible
|
||||
:width="860"
|
||||
:width="800"
|
||||
@cancel="onCancel"
|
||||
@ok="onOk"
|
||||
:maskClosable="false"
|
||||
|
|
|
@ -33,21 +33,59 @@
|
|||
固定号码
|
||||
</j-select-option>
|
||||
</j-select>
|
||||
<j-tree-select
|
||||
v-if="source === 'relation'"
|
||||
style="width: calc(100% - 120px)"
|
||||
placeholder="请选择收信人"
|
||||
@select="(key, node) => onChange(source, key, false, node?.relation, node.name)"
|
||||
:tree-data="treeData"
|
||||
:multiple="['email'].includes(notifyType)"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:value="relationData"
|
||||
>
|
||||
<template #title="{ key, username, title }">
|
||||
<div style="display: flex; justify-content: space-between; margin-right: 10px;" v-if="key !== 'p1' && key !== 'p2'">{{ title }} <span style="color: #cfcfcf;">{{ username }}</span></div>
|
||||
<span v-else>{{ title }}</span>
|
||||
</template>
|
||||
</j-tree-select>
|
||||
<template v-if="source === 'relation'">
|
||||
<j-tree-select
|
||||
v-if="['email'].includes(notifyType)"
|
||||
style="width: calc(100% - 120px)"
|
||||
placeholder="请选择收信人"
|
||||
@change="(key, label) => onChange(source, key, false, label)"
|
||||
:tree-data="treeData"
|
||||
:multiple="true"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:value="relationData"
|
||||
>
|
||||
<template #title="{ key, username, title }">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-right: 10px;
|
||||
"
|
||||
v-if="key !== 'p1' && key !== 'p2'"
|
||||
>
|
||||
{{ title }}
|
||||
<span style="color: #cfcfcf">{{ username }}</span>
|
||||
</div>
|
||||
<span v-else>{{ title }}</span>
|
||||
</template>
|
||||
</j-tree-select>
|
||||
<j-tree-select
|
||||
v-else
|
||||
style="width: calc(100% - 120px)"
|
||||
placeholder="请选择收信人"
|
||||
@select="
|
||||
(key, node) => onChange(source, key, node?.isRelation, node?.name)
|
||||
"
|
||||
:tree-data="treeData"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:value="relationData"
|
||||
>
|
||||
<template #title="{ key, username, title }">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-right: 10px;
|
||||
"
|
||||
v-if="key !== 'p1' && key !== 'p2'"
|
||||
>
|
||||
{{ title }}
|
||||
<span style="color: #cfcfcf">{{ username }}</span>
|
||||
</div>
|
||||
<span v-else>{{ title }}</span>
|
||||
</template>
|
||||
</j-tree-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<j-select
|
||||
style="width: calc(100% - 120px)"
|
||||
|
@ -55,7 +93,15 @@
|
|||
placeholder="请选择收信人"
|
||||
:value="value?.value"
|
||||
showSearch
|
||||
@change="(val, option) => onChange(source, val, false, option?.label || option?.name)"
|
||||
@change="
|
||||
(val, option) =>
|
||||
onChange(
|
||||
source,
|
||||
val,
|
||||
false,
|
||||
option?.label || option?.name,
|
||||
)
|
||||
"
|
||||
:options="relationList"
|
||||
/>
|
||||
<j-select
|
||||
|
@ -64,14 +110,25 @@
|
|||
placeholder="请输入收件人邮箱,多个收件人用换行分隔"
|
||||
:value="value?.value"
|
||||
mode="tags"
|
||||
@change="(val) => onChange(source, val, false, Array.isArray(val) ? val.join(',') : val)"
|
||||
@change="
|
||||
(val) =>
|
||||
onChange(
|
||||
source,
|
||||
val,
|
||||
false,
|
||||
Array.isArray(val) ? val.join(',') : val,
|
||||
)
|
||||
"
|
||||
/>
|
||||
<j-input
|
||||
style="width: calc(100% - 120px)"
|
||||
v-else-if="['sms', 'voice'].includes(notifyType)"
|
||||
placeholder="请输入固定号码"
|
||||
:value="value?.value"
|
||||
@change="(e) => onChange(source, e.target.value, false, e.target.value)"
|
||||
@change="
|
||||
(e) =>
|
||||
onChange(source, e.target.value, false, e.target.value)
|
||||
"
|
||||
></j-input>
|
||||
</template>
|
||||
</j-input-group>
|
||||
|
@ -262,7 +319,7 @@ const onChange = (
|
|||
_name?: string,
|
||||
) => {
|
||||
let _values: any = undefined;
|
||||
const _names: string[] = [_name || ''];
|
||||
const _names: string[] = Array.isArray(_name) ? _name : [_name || ''];
|
||||
if (Array.isArray(_value)) {
|
||||
if (props?.notify?.notifyType === 'email') {
|
||||
if (isRelation) {
|
||||
|
@ -280,7 +337,7 @@ const onChange = (
|
|||
_values = getObj(_source, _value, isRelation);
|
||||
}
|
||||
emit('update:value', _values);
|
||||
emit('change', { sendTo: _names.filter((item) => !!item).join(',') });
|
||||
emit('change', _names.filter((item) => !!item).join(','));
|
||||
};
|
||||
|
||||
watch(
|
||||
|
|
|
@ -1,126 +1,173 @@
|
|||
<template>
|
||||
<div class="mangement-container">
|
||||
<div class="left">
|
||||
<j-input-search
|
||||
v-model:value="leftData.searchValue"
|
||||
placeholder="请输入"
|
||||
style="margin-bottom: 24px"
|
||||
/>
|
||||
<!-- 使用v-if用于解决异步加载数据后不展开的问题 -->
|
||||
<j-tree
|
||||
v-if="leftData.treeData.length > 0"
|
||||
showLine
|
||||
defaultExpandAll
|
||||
:tree-data="leftData.treeData"
|
||||
v-model:selectedKeys="leftData.selectedKeys"
|
||||
@select="leftData.onSelect"
|
||||
>
|
||||
<template #title="{ dataRef }">
|
||||
<div
|
||||
v-if="dataRef.root"
|
||||
:style="`
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`"
|
||||
>
|
||||
<span>
|
||||
<page-container>
|
||||
<div class="manager-container">
|
||||
<div class="left">
|
||||
<j-input-search
|
||||
v-model:value="leftData.searchValue"
|
||||
placeholder="请输入"
|
||||
style="margin-bottom: 24px"
|
||||
/>
|
||||
<!-- 使用v-if用于解决异步加载数据后不展开的问题 -->
|
||||
<j-tree
|
||||
v-if="leftData.treeData.length > 0"
|
||||
showLine
|
||||
defaultExpandAll
|
||||
:tree-data="leftData.treeData"
|
||||
v-model:selectedKeys="leftData.selectedKeys"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #title="{ dataRef }">
|
||||
<div
|
||||
v-if="dataRef.root"
|
||||
style="
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 200px;
|
||||
"
|
||||
>
|
||||
<span>
|
||||
{{ dataRef.title }}
|
||||
</span>
|
||||
<AIcon
|
||||
type="PlusOutlined"
|
||||
style="color: #1d39c4"
|
||||
@click="addTable"
|
||||
/>
|
||||
</div>
|
||||
<span v-else>
|
||||
{{ dataRef.title }}
|
||||
</span>
|
||||
<AIcon
|
||||
type="PlusOutlined"
|
||||
style="color: #1d39c4"
|
||||
@click="leftData.addTable"
|
||||
/>
|
||||
</div>
|
||||
<span v-else>
|
||||
{{ dataRef.title }}
|
||||
</span>
|
||||
</template>
|
||||
</j-tree>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="btns">
|
||||
<j-button type="primary" @click="table.clickSave"
|
||||
>保存</j-button
|
||||
>
|
||||
</template>
|
||||
</j-tree>
|
||||
</div>
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
:columns="table.columns"
|
||||
model="TABLE"
|
||||
:dataSource="table.data"
|
||||
>
|
||||
<template #name="slotProps">
|
||||
<j-input
|
||||
:disabled="slotProps.scale !== undefined"
|
||||
v-model:value="slotProps.name"
|
||||
placeholder="请输入名称"
|
||||
:maxlength="64"
|
||||
/>
|
||||
</template>
|
||||
<template #type="slotProps">
|
||||
<j-input
|
||||
v-model:value="slotProps.type"
|
||||
placeholder="请输入类型"
|
||||
:maxlength="64"
|
||||
/>
|
||||
</template>
|
||||
<template #length="slotProps">
|
||||
<j-input-number
|
||||
v-model:value="slotProps.length"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
/>
|
||||
</template>
|
||||
<template #precision="slotProps">
|
||||
<j-input-number
|
||||
v-model:value="slotProps.precision"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
/>
|
||||
</template>
|
||||
<template #notnull="slotProps">
|
||||
<j-radio-group
|
||||
v-model:value="slotProps.notnull"
|
||||
button-style="solid"
|
||||
<div class="right">
|
||||
<div class="btns">
|
||||
<j-button type="primary" @click="clickSave">保存</j-button>
|
||||
</div>
|
||||
<j-form ref="formRef" :model="table">
|
||||
<j-table
|
||||
:columns="columns"
|
||||
:dataSource="table.data"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 500 }"
|
||||
>
|
||||
<j-radio-button :value="true">是</j-radio-button>
|
||||
<j-radio-button :value="false">否</j-radio-button>
|
||||
</j-radio-group>
|
||||
</template>
|
||||
<template #comment="slotProps">
|
||||
<j-input
|
||||
v-model:value="slotProps.comment"
|
||||
placeholder="请输入说明"
|
||||
/>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<PermissionButton
|
||||
:hasPermission="`{permission}:delete`"
|
||||
type="link"
|
||||
:tooltip="{ title: '删除' }"
|
||||
:popConfirm="{
|
||||
title: `确认删除`,
|
||||
onConfirm: () => table.clickDel(slotProps),
|
||||
}"
|
||||
:disabled="slotProps.status"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
<j-botton class="add-row" @click="table.addRow">
|
||||
<AIcon type="PlusOutlined" /> 新增行
|
||||
</j-botton>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<j-form-item
|
||||
:name="['data', index, 'name']"
|
||||
:rules="[
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: '请输入名称',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
:disabled="record.scale !== undefined"
|
||||
v-model:value="record.name"
|
||||
placeholder="请输入名称"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'type'">
|
||||
<j-form-item
|
||||
:name="['data', index, 'type']"
|
||||
:rules="[
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: '请输入类型',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="record.type"
|
||||
placeholder="请输入类型"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'length'">
|
||||
<j-form-item :name="['data', index, 'length']">
|
||||
<j-input-number
|
||||
v-model:value="record.length"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'precision'">
|
||||
<j-form-item
|
||||
:name="['data', index, 'precision']"
|
||||
>
|
||||
<j-input-number
|
||||
v-model:value="record.precision"
|
||||
:min="0"
|
||||
:max="99999"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'notnull'">
|
||||
<j-form-item
|
||||
:name="['data', index, 'notnull']"
|
||||
>
|
||||
<j-radio-group
|
||||
v-model:value="record.notnull"
|
||||
button-style="solid"
|
||||
>
|
||||
<j-radio-button :value="true"
|
||||
>是</j-radio-button
|
||||
>
|
||||
<j-radio-button :value="false"
|
||||
>否</j-radio-button
|
||||
>
|
||||
</j-radio-group>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'comment'">
|
||||
<j-form-item
|
||||
:name="['data', index, 'notnull']"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="record.comment"
|
||||
placeholder="请输入说明"
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<PermissionButton
|
||||
hasPermission="system/DataSource:delete"
|
||||
type="link"
|
||||
:tooltip="{ title: '删除' }"
|
||||
:danger="true"
|
||||
:popConfirm="{
|
||||
title: `确认删除`,
|
||||
onConfirm: () => clickDel(record),
|
||||
}"
|
||||
:disabled="record.status"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-form>
|
||||
|
||||
<j-button class="add-row" @click="addRow">
|
||||
<AIcon type="PlusOutlined" /> 新增行
|
||||
</j-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialogs">
|
||||
<j-modal
|
||||
v-model:visible="dialog.visible"
|
||||
title="新增"
|
||||
@ok="dialog.handleOk"
|
||||
>
|
||||
<j-modal v-model:visible="dialog.visible" title="新增" @ok="handleOk">
|
||||
<j-form :model="dialog.form" ref="addFormRef">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
|
@ -129,22 +176,22 @@
|
|||
{
|
||||
required: true,
|
||||
message: '请输入名称',
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
message: '最多可输入64个字符',
|
||||
trigger: 'change',
|
||||
trigger: 'blur',
|
||||
},
|
||||
{
|
||||
pattern: /^[0-9].*$/,
|
||||
message: '不能以数字开头',
|
||||
// pattern: /^[0-9].*$/,
|
||||
// message: '不能以数字开头',
|
||||
trigger: 'change',
|
||||
validator: checkName,
|
||||
},
|
||||
{
|
||||
pattern: /^\w+$/,
|
||||
message: '名称只能由数字、字母、下划线、中划线组成',
|
||||
trigger: 'change',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
|
@ -155,7 +202,7 @@
|
|||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
</div>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Management">
|
||||
|
@ -166,153 +213,166 @@ import {
|
|||
saveTable_api,
|
||||
delSaveRow_api,
|
||||
} from '@/api/system/dataSource';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import { FormInstance, message } from 'ant-design-vue';
|
||||
import { DataNode } from 'ant-design-vue/lib/tree';
|
||||
import _ from 'lodash';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import type { dbColumnType, dictItemType, sourceItemType } from '../typing';
|
||||
|
||||
const id = useRoute().query.id as string;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '列名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
},
|
||||
{
|
||||
title: '长度',
|
||||
dataIndex: 'length',
|
||||
key: 'length',
|
||||
},
|
||||
{
|
||||
title: '精度',
|
||||
dataIndex: 'precision',
|
||||
key: 'precision',
|
||||
},
|
||||
{
|
||||
title: '不能为空',
|
||||
dataIndex: 'notnull',
|
||||
key: 'notnull',
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'comment',
|
||||
key: 'comment',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
},
|
||||
];
|
||||
const formRef = ref();
|
||||
|
||||
const getInfo = (_id: string) => {
|
||||
getDataSourceInfo_api(_id).then((resp: any) => {
|
||||
info.data = resp.result;
|
||||
});
|
||||
};
|
||||
|
||||
const info = reactive({
|
||||
data: {} as sourceItemType,
|
||||
init: () => {
|
||||
id &&
|
||||
getDataSourceInfo_api(id).then((resp: any) => {
|
||||
info.data = resp.result;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const leftData = reactive({
|
||||
searchValue: '',
|
||||
sourceTree: [] as dictItemType[],
|
||||
treeData: [] as DataNode[],
|
||||
treeData: [] as any[],
|
||||
selectedKeys: [] as string[],
|
||||
oldKey: '',
|
||||
|
||||
init: () => {
|
||||
leftData.getTree();
|
||||
watch(
|
||||
[
|
||||
() => leftData.searchValue,
|
||||
() => leftData.sourceTree,
|
||||
() => info.data,
|
||||
],
|
||||
(n) => {
|
||||
if (leftData.sourceTree.length < 1 || !info.data.shareConfig)
|
||||
return;
|
||||
let filterArr = [];
|
||||
if (leftData.searchValue) {
|
||||
filterArr = leftData.sourceTree.filter((item) =>
|
||||
item.name.includes(n[0]),
|
||||
);
|
||||
} else filterArr = leftData.sourceTree;
|
||||
leftData.treeData = [
|
||||
{
|
||||
title: info.data.shareConfig.schema,
|
||||
key: info.data.shareConfig.schema,
|
||||
root: true,
|
||||
children: filterArr.map((item) => ({
|
||||
title: item.name,
|
||||
key: item.name,
|
||||
})),
|
||||
},
|
||||
];
|
||||
leftData.selectedKeys = [filterArr[0].name];
|
||||
leftData.onSelect([filterArr[0].name]);
|
||||
},
|
||||
{},
|
||||
);
|
||||
},
|
||||
getTree: () => {
|
||||
rdbTree_api(id)
|
||||
.then((resp: any) => {
|
||||
leftData.sourceTree = resp.result;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
onSelect: (selectedKeys: string[], e?: any) => {
|
||||
if (e?.node?.root) {
|
||||
leftData.selectedKeys = [leftData.oldKey];
|
||||
return;
|
||||
}
|
||||
leftData.oldKey = selectedKeys[0];
|
||||
const key = selectedKeys[0];
|
||||
table.getTabelData(key);
|
||||
},
|
||||
addTable: (e: Event) => {
|
||||
e.stopPropagation();
|
||||
},
|
||||
});
|
||||
|
||||
const table = reactive({
|
||||
columns: [
|
||||
{
|
||||
title: '列名',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '长度',
|
||||
dataIndex: 'length',
|
||||
key: 'length',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '精度',
|
||||
dataIndex: 'precision',
|
||||
key: 'precision',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '不能为空',
|
||||
dataIndex: 'notnull',
|
||||
key: 'notnull',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'comment',
|
||||
key: 'comment',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
],
|
||||
data: [] as dbColumnType[],
|
||||
|
||||
getTabelData: (key: string) => {
|
||||
const queryTables = (key: string) => {
|
||||
if (key) {
|
||||
rdbTables_api(id, key).then((resp: any) => {
|
||||
table.data = resp.result.columns.map(
|
||||
(item: object, index: number) => ({ ...item, index }),
|
||||
(item: object, index: number) => ({
|
||||
...item,
|
||||
index,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = (refresh?: boolean) => {
|
||||
rdbTree_api(id)
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
leftData.sourceTree = resp.result;
|
||||
if (refresh) {
|
||||
leftData.selectedKeys = [resp.result[0]?.name];
|
||||
queryTables(resp.result[0]?.name);
|
||||
} else {
|
||||
queryTables(leftData.selectedKeys[0]);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const onSelect = (selectedKeys: string[], e?: any) => {
|
||||
if (e?.node?.root) {
|
||||
leftData.selectedKeys = [leftData.oldKey];
|
||||
return;
|
||||
}
|
||||
if (!selectedKeys[0]) {
|
||||
return;
|
||||
}
|
||||
leftData.oldKey = selectedKeys[0];
|
||||
const key = selectedKeys[0];
|
||||
queryTables(key);
|
||||
};
|
||||
|
||||
const addTable = (e: Event) => {
|
||||
e?.stopPropagation();
|
||||
dialog.visible = true;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => id,
|
||||
(newId) => {
|
||||
if (newId) {
|
||||
getInfo(newId);
|
||||
handleSearch(true);
|
||||
}
|
||||
},
|
||||
addRow: () => {
|
||||
const initData: dbColumnType = {
|
||||
precision: 0,
|
||||
length: 0,
|
||||
notnull: false,
|
||||
type: '',
|
||||
comment: '',
|
||||
name: '',
|
||||
};
|
||||
table.data.push(initData);
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
clickSave: () => {
|
||||
);
|
||||
|
||||
const table = reactive({
|
||||
data: [] as dbColumnType[],
|
||||
});
|
||||
|
||||
const addRow = () => {
|
||||
const initData: dbColumnType = {
|
||||
precision: 0,
|
||||
length: 0,
|
||||
notnull: false,
|
||||
type: '',
|
||||
comment: '',
|
||||
name: '',
|
||||
};
|
||||
table.data.push(initData);
|
||||
};
|
||||
|
||||
const clickDel = (row: any) => {
|
||||
if (row.scale !== undefined) {
|
||||
delSaveRow_api(id, leftData.selectedKeys[0], [row.name]).then(
|
||||
(resp: any) => {
|
||||
if (resp.status === 200) table.data.splice(row.index, 1);
|
||||
},
|
||||
);
|
||||
} else table.data.splice(row.index, 1);
|
||||
};
|
||||
|
||||
const clickSave = () => {
|
||||
formRef.value.validate().then((_data: any) => {
|
||||
const columns = cloneDeep(table.data);
|
||||
columns.forEach((item) => delete item.index);
|
||||
if (!columns.length) {
|
||||
onlyMessage('请配置数据源字段', 'error');
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
name: leftData.selectedKeys[0],
|
||||
columns,
|
||||
|
@ -320,20 +380,11 @@ const table = reactive({
|
|||
saveTable_api(id, params).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
table.getTabelData(params.name);
|
||||
queryTables(params.name);
|
||||
}
|
||||
});
|
||||
},
|
||||
clickDel: (row: any) => {
|
||||
if (row.scale !== undefined) {
|
||||
delSaveRow_api(id, leftData.selectedKeys[0], [row.name]).then(
|
||||
(resp: any) => {
|
||||
if (resp.status === 200) table.data.splice(row.index, 1);
|
||||
},
|
||||
);
|
||||
} else table.data.splice(row.index, 1);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const addFormRef = ref<FormInstance>();
|
||||
const dialog = reactive({
|
||||
|
@ -341,73 +392,110 @@ const dialog = reactive({
|
|||
form: {
|
||||
name: '',
|
||||
},
|
||||
handleOk: () => {
|
||||
addFormRef.value &&
|
||||
addFormRef.value.validate().then(() => {
|
||||
const name = dialog.form.name;
|
||||
leftData.sourceTree.unshift({
|
||||
id: name,
|
||||
name,
|
||||
});
|
||||
leftData.oldKey = name;
|
||||
leftData.selectedKeys = [name];
|
||||
table.data = [];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
init();
|
||||
function init() {
|
||||
info.init();
|
||||
leftData.init();
|
||||
}
|
||||
const handleOk = () => {
|
||||
addFormRef.value &&
|
||||
addFormRef.value.validate().then(() => {
|
||||
const name = dialog.form.name;
|
||||
leftData.sourceTree.unshift({
|
||||
id: name,
|
||||
name,
|
||||
});
|
||||
leftData.oldKey = name;
|
||||
leftData.selectedKeys = [name];
|
||||
table.data = [];
|
||||
dialog.visible = false;
|
||||
addFormRef.value?.resetFields();
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
[() => leftData.searchValue, () => leftData.sourceTree],
|
||||
([m, n]) => {
|
||||
if (!!m) {
|
||||
const list = n.filter((item) => {
|
||||
return item.name.includes(m);
|
||||
});
|
||||
leftData.treeData = [
|
||||
{
|
||||
title: info.data.shareConfig.schema,
|
||||
key: info.data.shareConfig.schema,
|
||||
root: true,
|
||||
children: list.map((item) => ({
|
||||
title: item.name,
|
||||
key: item.name,
|
||||
})),
|
||||
},
|
||||
];
|
||||
if (!_.map(list, 'name').includes(leftData.selectedKeys[0])) {
|
||||
leftData.selectedKeys = [list[0]?.name];
|
||||
queryTables(list[0]?.name);
|
||||
}
|
||||
} else {
|
||||
leftData.treeData = [
|
||||
{
|
||||
title: info.data.shareConfig.schema,
|
||||
key: info.data.shareConfig.schema,
|
||||
root: true,
|
||||
children: leftData.sourceTree.map((item) => ({
|
||||
title: item.name,
|
||||
key: item.name,
|
||||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const checkName = (_: any, value: any) =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (value) {
|
||||
const first = value.slice(0, 1);
|
||||
if (typeof Number(first) === 'number' && !isNaN(Number(first))) {
|
||||
reject('不能以数字开头');
|
||||
} else {
|
||||
resolve('');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mangement-container {
|
||||
margin: 24px;
|
||||
.manager-container {
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
min-height: 500px;
|
||||
|
||||
.left {
|
||||
flex-basis: 280px;
|
||||
padding-right: 24px;
|
||||
padding: 0 24px;
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(.ant-tree-treenode) {
|
||||
width: 100%;
|
||||
.ant-tree-switcher-noop {
|
||||
display: none;
|
||||
}
|
||||
.ant-tree-node-content-wrapper {
|
||||
width: 100%;
|
||||
.ant-tree-title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&:first-child .ant-tree-node-selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: calc(100% - 280px);
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #f0f0f0;
|
||||
padding-left: 24px;
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
padding: 0px 24px;
|
||||
}
|
||||
|
||||
.add-row {
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin: 24px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:title="dialogTitle"
|
||||
:confirmLoading="loading"
|
||||
@ok="confirm"
|
||||
@cancel="emits('update:visible', false)"
|
||||
@cancel="emits('cancel')"
|
||||
>
|
||||
<j-form ref="formRef" :model="form.data" layout="vertical">
|
||||
<j-row :gutter="24">
|
||||
|
@ -51,7 +51,7 @@
|
|||
message: '请输入URL',
|
||||
trigger: 'change',
|
||||
},
|
||||
{ validator: form.checkUrl, trigger: 'blur' },
|
||||
{ validator: checkUrl, trigger: 'blur' },
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
|
@ -193,9 +193,8 @@ import { FormInstance, message } from 'ant-design-vue';
|
|||
import { Rule } from 'ant-design-vue/lib/form';
|
||||
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
|
||||
|
||||
const emits = defineEmits(['confirm', 'update:visible']);
|
||||
const emits = defineEmits(['confirm', 'cancel']);
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
data: sourceItemType;
|
||||
}>();
|
||||
// 弹窗相关
|
||||
|
@ -203,19 +202,25 @@ const dialogTitle = computed(() =>
|
|||
props.data.id ? '编辑数据源' : '新增数据源',
|
||||
);
|
||||
const loading = ref(false);
|
||||
const confirm = () => {
|
||||
loading.value = true;
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(() => form.submit())
|
||||
.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('confirm');
|
||||
emits('update:visible', false);
|
||||
}
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
|
||||
const checkUrl = (_rule: Rule, value: string): Promise<any> => {
|
||||
if (!value) return Promise.resolve();
|
||||
const arr = value.split(':');
|
||||
if (arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject('请输入正确的URL');
|
||||
}
|
||||
};
|
||||
|
||||
const getTypeOption = () => {
|
||||
getDataTypeDict_api().then((resp: any) => {
|
||||
const result = resp.result as dictItemType[];
|
||||
form.typeOptions = result.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
@ -223,30 +228,38 @@ const form = reactive({
|
|||
data: {
|
||||
...props.data,
|
||||
} as sourceItemType,
|
||||
|
||||
typeOptions: [] as optionItemType[],
|
||||
|
||||
checkUrl: (_rule: Rule, value: string): Promise<any> => {
|
||||
if (!value) return Promise.reject('请输入URL');
|
||||
const arr = value.split(':');
|
||||
if (arr?.[0] === 'jdbc' || arr?.[0] === 'r2dbc') {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject('请输入正确的URL');
|
||||
}
|
||||
},
|
||||
getTypeOption: () => {
|
||||
getDataTypeDict_api().then((resp: any) => {
|
||||
const result = resp.result as dictItemType[];
|
||||
form.typeOptions = result.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
});
|
||||
},
|
||||
submit: () => {
|
||||
return saveDataSource_api(form.data);
|
||||
},
|
||||
});
|
||||
form.getTypeOption();
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue) => {
|
||||
form.data = {...newValue, shareConfig: { ...newValue?.shareConfig }}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getTypeOption();
|
||||
})
|
||||
|
||||
const confirm = () => {
|
||||
loading.value = true;
|
||||
formRef.value
|
||||
?.validate()
|
||||
.then(async (_data: any) => {
|
||||
const resp = await saveDataSource_api({ ...props.data, ..._data });
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功');
|
||||
emits('confirm');
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -14,8 +14,13 @@
|
|||
model="TABLE"
|
||||
:params="queryParams"
|
||||
:defaultParams="{
|
||||
pageSize: 10,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
:pagination="{
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
}"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<PermissionButton
|
||||
|
@ -31,7 +36,7 @@
|
|||
:status="slotProps.state?.value"
|
||||
:text="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
enabled: 'success',
|
||||
enabled: 'processing',
|
||||
disabled: 'error',
|
||||
}"
|
||||
>
|
||||
|
@ -116,6 +121,7 @@
|
|||
? '请先禁用,再删除'
|
||||
: '删除',
|
||||
}"
|
||||
:danger="true"
|
||||
:popConfirm="{
|
||||
title: `确认删除`,
|
||||
onConfirm: () => table.clickDel(slotProps),
|
||||
|
@ -130,7 +136,7 @@
|
|||
|
||||
<EditDialog
|
||||
v-if="dialog.visible"
|
||||
v-model:visible="dialog.visible"
|
||||
@cancel="table.cancel"
|
||||
:data="dialog.selectItem"
|
||||
@confirm="table.refresh"
|
||||
/>
|
||||
|
@ -211,7 +217,7 @@ const columns = [
|
|||
value: 'enabled',
|
||||
},
|
||||
{
|
||||
label: '已禁用',
|
||||
label: '禁用',
|
||||
value: 'disabled',
|
||||
},
|
||||
],
|
||||
|
@ -277,7 +283,13 @@ const table = {
|
|||
// 刷新列表
|
||||
refresh: () => {
|
||||
tableRef.value.reload();
|
||||
dialog.visible = false
|
||||
dialog.selectItem = {}
|
||||
},
|
||||
cancel: () => {
|
||||
dialog.visible = false
|
||||
dialog.selectItem = {}
|
||||
}
|
||||
};
|
||||
table.getTypeOption();
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
:params="queryParams"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: table._selectedRowKeys.value,
|
||||
onChange: selectRow,
|
||||
onChange: selectChange,
|
||||
}"
|
||||
@cancelSelect="table.cancelSelect"
|
||||
:columns="columns"
|
||||
|
@ -55,7 +55,7 @@
|
|||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
online: 'success',
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
|
@ -121,7 +121,7 @@
|
|||
:status="slotProps.state.value"
|
||||
:text="slotProps.state.text"
|
||||
:statusNames="{
|
||||
online: 'success',
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
|
@ -173,10 +173,8 @@ const confirm = () => {
|
|||
permission: item.selectPermissions,
|
||||
}));
|
||||
|
||||
if (params.length === 1) {
|
||||
// 只选择一个产品资产分配时, 分配之后, 进入设备资产分配需查出对应产品下的设备
|
||||
departmentStore.setProductId(params[0].assetIdList[0]);
|
||||
}
|
||||
// 分配产品资产后, 进入设备资产分配,默认查询第一个产品下的设备
|
||||
departmentStore.setProductId(params[0].assetIdList[0]);
|
||||
|
||||
loading.value = true;
|
||||
bindDeviceOrProductList_api(props.assetType, params)
|
||||
|
@ -475,15 +473,20 @@ const table: any = {
|
|||
},
|
||||
};
|
||||
table.init();
|
||||
const selectRow = (keys: string[], rows: any[]) => {
|
||||
const okRows = rows.filter(
|
||||
(item) =>
|
||||
!!item.permissionList.find(
|
||||
(permiss: any) => permiss.value === 'share',
|
||||
),
|
||||
);
|
||||
table.selectedRows = okRows;
|
||||
table._selectedRowKeys.value = okRows.map((item) => item.id);
|
||||
// const selectRow = (rows: any[], check: boolean) => {
|
||||
// const okRows = rows.filter(
|
||||
// (item) =>
|
||||
// !!item.permissionList.find(
|
||||
// (permiss: any) => permiss.value === 'share',
|
||||
// ),
|
||||
// );
|
||||
// table.selectedRows = okRows;
|
||||
// table._selectedRowKeys.value = okRows.map((item) => item.id);
|
||||
// };
|
||||
// fix: bug#10749
|
||||
const selectChange = (keys: string[], rows: any[]) => {
|
||||
table.selectedRows = rows;
|
||||
table._selectedRowKeys.value = keys;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
visible
|
||||
@cancel="emits('update:visible', false)"
|
||||
>
|
||||
<a-alert
|
||||
message="只能分配有'共享'权限的资产数据"
|
||||
type="warning"
|
||||
show-icon
|
||||
/>
|
||||
<div style="margin-top: 5px;">
|
||||
<div class="alert-info">
|
||||
<j-space>
|
||||
<AIcon type="ExclamationCircleOutlined" />
|
||||
<span>只能分配有'共享'权限的资产数据</span>
|
||||
</j-space>
|
||||
</div>
|
||||
<div style="margin-top: 5px">
|
||||
<span>资产权限:</span>
|
||||
<j-checkbox-group
|
||||
v-model:value="form.permission"
|
||||
|
@ -67,4 +68,11 @@ const options = computed(() => {
|
|||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped lang="less">
|
||||
.alert-info {
|
||||
background: #f3f3f3;
|
||||
border-radius: 2px;
|
||||
padding: 6px;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -68,8 +68,8 @@
|
|||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
1: 'processing',
|
||||
0: 'error',
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
}"
|
||||
>
|
||||
<template #img>
|
||||
|
@ -171,8 +171,8 @@
|
|||
:status="slotProps.state.value"
|
||||
:text="slotProps.state.text"
|
||||
:statusNames="{
|
||||
1: 'processing',
|
||||
0: 'error',
|
||||
online: 'processing',
|
||||
offline: 'error',
|
||||
}"
|
||||
></BadgeStatus>
|
||||
</template>
|
||||
|
@ -517,6 +517,7 @@ const table = {
|
|||
},
|
||||
];
|
||||
unBindDeviceOrProduct_api('product', params).then(() => {
|
||||
tableData._selectedRowKeys = [];
|
||||
message.success('操作成功');
|
||||
table.refresh();
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<pro-search
|
||||
:columns="columns"
|
||||
target="category"
|
||||
@search="(params:any)=>queryParams = {...params}"
|
||||
@search="handleParams"
|
||||
/>
|
||||
<j-pro-table
|
||||
ref="tableRef"
|
||||
|
@ -119,8 +119,8 @@ const columns = [
|
|||
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
|
@ -149,6 +149,10 @@ const columns = [
|
|||
// 搜索参数
|
||||
const queryParams = ref({});
|
||||
|
||||
const handleParams = (params: any) => {
|
||||
queryParams.value = params
|
||||
}
|
||||
|
||||
// 表格
|
||||
const tableRef = ref<Record<string, any>>({}); // 表格实例
|
||||
const table = reactive({
|
||||
|
@ -169,6 +173,7 @@ const table = reactive({
|
|||
value: props.parentId,
|
||||
},
|
||||
],
|
||||
type: 'and'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -213,4 +218,11 @@ const table = reactive({
|
|||
});
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.parentId,
|
||||
() => {
|
||||
table.refresh();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
<div
|
||||
class="pager"
|
||||
v-if="
|
||||
requestBody.params.paramsTable.length &&
|
||||
requestBody.params.paramsTable.length > 10 &&
|
||||
requestBody.pageSize
|
||||
"
|
||||
>
|
||||
|
|
|
@ -83,10 +83,27 @@ const rowSelection = {
|
|||
selectedRowKeys: ref<string[]>([]),
|
||||
};
|
||||
const save = async () => {
|
||||
const keys = props.selectedRowKeys;
|
||||
// fix: #bug10828
|
||||
// 当前节点表格数据id
|
||||
const currenTableKeys = props.tableData.map((m: any) => m.id);
|
||||
// 当前表格选中的id
|
||||
const currentSelectedKeys = rowSelection.selectedRowKeys.value;
|
||||
// 当前表格, 原有选中的id
|
||||
const oldSelectedKeys = currenTableKeys.filter((key) =>
|
||||
props.sourceKeys.includes(key),
|
||||
);
|
||||
|
||||
const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key));
|
||||
const addKeys = keys.filter((key) => !props.sourceKeys.includes(key));
|
||||
// const keys = props.selectedRowKeys;
|
||||
// const removeKeys = props.sourceKeys.filter((key) => !keys.includes(key));
|
||||
// const addKeys = keys.filter((key) => !props.sourceKeys.includes(key));
|
||||
// 取消选择的数据项
|
||||
const removeKeys = oldSelectedKeys.filter(
|
||||
(key) => !currentSelectedKeys.includes(key),
|
||||
);
|
||||
// 新增选择的项
|
||||
const addKeys = currentSelectedKeys.filter(
|
||||
(key) => !oldSelectedKeys.includes(key),
|
||||
);
|
||||
|
||||
if (props.mode === 'api') {
|
||||
// 此时是api配置
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
v-model:value="form.data.targetType"
|
||||
:disabled="!!form.data.id"
|
||||
@change="form.rules.checkUnique"
|
||||
placeholder="请选择关联方"
|
||||
placeholder="请选择被关联方"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in targetList"
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
</PermissionButton>
|
||||
|
||||
<PermissionButton
|
||||
:danger="true"
|
||||
:hasPermission="`${permission}:delete`"
|
||||
type="link"
|
||||
:tooltip="{ title: '删除' }"
|
||||
|
@ -161,6 +162,8 @@ const table = {
|
|||
if (resp.status === 200) {
|
||||
tableRef.value?.reload();
|
||||
message.success('操作成功!');
|
||||
} else {
|
||||
message.error(resp.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -117,9 +117,12 @@
|
|||
show-search
|
||||
style="width: 100%"
|
||||
placeholder="请选择组织"
|
||||
multiple
|
||||
:tree-data="form.departmentOptions"
|
||||
:fieldNames="{ label: 'name', value: 'id' }"
|
||||
multiple
|
||||
:filterTreeNode="
|
||||
(v: string, node: any) => filterSelectNode(v, node, 'name')
|
||||
"
|
||||
>
|
||||
<template #title="{ name }">
|
||||
{{ name }}
|
||||
|
@ -195,6 +198,7 @@ import { Rule } from 'ant-design-vue/es/form';
|
|||
import { DefaultOptionType } from 'ant-design-vue/es/vc-tree-select/TreeSelect';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { passwordRegEx } from '@/utils/validate';
|
||||
import { filterSelectNode } from '@/utils/comm';
|
||||
|
||||
const deptPermission = 'system/Department';
|
||||
const rolePermission = 'system/Role';
|
||||
|
@ -250,7 +254,8 @@ const form = reactive({
|
|||
if (!value) return reject('请输入密码');
|
||||
else if (value.length > 64) return reject('最多可输入64个字符');
|
||||
else if (value.length < 8) return reject('密码不能少于8位');
|
||||
else if (!passwordRegEx(value)) return reject('密码必须包含大小写英文和数字');
|
||||
else if (!passwordRegEx(value))
|
||||
return reject('密码必须包含大小写英文和数字');
|
||||
validateField_api('password', value).then((resp: any) => {
|
||||
resp.result.passed
|
||||
? resolve('')
|
||||
|
|
|
@ -3700,8 +3700,8 @@ jetlinks-store@^0.0.3:
|
|||
|
||||
jetlinks-ui-components@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#8cb5c9e68e46e6e7eebc0d96b1cdaab24828779f"
|
||||
integrity sha512-yIbmplK+twekevr7n+dGMvO8tvyIqguC60TWeJCmx2mUqpwv8dEnr/cwwpJee4PBLWohvGPywsYgmm7KxVBbcw==
|
||||
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#6dc396bc8a1b6f5a08accf5f46aec2099c15f481"
|
||||
integrity sha512-mnVN6MfHfyZf82miEoZV8+ud6RBH29x0A8PfpcraFQUl9Wat6XcjGppr8FOkmFbGw8laCGK5jlbiX3cYJUwaxw==
|
||||
dependencies:
|
||||
"@vueuse/core" "^9.12.0"
|
||||
ant-design-vue "^3.2.15"
|
||||
|
|
Loading…
Reference in New Issue