Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-23 11:48:46 +08:00
commit e9d5c8e9c3
69 changed files with 862 additions and 519 deletions

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1,5 +1,10 @@
<template> <template>
<ConfigProvider :locale='zhCN'> <ConfigProvider :locale='zhCN'>
<!-- <router-view v-slot="{ Component }">-->
<!-- <keep-alive>-->
<!-- <component :is="Component" />-->
<!-- </keep-alive>-->
<!-- </router-view>-->
<router-view /> <router-view />
</ConfigProvider> </ConfigProvider>
</template> </template>

View File

@ -6,6 +6,7 @@
v-model:selectedKeys="state.selectedKeys" v-model:selectedKeys="state.selectedKeys"
:pure="state.pure" :pure="state.pure"
:breadcrumb="{ routes: breadcrumb }" :breadcrumb="{ routes: breadcrumb }"
@backClick='routerBack'
> >
<template #breadcrumbRender="slotProps"> <template #breadcrumbRender="slotProps">
<a <a
@ -70,6 +71,10 @@ const state = reactive<StateType>({
selectedKeys: [], selectedKeys: [],
}); });
const routerBack = () => {
router.go(-1)
}
const findRouteMeta = (code: string) => { const findRouteMeta = (code: string) => {
let meta = [] let meta = []
let menuItem: any = menu.menus[code] let menuItem: any = menu.menus[code]

View File

@ -194,7 +194,7 @@ const timeChange = (e: any) => {
} }
const inputChange = (e: any) => { const inputChange = (e: any) => {
emit('change', e.target ? e.target.value : e) emit('change', e && e.target ? e.target.value : e)
} }
const dateChange = (e: any) => { const dateChange = (e: any) => {

View File

@ -51,7 +51,7 @@ export const AccountMenu = {
export default [ export default [
{ path: '/*', redirect: '/'}, { path: '/*', redirect: '/'},
{ {
path: '/login', path: LoginPath,
component: () => import('@/views/user/Login/index.vue') component: () => import('@/views/user/Login/index.vue')
}, },
{ {

View File

@ -155,10 +155,11 @@ const errorHandler = (error: any) => {
Notification.error({ Notification.error({
key: '401', key: '401',
message: 'Unauthorized', message: 'Unauthorized',
description: 'Authorization verification failed' description: '用户未登录'
}) })
setTimeout(() => { setTimeout(() => {
router.replace({ debugger
router.push({
path: LoginPath path: LoginPath
}) })
}, 0) }, 0)

View File

@ -16,6 +16,9 @@
moment(slotProps.requestTime).format('YYYY-MM-DD HH:mm:ss') moment(slotProps.requestTime).format('YYYY-MM-DD HH:mm:ss')
}} }}
</template> </template>
<template #description="slotProps">
{{ slotProps.action }}
</template>
<template #responseTime="slotProps"> <template #responseTime="slotProps">
<j-tag color="purple"> <j-tag color="purple">
{{ slotProps.responseTime - slotProps.requestTime }} ms {{ slotProps.responseTime - slotProps.requestTime }} ms
@ -59,12 +62,7 @@
</template> </template>
</j-pro-table> </j-pro-table>
</div> </div>
<j-modal <j-modal :width="1100" v-model:visible="visible" title="详情">
:width="1100"
v-model:visible="visible"
title="详情"
@ok="handleOk"
>
<j-descriptions :data="descriptionsData" title="" bordered :column="2"> <j-descriptions :data="descriptionsData" title="" bordered :column="2">
<j-descriptions-item label="URL"> <j-descriptions-item label="URL">
{{ descriptionsData?.url }} {{ descriptionsData?.url }}
@ -112,6 +110,9 @@
/> />
</j-descriptions-item> </j-descriptions-item>
</j-descriptions> </j-descriptions>
<template #footer>
<j-button type="primary" @click="handleOk">关闭</j-button>
</template>
</j-modal> </j-modal>
</template> </template>
<script lang="ts" setup name="AccessLog"> <script lang="ts" setup name="AccessLog">
@ -145,6 +146,16 @@ const columns = [
}, },
ellipsis: true, ellipsis: true,
}, },
{
title: '说明',
dataIndex: 'description',
key: 'description',
scopedSlots: true,
search: {
type: 'string',
},
ellipsis: true,
},
{ {
title: '请求方法', title: '请求方法',
dataIndex: 'httpMethod', dataIndex: 'httpMethod',
@ -198,9 +209,9 @@ const columns = [
title: '请求用户', title: '请求用户',
dataIndex: 'username', dataIndex: 'username',
key: 'username', key: 'username',
search: { // search: {
type: 'string', // type: 'string',
}, // },
width: 150, width: 150,
scopedSlots: true, scopedSlots: true,
}, },

View File

@ -66,12 +66,7 @@
</template> </template>
</j-pro-table> </j-pro-table>
</div> </div>
<j-modal <j-modal :width="1100" v-model:visible="visible" title="详情">
:width="1100"
v-model:visible="visible"
title="详情"
@ok="handleOk"
>
<div> <div>
<span class="mr-10">[{{ descriptionsData?.threadName }}]</span> <span class="mr-10">[{{ descriptionsData?.threadName }}]</span>
<span class="mr-10">{{ <span class="mr-10">{{
@ -102,6 +97,9 @@
placeholder="暂无数据" placeholder="暂无数据"
:auto-size="{ minRows: 24, maxRows: 28 }" :auto-size="{ minRows: 24, maxRows: 28 }"
/> />
<template #footer>
<j-button type="primary" @click="handleOk">关闭</j-button>
</template>
</j-modal> </j-modal>
</template> </template>
<script lang="ts" setup name="SystemLog"> <script lang="ts" setup name="SystemLog">

View File

@ -19,6 +19,7 @@
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
valueFormat="YYYY-MM-DD HH:mm:ss" valueFormat="YYYY-MM-DD HH:mm:ss"
style="margin-left: 12px" style="margin-left: 12px"
:show-time="{ format: 'HH:mm:ss' }"
@change="rangeChange" @change="rangeChange"
v-model:value="rangeVal" v-model:value="rangeVal"
:allowClear="false" :allowClear="false"
@ -110,5 +111,5 @@ const handleBtnChange = (val: string) => {
}); });
}; };
handleBtnChange(radioValue.value); handleBtnChange(radioValue.value);
watch(() => radioValue.value, { deep: true, immediate: true }); // watch(() => radioValue.value, { deep: true, immediate: true });
</script> </script>

View File

@ -364,7 +364,7 @@ const setDevMesChartOption = (
grid: { grid: {
top: '2%', top: '2%',
bottom: '5%', bottom: '5%',
left: maxY > 100000 ? '90px' : '60px', left: maxY > 100000 ? '50px' : '70px',
right: '50px', right: '50px',
}, },
series: [ series: [

View File

@ -237,6 +237,8 @@ const emit = defineEmits(['change']);
const id = props.data.id; const id = props.data.id;
const VersionOrder = props.data.versionOrder; const VersionOrder = props.data.versionOrder;
const VersionSign = props.data.sign;
const VersionUrl = props.data.url;
const formData: any = ref({ const formData: any = ref({
name: '', name: '',
@ -254,12 +256,14 @@ const extraValue: any = ref({});
const validatorSign = async (_: Record<string, any>, value: string) => { const validatorSign = async (_: Record<string, any>, value: string) => {
const { signMethod, url } = formData.value; const { signMethod, url } = formData.value;
if (value && !!signMethod && !!url && !extraValue.value) { if (id && VersionSign === value && VersionUrl === url) {
return Promise.resolve();
} else {
if (value && !!signMethod && !!url && !!extraValue.value) {
return extraValue.value[signMethod] !== value return extraValue.value[signMethod] !== value
? Promise.reject('签名不一致,请检查文件是否上传正确') ? Promise.reject('签名不一致,请检查文件是否上传正确')
: Promise.resolve(); : Promise.resolve();
} else { }
return Promise.resolve();
} }
}; };
const validatorVersionOrder = async (_: Record<string, any>, value: string) => { const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
@ -270,7 +274,9 @@ const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
if (value && !!signMethod && productId) { if (value && !!signMethod && productId) {
const res = await validateVersion(productId, value); const res = await validateVersion(productId, value);
if (res.status === 200) { if (res.status === 200) {
return Promise.reject(res.result ? '版本序号已存在' : ''); return res.result
? Promise.reject('版本序号已存在')
: Promise.resolve();
} }
} }
} }

View File

@ -280,7 +280,7 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
if (!data) { if (!data) {
return []; return [];
} }
const Actions = [ const Actions: any = [
{ {
key: 'view', key: 'view',
text: '查看', text: '查看',
@ -292,7 +292,11 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
handlEye(data.errorReason); handlEye(data.errorReason);
}, },
}, },
{ ];
const { state, mode } = data;
if (mode.value === 'push' && state.value === 'failed') {
Actions.push({
key: 'update', key: 'update',
text: '重试', text: '重试',
tooltip: { tooltip: {
@ -305,8 +309,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
handlTry(data.id); handlTry(data.id);
}, },
}, },
}, });
]; }
return Actions; return Actions;
}; };

View File

@ -208,10 +208,4 @@ watch(
); );
</script> </script>
<style lang="less" scoped> <style lang="less" scoped></style>
.form {
.form-submit {
background-color: @primary-color !important;
}
}
</style>

View File

@ -143,7 +143,7 @@ const columns = [
key: 'createTime', key: 'createTime',
dataIndex: 'createTime', dataIndex: 'createTime',
search: { search: {
type: 'time', type: 'date',
}, },
width: 200, width: 200,
scopedSlots: true, scopedSlots: true,
@ -197,6 +197,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
{ {
key: 'delete', key: 'delete',
text: '删除', text: '删除',
tooltip: {
title: '删除',
},
popConfirm: { popConfirm: {
title: '确认删除?', title: '确认删除?',
okText: ' 确定', okText: ' 确定',

View File

@ -147,6 +147,10 @@ const columns = [
dataIndex: 'id', dataIndex: 'id',
key: 'id', key: 'id',
ellipsis: true, ellipsis: true,
search:{
type:'string',
defaultTermType: 'eq'
}
}, },
{ {
title: '设备名称', title: '设备名称',

View File

@ -1,21 +1,16 @@
<template> <template>
<page-container <page-container
:tabList="list" :tabList="list"
:showBack="true"
:tabActiveKey="instanceStore.tabActiveKey" :tabActiveKey="instanceStore.tabActiveKey"
@tabChange="onTabChange" @tabChange="onTabChange"
> >
<template #title> <template #title>
<div>
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<!-- <j-button @click="onBack" size="small">返回</j-button> -->
<div style="font-size: 24px">
{{ instanceStore.current?.name }} {{ instanceStore.current?.name }}
</div>
<j-divider type="vertical" /> <j-divider type="vertical" />
<j-space> <j-space>
<span <span style="font-size: 14px; color: rgba(0, 0, 0, 0.85)">
style="font-size: 14px; color: rgba(0, 0, 0, 0.85)"
>
状态 状态
<j-badge <j-badge
:status=" :status="
@ -28,8 +23,7 @@
</span> </span>
<PermissionButton <PermissionButton
v-if=" v-if="
instanceStore.current?.state?.value === instanceStore.current?.state?.value === 'notActive'
'notActive'
" "
type="link" type="link"
style="margin-top: -5px; padding: 0 20px" style="margin-top: -5px; padding: 0 20px"
@ -42,9 +36,7 @@
启用设备 启用设备
</PermissionButton> </PermissionButton>
<PermissionButton <PermissionButton
v-if=" v-if="instanceStore.current?.state?.value === 'online'"
instanceStore.current?.state?.value === 'online'
"
type="link" type="link"
style="margin-top: -5px; padding: 0 20px" style="margin-top: -5px; padding: 0 20px"
:popConfirm="{ :popConfirm="{
@ -59,8 +51,7 @@
v-if=" v-if="
instanceStore.current?.accessProvider === instanceStore.current?.accessProvider ===
'child-device' && 'child-device' &&
instanceStore.current?.state?.value === instanceStore.current?.state?.value === 'offline'
'offline'
" "
:title=" :title="
instanceStore.current?.features?.find( instanceStore.current?.features?.find(
@ -77,7 +68,8 @@
</j-tooltip> </j-tooltip>
</j-space> </j-space>
</div> </div>
<div style="padding-top: 24px"> </template>
<template #content>
<j-descriptions size="small" :column="4"> <j-descriptions size="small" :column="4">
<j-descriptions-item label="ID">{{ <j-descriptions-item label="ID">{{
instanceStore.current?.id instanceStore.current?.id
@ -93,8 +85,6 @@
</PermissionButton> </PermissionButton>
</j-descriptions-item> </j-descriptions-item>
</j-descriptions> </j-descriptions>
</div>
</div>
</template> </template>
<template #extra> <template #extra>
<img <img
@ -142,7 +132,7 @@ statusMap.set('notActive', 'warning');
const statusRef = ref(); const statusRef = ref();
const list = ref([ const initList = [
{ {
key: 'Info', key: 'Info',
tab: '实例信息', tab: '实例信息',
@ -163,7 +153,9 @@ const list = ref([
key: 'Log', key: 'Log',
tab: '日志管理', tab: '日志管理',
}, },
]); ];
const list = ref([...initList]);
const tabs = { const tabs = {
Info, Info,
@ -191,64 +183,7 @@ const getStatus = (id: string) => {
}); });
}; };
watch( const getDetail = () => {
() => route.params?.id,
(newId) => {
if (newId) {
instanceStore.refresh(String(newId));
getStatus(String(newId));
instanceStore.tabActiveKey = 'Info'
}
},
{ immediate: true, deep: true },
);
onMounted(() => {
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
});
// const onBack = () => {
// menuStory.jumpPage('device/Instance');
// };
const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e;
};
const handleAction = async () => {
if (instanceStore.current?.id) {
const resp = await _deploy(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleDisconnect = async () => {
if (instanceStore.current?.id) {
const resp = await _disconnect(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleRefresh = async () => {
if (instanceStore.current?.id) {
await instanceStore.refresh(instanceStore.current?.id);
message.success('操作成功');
}
};
const jumpProduct = () => {
menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current?.productId,
});
};
watchEffect(() => {
const keys = list.value.map((i) => i.key); const keys = list.value.map((i) => i.key);
if ( if (
instanceStore.current?.protocol && instanceStore.current?.protocol &&
@ -309,8 +244,67 @@ watchEffect(() => {
tab: '边缘端映射', tab: '边缘端映射',
}); });
} }
};
watch(
() => route.params?.id,
async (newId) => {
if (newId) {
await instanceStore.refresh(String(newId));
getStatus(String(newId));
list.value = [...initList];
getDetail();
instanceStore.tabActiveKey = 'Info';
}
},
{ immediate: true, deep: true },
);
onMounted(() => {
instanceStore.tabActiveKey = history.state?.params?.tab || 'Info';
}); });
const onBack = () => {
menuStory.jumpPage('device/Instance');
};
const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e;
};
const handleAction = async () => {
if (instanceStore.current?.id) {
const resp = await _deploy(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleDisconnect = async () => {
if (instanceStore.current?.id) {
const resp = await _disconnect(instanceStore.current?.id);
if (resp.status === 200) {
message.success('操作成功!');
instanceStore.refresh(instanceStore.current?.id);
}
}
};
const handleRefresh = async () => {
if (instanceStore.current?.id) {
await instanceStore.refresh(instanceStore.current?.id);
message.success('操作成功');
}
};
const jumpProduct = () => {
menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current?.productId,
});
};
onUnmounted(() => { onUnmounted(() => {
statusRef.value && statusRef.value.unsubscribe(); statusRef.value && statusRef.value.unsubscribe();
}); });

View File

@ -568,7 +568,17 @@ const query = reactive({
}); });
const param = ref<Record<string, any>>({ const param = ref<Record<string, any>>({
pageSize: 4, pageSize: 4,
terms: [], terms: [
{
terms: [
{
column: 'channel',
termType: 'nin',
value: 'plugin',
},
],
},
],
}); });
const queryParams = ref<Record<string, any>>({}); const queryParams = ref<Record<string, any>>({});
/** /**
@ -669,49 +679,6 @@ const driver1 = new Driver({
}, },
}); });
/**
* 表格列表
*/
// const columnsMQTT: any[] = [
// {
// title: '',
// dataIndex: 'group',
// key: 'group',
// ellipsis: true,
// width: 100,
// // customCell: (record: any, index: number) => {
// // const list =
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
// // const arr = list.filter((res: any) => {
// // // gpsNumber
// // return res?.group == record?.group;
// // });
// // if (index == 0 || list[index - 1]?.group != record?.group) {
// // return { rowSpan: arr.length };
// // } else {
// // return { rowSpan: 0 };
// // }
// // },
// },
// {
// title: 'topic',
// dataIndex: 'topic',
// key: 'topic',
// },
// {
// title: '',
// dataIndex: 'stream',
// key: 'stream',
// ellipsis: true,
// align: 'center',
// width: 100,
// },
// {
// title: '',
// dataIndex: 'description',
// key: 'description',
// },
// ];
let columnsMQTT = ref(<TableColumnType>[]); let columnsMQTT = ref(<TableColumnType>[]);
const ColumnsMQTT = [ const ColumnsMQTT = [
{ {
@ -760,38 +727,6 @@ const ColumnsHTTP = [
// scopedSlots: { customRender: 'description' }, // scopedSlots: { customRender: 'description' },
}, },
]; ];
// const columnsHTTP: any[] = [
// {
// title: '',
// dataIndex: 'group',
// key: 'group',
// ellipsis: true,
// width: 100,
// // customCell: (record: any, index: number) => {
// // const list =
// // (config?.routes || []).sort((a: any, b: any) => a - b) || [];
// // const arr = list.filter((res: any) => {
// // // gpsNumber
// // return res?.group == record?.group;
// // });
// // if (index == 0 || list[index - 1]?.group != record?.group) {
// // return { rowSpan: arr.length };
// // } else {
// // return { rowSpan: 0 };
// // }
// // },
// },
// {
// title: '',
// dataIndex: 'example',
// key: 'example',
// },
// {
// title: '',
// dataIndex: 'description',
// key: 'description',
// },
// ];
/** /**
* 获取上下行数据 * 获取上下行数据
*/ */
@ -892,6 +827,28 @@ const submitData = async () => {
accessProvider: current.value?.provider, accessProvider: current.value?.provider,
messageProtocol: current.value?.protocol, messageProtocol: current.value?.protocol,
}; };
// getConfigView(current.value?.protocol, current.value?.transport).then(
// (resp: any) => {
// metadata.value = (resp?.result[0] as ConfigMetadata) || {
// properties: [],
// };
// // udp
// if (metadata.value?.properties) {
// metadata.value?.properties.forEach((item) => {
// if (
// item.name === '' &&
// (!productStore.current?.configuration ||
// !productStore.current?.configuration.hasOwnProperty(
// item.name,
// ))
// ) {
// formData.data[item.name] =
// item.type.expands?.defaultValue;
// }
// });
// }
// },
// );
const metatdata = JSON.parse(productStore.current?.metadata || '{}'); const metatdata = JSON.parse(productStore.current?.metadata || '{}');
if (!productStore.current?.metadata) { if (!productStore.current?.metadata) {
const response = await getConfigView( const response = await getConfigView(

View File

@ -563,7 +563,7 @@ const query = reactive({
}, },
}, },
{ {
title: '所属部门', title: '所属组织',
key: 'id$dim-assets', key: 'id$dim-assets',
dataIndex: 'id$dim-assets', dataIndex: 'id$dim-assets',
search: { search: {

View File

@ -196,11 +196,12 @@ const resetMetadata = async () => {
// } // }
const { id } = route.params const { id } = route.params
if (target === 'device') { if (target === 'device') {
instanceStore.refresh(id as string) await instanceStore.refresh(id as string)
} else { } else {
productStore.refresh(id as string) await productStore.getDetail(id as string)
} }
metadataStore.set('importMetadata', true) metadataStore.set('importMetadata', true)
}; };
const removeItem = async (record: MetadataItem) => { const removeItem = async (record: MetadataItem) => {

View File

@ -61,12 +61,15 @@ const close = () => {
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore()
const productStore = useProductStore() const productStore = useProductStore()
const metadata = computed(() => {
const metadataMap = { const metadataMap = {
product: productStore.current?.metadata as string, product: productStore.current?.metadata as string,
device: instanceStore.current?.metadata as string, device: instanceStore.current?.metadata as string,
}; };
const metadata = metadataMap[props.type]; return metadataMap[props.type];
const value = ref(metadata) })
// const metadata = metadataMap[props.type];
const value = ref(metadata.value)
const handleExport = async () => { const handleExport = async () => {
try { try {
downloadObject( downloadObject(
@ -86,14 +89,14 @@ const handleConvertMetadata = (key: Key) => {
if (key === 'alink') { if (key === 'alink') {
value.value = ''; value.value = '';
if (metadata) { if (metadata) {
convertMetadata('to', 'alink', JSON.parse(metadata)).then(res => { convertMetadata('to', 'alink', JSON.parse(metadata.value)).then(res => {
if (res.status === 200) { if (res.status === 200) {
value.value = JSON.stringify(res.result) value.value = JSON.stringify(res.result)
} }
}); });
} }
} else { } else {
value.value = metadata; value.value = metadata.value;
} }
}; };

View File

@ -237,6 +237,11 @@ const getData = (
return new Promise((resolve) => { return new Promise((resolve) => {
queryFlow(start, end, { queryFlow(start, end, {
orderBy: 'date', orderBy: 'date',
terms: [{
column : "cardId",
termType: "eq",
value: route.params.id
}]
}).then((resp: any) => { }).then((resp: any) => {
if (resp.status === 200) { if (resp.status === 200) {
const sortArray = resp.result.sort( const sortArray = resp.result.sort(

View File

@ -232,8 +232,8 @@ const handleOk = () => {
btnLoading.value = true; btnLoading.value = true;
const resp = const resp =
props.type === 'add' props.type === 'add'
? await add(toRaw(modelRef)) ? await add(toRaw(modelRef)).catch(err => err)
: await edit(toRaw(modelRef)); : await edit(toRaw(modelRef)).catch(err => err);
btnLoading.value = false; btnLoading.value = false;
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功') message.success('操作成功')

View File

@ -457,6 +457,7 @@ const columns = [
width: 200, width: 200,
search: { search: {
type: 'string', type: 'string',
defaultTermType: 'eq'
}, },
}, },
{ {
@ -466,6 +467,9 @@ const columns = [
ellipsis: true, ellipsis: true,
scopedSlots: true, scopedSlots: true,
width: 200, width: 200,
search: {
type: 'string'
}
}, },
{ {
title: '平台对接', title: '平台对接',
@ -571,7 +575,8 @@ const columns = [
{ label: '正常', value: 'using' }, { label: '正常', value: 'using' },
{ label: '未激活', value: 'toBeActivated' }, { label: '未激活', value: 'toBeActivated' },
{ label: '停机', value: 'deactivate' }, { label: '停机', value: 'deactivate' },
], { label: '其它', value: 'using,toBeActivated,deactivate' },
]
}, },
}, },
{ {
@ -737,7 +742,16 @@ const getActions = (
}; };
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
params.value = e; const newParams = (e?.terms as any[])?.map(item1 => {
item1.terms = item1.terms.map((item2: any) => {
if (['cardStateType'].includes(item2.column)) {
item2.termType = 'nin'
}
return item2
})
return item1
})
params.value = { terms: newParams || [] };
}; };
const onSelectChange = (keys: string[], rows: []) => { const onSelectChange = (keys: string[], rows: []) => {

View File

@ -87,7 +87,7 @@
</page-container> </page-container>
</template> </template>
<script setup lang="ts"> <script setup lang="ts" name='IotCardHome'>
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import Guide from '../components/Guide.vue'; import Guide from '../components/Guide.vue';
import moment from 'moment'; import moment from 'moment';
@ -113,14 +113,14 @@ const menuHasPermission = useMenuStore().hasPermission;
const btnHasPermission = usePermissionStore().hasPermission; const btnHasPermission = usePermissionStore().hasPermission;
// //
const dashBoardUrl = menuHasPermission('/iot-card/Dashboard'); const dashBoardUrl = menuHasPermission('iot-card/Dashboard');
const platformUrl = menuHasPermission('/iot-card/Platform/Detail'); const platformUrl = menuHasPermission('iot-card/Platform/Detail');
const recordUrl = menuHasPermission('/iot-card/Record'); const recordUrl = menuHasPermission('iot-card/Record');
const cardUrl = menuHasPermission('/iot-card/CardManagement'); const cardUrl = menuHasPermission('iot-card/CardManagement');
// //
const paltformPermission = btnHasPermission(`/iot-card/Platform:add`); const paltformPermission = btnHasPermission(`iot-card/Platform:add`);
const cardPermission = btnHasPermission(`/iot-card/CardManagement:add`); const cardPermission = btnHasPermission(`iot-card/CardManagement:add`);
const Image = { const Image = {
1: getImage('/home/1.png'), 1: getImage('/home/1.png'),
@ -179,23 +179,19 @@ const pieChartData = ref<any[]>([
]); ]);
const jumpPage = (data: GuideItemProps) => { const jumpPage = (data: GuideItemProps) => {
console.log(data.auth)
if (!data.auth){ if (!data.auth){
message.warning('暂无权限,请联系管理员'); message.warning('暂无权限,请联系管理员');
return return
} }
if (data.key === 'EQUIPMENT') { if (data.key === 'EQUIPMENT') {
menuStory.jumpPage(data.url, { id: 'add' }); menuStory.jumpPage(data.url, { id: ':id'});
} else { } else {
menuStory.jumpPage(data.url); menuStory.jumpPage(data.url);
} }
}; };
const jumpDashboard = () => { const jumpDashboard = () => {
// if (dashBoardUrl) {
// router.push(`${dashBoardUrl}`);
// } else {
// message.warning('');
// }
menuStory.jumpPage('iot-card/Dashboard'); menuStory.jumpPage('iot-card/Dashboard');
}; };

View File

@ -204,6 +204,7 @@ const rules = {
}; };
const getDetail = async () => { const getDetail = async () => {
console.log(route.params)
if (route.params.id === ':id') return; if (route.params.id === ':id') return;
const resp: any = await queryById(route.params.id); const resp: any = await queryById(route.params.id);
if (resp.status === 200) { if (resp.status === 200) {

View File

@ -238,7 +238,7 @@ const handleSearch = (e: any) => {
* 新增 * 新增
*/ */
const handleAdd = () => { const handleAdd = () => {
menuStory.jumpPage('iot-card/Platform/Detail', { id: 'add' }) menuStory.jumpPage('iot-card/Platform/Detail', { id: ':id' })
}; };
</script> </script>

View File

@ -16,8 +16,9 @@
</j-radio-button> </j-radio-button>
</j-radio-group> </j-radio-group>
<j-range-picker <j-range-picker
format="YYYY-MM-DD" format="YYYY-MM-DD HH:mm:ss"
valueFormat="YYYY-MM-DD" valueFormat="YYYY-MM-DD HH:mm:ss"
:show-time="{ format: 'HH:mm:ss' }"
style="margin-left: 12px" style="margin-left: 12px"
@change="rangeChange" @change="rangeChange"
v-model:value="rangeVal" v-model:value="rangeVal"
@ -28,7 +29,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import moment from 'moment'; import dayjs from 'dayjs';
import { PropType } from 'vue'; import { PropType } from 'vue';
interface BtnOptions { interface BtnOptions {
@ -70,38 +71,37 @@ const rangeVal = ref<[string, string]>();
const rangeChange = (val: any) => { const rangeChange = (val: any) => {
radioValue.value = undefined; radioValue.value = undefined;
emit('change', { emit('change', {
start: moment(val[0]).valueOf(), start: dayjs(val[0]).valueOf(),
end: moment(val[1]).valueOf(), end: dayjs(val[1]).valueOf(),
type: undefined, type: undefined,
}); });
}; };
const getTimeByType = (type: string) => { const getTimeByType = (type?: string) => {
switch (type) { switch (type) {
case 'hour': case 'hour':
return moment().subtract(1, 'hours').valueOf(); return dayjs().subtract(1, 'hours').valueOf();
case 'week': case 'week':
return moment().subtract(6, 'days').valueOf(); return dayjs().subtract(6, 'days').valueOf();
case 'month': case 'month':
return moment().subtract(29, 'days').valueOf(); return dayjs().subtract(29, 'days').valueOf();
case 'year': case 'year':
return moment().subtract(365, 'days').valueOf(); return dayjs().subtract(365, 'days').valueOf();
default: default:
return moment().startOf('day').valueOf(); return dayjs().startOf('day').valueOf();
} }
}; };
const handleBtnChange = (val: string) => { const handleBtnChange = (val?: string) => {
radioValue.value = val; let endTime = dayjs(new Date()).valueOf();
let endTime = moment(new Date()).valueOf();
let startTime = getTimeByType(val); let startTime = getTimeByType(val);
if (val === 'yesterday') { if (val === 'yesterday') {
startTime = moment().subtract(1, 'days').startOf('day').valueOf(); startTime = dayjs().subtract(1, 'days').startOf('day').valueOf();
endTime = moment().subtract(1, 'days').endOf('day').valueOf(); endTime = dayjs().subtract(1, 'days').endOf('day').valueOf();
} }
rangeVal.value = [ rangeVal.value = [
moment(startTime).format('YYYY-MM-DD'), dayjs(startTime).format('YYYY-MM-DD HH:mm:ss'),
moment(endTime).format('YYYY-MM-DD'), dayjs(endTime).format('YYYY-MM-DD HH:mm:ss'),
]; ];
emit('change', { emit('change', {
start: startTime, start: startTime,
@ -110,11 +110,8 @@ const handleBtnChange = (val: string) => {
}); });
}; };
watch( nextTick(() => {
() => radioValue.value, handleBtnChange(radioValue.value)
(val) => { })
handleBtnChange(val);
},
{ deep: true, immediate: true },
);
</script> </script>

View File

@ -3,7 +3,7 @@
hoverable hoverable
:class="[ :class="[
'card-render', 'card-render',
'container', `access-${data.type || 'network'}`,
checked === data.id ? 'checked' : '', checked === data.id ? 'checked' : '',
]" ]"
@click="checkedChange(data.id)" @click="checkedChange(data.id)"
@ -106,8 +106,16 @@ const checkedChange = (id: string) => {
} }
} }
} }
.container { .access-media {
background: url('/public/images/access-icon.png') no-repeat; background: url('/public/images/access-media.png') no-repeat;
background-position: bottom right;
}
.access-network {
background: url('/public/images/access-network.png') no-repeat;
background-position: bottom right;
}
.access-protocol {
background: url('/public/images/access-protocol.png') no-repeat;
background-position: bottom right; background-position: bottom right;
} }
</style> </style>

View File

@ -20,7 +20,11 @@
message: '请输入名称', message: '请输入名称',
trigger: 'blur', trigger: 'blur',
}, },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]" ]"
> >
<j-input <j-input
@ -119,7 +123,7 @@ const onFinish = async (values: any) => {
}; };
onMounted(() => { onMounted(() => {
if (id === ':id') { if (id !== ':id') {
formState.value = { formState.value = {
name: props.data.name, name: props.data.name,
description: props.data?.description || '', description: props.data?.description || '',

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<j-steps class="steps-steps" :current="stepCurrent"> <j-steps class="steps-steps" :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" /> <j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps> </j-steps>
<div class="steps-content"> <div class="steps-content">
<div class="steps-box" v-if="current === 0"> <div class="steps-box" v-if="current === 0">
@ -51,6 +51,7 @@
max: 64, max: 64,
message: message:
'最多可输入64个字符', '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -75,6 +76,7 @@
max: 64, max: 64,
message: message:
'最多可输入64个字符', '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -184,7 +186,11 @@
</PermissionButton> </PermissionButton>
</div> </div>
<j-scrollbar height="480"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
@ -193,12 +199,16 @@
<AccessCard <AccessCard
@checkedChange="procotolChange" @checkedChange="procotolChange"
:checked="procotolCurrent" :checked="procotolCurrent"
:data="item" :data="{ ...item, type: 'protocol' }"
> >
</AccessCard> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar> </j-scrollbar>
</div> </div>
</div> </div>
@ -222,7 +232,11 @@
message: '请输入名称', message: '请输入名称',
trigger: 'blur', trigger: 'blur',
}, },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]" ]"
> >
<j-input <j-input

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<j-steps class="steps-steps" :current="stepCurrent"> <j-steps class="steps-steps" :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" /> <j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps> </j-steps>
<div class="steps-content"> <div class="steps-content">
<div class="steps-box" v-if="current === 0"> <div class="steps-box" v-if="current === 0">
@ -63,6 +63,7 @@
max: 64, max: 64,
message: message:
'最多可输入64个字符', '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -86,13 +87,14 @@
max: 64, max: 64,
message: message:
'最多可输入64个字符', '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
<template #label> <template #label>
通知Token 通知Token
<j-tooltip <j-tooltip
title="接收OneNet推送的Token地址" title="自定义token,可用于验证请求是否来自OneNet"
> >
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
@ -116,6 +118,7 @@
max: 64, max: 64,
message: message:
'最多可输入64个字符', '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -266,7 +269,11 @@
</PermissionButton> </PermissionButton>
</div> </div>
<j-scrollbar height="480"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
@ -275,12 +282,16 @@
<AccessCard <AccessCard
@checkedChange="procotolChange" @checkedChange="procotolChange"
:checked="procotolCurrent" :checked="procotolCurrent"
:data="item" :data="{ ...item, type: 'protocol' }"
> >
</AccessCard> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar> </j-scrollbar>
</div> </div>
</div> </div>
@ -305,7 +316,11 @@
message: '请输入名称', message: '请输入名称',
trigger: 'blur', trigger: 'blur',
}, },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]" ]"
> >
<j-input <j-input

View File

@ -5,7 +5,7 @@
class="steps-steps" class="steps-steps"
:current="stepCurrent" :current="stepCurrent"
> >
<j-step v-for="item in steps" :key="item" :title="item" /> <j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps> </j-steps>
<div v-if="channel !== 'edge-child-device'" class="steps-content"> <div v-if="channel !== 'edge-child-device'" class="steps-content">
<div class="steps-box" v-if="current === 0"> <div class="steps-box" v-if="current === 0">
@ -30,7 +30,11 @@
</PermissionButton> </PermissionButton>
</div> </div>
<j-scrollbar height="480"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0"> <j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="networkList.length > 0"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in networkList" v-for="item in networkList"
@ -91,7 +95,11 @@
</AccessCard> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar> </j-scrollbar>
</div> </div>
</div> </div>
@ -119,7 +127,11 @@
message: '请输入名称', message: '请输入名称',
trigger: 'blur', trigger: 'blur',
}, },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]" ]"
> >
<j-input <j-input

View File

@ -1,7 +1,7 @@
<template> <template>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<j-steps :current="stepCurrent"> <j-steps :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" /> <j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps> </j-steps>
<div class="steps-content"> <div class="steps-content">
<div class="steps-box" v-if="current === 0"> <div class="steps-box" v-if="current === 0">
@ -29,7 +29,8 @@
}, },
{ {
max: 64, max: 64,
message: '最大可输入64个字符', message: '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -50,7 +51,8 @@
}, },
{ {
max: 64, max: 64,
message: '最大可输入64个字符', message: '最多可输入64个字符',
trigger: 'blur',
}, },
]" ]"
> >
@ -202,7 +204,11 @@
<j-collapse v-model:activeKey="activeKey"> <j-collapse v-model:activeKey="activeKey">
<j-collapse-panel <j-collapse-panel
:key="cluster.id" :key="cluster.id"
:header="`#${index + 1}.节点`" :header="
cluster.clusterNodeId
? cluster.clusterNodeId
: `#${index + 1}.配置信息`
"
> >
<template #extra> <template #extra>
<span <span
@ -504,7 +510,7 @@ interface Form2 {
} }
interface FormState { interface FormState {
domain: string | undefined; domain: string | undefined;
sipId: string | undefined; sipId: string | number | undefined;
shareCluster: boolean; shareCluster: boolean;
hostPort: { hostPort: {
port: string | undefined; port: string | undefined;
@ -576,7 +582,7 @@ const removeCluster = (item: Form2) => {
}; };
const addCluster = () => { const addCluster = () => {
const id = Date.now(); const id: any = Date.now();
dynamicValidateForm.cluster.push({ dynamicValidateForm.cluster.push({
clusterNodeId: undefined, clusterNodeId: undefined,
port: undefined, port: undefined,
@ -613,7 +619,11 @@ const { resetFields, validate, validateInfos } = useForm(
reactive({ reactive({
name: [ name: [
{ required: true, message: '请输入名称', trigger: 'blur' }, { required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
], ],
description: [{ max: 200, message: '最多可输入200个字符' }], description: [{ max: 200, message: '最多可输入200个字符' }],
}), }),
@ -628,7 +638,7 @@ const saveData = () => {
transport: 'SIP', transport: 'SIP',
channel: 'gb28181', channel: 'gb28181',
}; };
params.configuration.sipId = Number(params.configuration?.sipId);
const resp = const resp =
id === ':id' ? await save(params) : await update({ ...params, id }); id === ':id' ? await save(params) : await update({ ...params, id });
if (resp.status === 200) { if (resp.status === 200) {
@ -645,7 +655,9 @@ const next = async () => {
data1.hostPort.port = port; data1.hostPort.port = port;
} }
if (!data1?.shareCluster) { if (!data1?.shareCluster) {
let data2 = await formRef2.value?.validate(); await formRef2.value
?.validate()
.then((data2) => {
if (data2 && data2?.cluster) { if (data2 && data2?.cluster) {
data2.cluster.forEach((i: any) => { data2.cluster.forEach((i: any) => {
i.enabled = true; i.enabled = true;
@ -656,9 +668,19 @@ const next = async () => {
...data2, ...data2,
}; };
} }
}
current.value = current.value + 1; current.value = current.value + 1;
params.configuration = data1; params.configuration = data1;
})
.catch((err) => {
err.errorFields.forEach((item: any) => {
const activeId: any =
dynamicValidateForm.cluster[item.name[1]].id;
if (!activeKey.value.includes(activeId)) {
activeKey.value.push(activeId); //
}
});
});
}
}; };
const prev = () => { const prev = () => {
current.value = current.value - 1; current.value = current.value - 1;
@ -698,11 +720,25 @@ onMounted(() => {
}); });
if (id !== ':id') { if (id !== ':id') {
formState.value = props.data.configuration; const { configuration, name, description = '' } = props.data;
formData.value = { formData.value = { name, description };
name: props.data.name,
description: props.data?.description || '', if (configuration?.shareCluster) {
formState.value = {
...formState.value,
...props.data.configuration,
}; };
} else {
formState.value = {
...formState.value,
...props.data.configuration,
};
dynamicValidateForm.cluster = configuration.cluster;
if (dynamicValidateForm.cluster.length === 1) {
activeKey.value = ['1'];
dynamicValidateForm.cluster[0].id = 1;
}
}
} }
}); });

View File

@ -21,7 +21,11 @@
message: '请输入名称', message: '请输入名称',
trigger: 'blur', trigger: 'blur',
}, },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
]" ]"
> >
<j-input <j-input

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<j-steps :current="stepCurrent"> <j-steps :current="stepCurrent">
<j-step v-for="item in steps" :key="item" :title="item" /> <j-step disabled v-for="item in steps" :key="item" :title="item" />
</j-steps> </j-steps>
<div class="steps-content"> <div class="steps-content">
<div class="steps-box" v-if="current === 0"> <div class="steps-box" v-if="current === 0">
@ -26,7 +26,11 @@
</PermissionButton> </PermissionButton>
</div> </div>
<j-scrollbar height="480"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0"> <j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="networkList.length > 0"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in networkList" v-for="item in networkList"
@ -40,6 +44,7 @@
description: item.description description: item.description
? item.description ? item.description
: descriptionList[provider.id], : descriptionList[provider.id],
type: 'network',
}" }"
> >
<template #other> <template #other>
@ -87,7 +92,11 @@
</AccessCard> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar> </j-scrollbar>
</div> </div>
<div class="steps-box" v-else-if="current === 1"> <div class="steps-box" v-else-if="current === 1">
@ -112,21 +121,29 @@
</PermissionButton> </PermissionButton>
</div> </div>
<j-scrollbar height="480"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
style="width: 100%"
v-if="procotolList.length > 0"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
:key="item?.id" :key="item?.id"
> >
<access-card <AccessCard
@checkedChange="procotolChange" @checkedChange="procotolChange"
:checked="procotolCurrent" :checked="procotolCurrent"
:data="item" :data="{ ...item, type: 'protocol' }"
> >
</access-card> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty
style="margin-top: 10%"
v-else
description="暂无数据"
/>
</j-scrollbar> </j-scrollbar>
</div> </div>
<div class="steps-box" v-else> <div class="steps-box" v-else>
@ -366,7 +383,11 @@ const { resetFields, validate, validateInfos } = useForm(
reactive({ reactive({
name: [ name: [
{ required: true, message: '请输入名称', trigger: 'blur' }, { required: true, message: '请输入名称', trigger: 'blur' },
{ max: 64, message: '最多可输入64个字符' }, {
max: 64,
message: '最多可输入64个字符',
trigger: 'blur',
},
], ],
description: [{ max: 200, message: '最多可输入200个字符' }], description: [{ max: 200, message: '最多可输入200个字符' }],
}), }),

View File

@ -186,6 +186,7 @@ const tableRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
let providersList = ref<Record<string, any>>([]); let providersList = ref<Record<string, any>>([]);
const providersOptions = ref<Record<string, any>>([]);
const statusMap = new Map(); const statusMap = new Map();
statusMap.set('enabled', 'success'); statusMap.set('enabled', 'success');
@ -207,13 +208,7 @@ const columns = [
key: 'provider', key: 'provider',
search: { search: {
type: 'select', type: 'select',
options: async () => { options: providersOptions,
const res: any = await getProviders();
return (res?.result || [])?.map((item: any) => ({
lable: item.name,
value: item.id,
}));
},
}, },
}, },
{ {
@ -315,6 +310,10 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
const getProvidersList = async () => { const getProvidersList = async () => {
const res: any = await getProviders(); const res: any = await getProviders();
providersList.value = res.result; providersList.value = res.result;
providersOptions.value = (res?.result || [])?.map((item: any) => ({
label: item.name,
value: item.id,
}));
}; };
getProvidersList(); getProvidersList();

View File

@ -54,12 +54,17 @@ const loading = ref(false);
const handleChange = (info: UploadChangeParam) => { const handleChange = (info: UploadChangeParam) => {
loading.value = true; loading.value = true;
if (info.file.status === 'done') { if (info.file.status === 'done') {
onlyMessage('上传成功!', 'success');
const result = info.file.response?.result; const result = info.file.response?.result;
const reg = new RegExp(/\.pem$/i);
if (reg.test(info.file.name)) {
keystoreBase64.value = result; keystoreBase64.value = result;
loading.value = false;
emit('change', result); emit('change', result);
emit('update:modelValue', result); emit('update:modelValue', result);
onlyMessage('上传成功!', 'success');
} else {
onlyMessage('请上传.pem格式的文件', 'error');
}
loading.value = false;
} }
}; };
const textChange = (val: any) => { const textChange = (val: any) => {

View File

@ -9,7 +9,7 @@
:model="formData" :model="formData"
name="basic" name="basic"
:label-col="{ span: 8 }" :label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }" :wrapper-col="{ span: 24 }"
autocomplete="off" autocomplete="off"
> >
<j-form-item <j-form-item
@ -86,7 +86,7 @@
<h1>2. 配置说明</h1> <h1>2. 配置说明</h1>
<h2>1证书文件</h2> <h2>1证书文件</h2>
<div> <div>
您可以使用文本编辑工具打开PEM或者CRT格式的证书文件复制其中的内容并粘贴到该文本框或者单击该文本框下的上传并选择存储在本地计算机的证书文件将文件内容上传到文本框 您可以使用文本编辑工具打开PEM格式的证书文件复制其中的内容并粘贴到该文本框或者单击该文本框下的上传并选择存储在本地计算机的证书文件将文件内容上传到文本框
</div> </div>
<h2>2证书私钥</h2> <h2>2证书私钥</h2>
<div> <div>
@ -208,31 +208,4 @@ detail(id);
} }
} }
} }
.doc {
height: 100%;
padding: 0 24px;
overflow-y: auto;
color: rgba(#000, 0.8);
font-size: 14px;
background-color: #fff;
h1 {
margin: 16px 0;
color: rgba(#000, 0.85);
font-weight: bold;
font-size: 14px;
&:first-child {
margin-top: 0;
}
}
h2 {
margin: 6px 10px;
color: rgba(0, 0, 0, 0.8);
font-weight: 400;
font-size: 14px;
}
}
</style> </style>

View File

@ -76,14 +76,17 @@ const columns = [
width: 200, width: 200,
ellipsis: true, ellipsis: true,
search: { search: {
type: 'select', type: 'string',
options: [
{
label: '证书标准',
value: 'common',
},
],
}, },
// search: {
// type: 'select',
// options: [
// {
// label: '',
// value: 'common',
// },
// ],
// },
scopedSlots: true, scopedSlots: true,
}, },
{ {
@ -118,17 +121,17 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
return []; return [];
} }
return [ return [
{ // {
key: 'view', // key: 'view',
text: '查看', // text: '',
tooltip: { // tooltip: {
title: '查看', // title: '',
}, // },
icon: 'EyeOutlined', // icon: 'EyeOutlined',
onClick: async () => { // onClick: async () => {
handlEye(data.id); // handlEye(data.id);
}, // },
}, // },
{ {
key: 'update', key: 'update',
text: '编辑', text: '编辑',
@ -143,6 +146,9 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
{ {
key: 'delete', key: 'delete',
text: '删除', text: '删除',
tooltip: {
title: '删除',
},
popConfirm: { popConfirm: {
title: '确认删除?', title: '确认删除?',
okText: ' 确定', okText: ' 确定',

View File

@ -91,17 +91,41 @@
> >
<j-collapse <j-collapse
v-model:activeKey="activeKey" v-model:activeKey="activeKey"
class="collapse" :class="[
!formData.shareCluster
? 'collapse'
: 'collapse-panel',
]"
:ghost="formData.shareCluster"
collapsible="header"
> >
<j-collapse-panel <j-collapse-panel
:key="cluster.id"
:show-arrow="!formData.shareCluster"
>
<!-- <j-collapse-panel
:key="cluster.id" :key="cluster.id"
:header=" :header="
cluster.serverId cluster.serverId
? cluster.serverId ? cluster.serverId
: `#${index + 1}.配置信息` : !formData.shareCluster
? `#${index + 1}.配置信息`
: ''
" "
collapsible="header" collapsible="header"
> :show-arrow="!formData.shareCluster"
> -->
<template #header v-if="!shareCluster">
<div class="collapse-header">
{{
cluster.serverId
? cluster.serverId
: !formData.shareCluster
? `#${index + 1}.配置信息`
: ''
}}
</div>
</template>
<template #extra v-if="!shareCluster"> <template #extra v-if="!shareCluster">
<j-popconfirm <j-popconfirm
@confirm.prevent=" @confirm.prevent="
@ -1112,6 +1136,10 @@ const filterConfigByType = (data: any, type: string) => {
}); });
}; };
const changeheader = (value: string) => {
console.log(22, value);
};
const getPortOptions = (portOptions: object, index = 0) => { const getPortOptions = (portOptions: object, index = 0) => {
if (!portOptions) return; if (!portOptions) return;
const type = formData.value.type; const type = formData.value.type;
@ -1369,7 +1397,20 @@ watch(
.collapse { .collapse {
margin-bottom: 20px; margin-bottom: 20px;
background: #f4f4f4; background: #f4f4f4;
:deep(.ant-collapse-header-text) {
flex: 1;
} }
}
.collapse-panel {
margin-bottom: 20px;
border: #d9d9d9 1px solid;
background: #f4f4f4;
border-radius: 2px;
:deep(.ant-collapse-header) {
padding: 0;
}
}
.delete-btn { .delete-btn {
display: inline-block; display: inline-block;
color: #e50012; color: #e50012;

View File

@ -20,8 +20,8 @@ export const Configuration = {
script: '', script: '',
size: '', size: '',
length: '4', length: '4',
offset: '0', offset: undefined,
little: 'false', little: undefined,
}, },
}; };
@ -103,11 +103,12 @@ export const isVisible = (
) => VisibleData[LastName].includes(dependencies); ) => VisibleData[LastName].includes(dependencies);
export const Validator = { export const Validator = {
regIp: new RegExp( regIpv4: new RegExp(
/((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/, /^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/,
), ),
regIPv6: new RegExp(/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/),
regDomain: new RegExp( regDomain: new RegExp(
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/, /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
), ),
regOnlyNumber: new RegExp(/^\d+$/), regOnlyNumber: new RegExp(/^\d+$/),
}; };
@ -159,7 +160,8 @@ export const Rules = {
message: '请输入公网地址', message: '请输入公网地址',
}, },
{ {
pattern: Validator.regIp || Validator.regDomain, pattern:
Validator.regIpv4 || Validator.regIPv6 || Validator.regDomain,
message: '请输入正确格式的域名或ip', message: '请输入正确格式的域名或ip',
}, },
], ],
@ -179,7 +181,9 @@ export const Rules = {
message: '请输入远程地址', message: '请输入远程地址',
}, },
{ {
pattern: Validator.regIp || Validator.regDomain, pattern:
Validator.regIpv4 || Validator.regIPv6 || Validator.regDomain,
message: '请输入正确格式的域名或ip', message: '请输入正确格式的域名或ip',
}, },
], ],

View File

@ -76,7 +76,10 @@
message: '请输入API Host', message: '请输入API Host',
}, },
{ {
pattern: regDomain, pattern:
Validator.regIpv4 ||
Validator.regIPv6 ||
Validator.regDomain,
message: '请输入正确的IP地址或者域名', message: '请输入正确的IP地址或者域名',
}, },
]" ]"
@ -130,7 +133,10 @@
message: '请输入RTP IP', message: '请输入RTP IP',
}, },
{ {
pattern: regDomain, pattern:
Validator.regIpv4 ||
Validator.regIPv6 ||
Validator.regDomain,
message: '请输入正确的IP地址或者域名', message: '请输入正确的IP地址或者域名',
}, },
]" ]"
@ -191,7 +197,15 @@
style="width: 100%" style="width: 100%"
:min="1" :min="1"
:max=" :max="
formData.configuration.dynamicRtpPortRange1 Number(
formData.configuration
.dynamicRtpPortRange1,
) < 65535
? Number(
formData.configuration
.dynamicRtpPortRange1,
)
: 65535
" "
:precision="0" :precision="0"
placeholder="起始端口" placeholder="起始端口"
@ -279,8 +293,17 @@ const formRef = ref<FormInstance>();
const loading = ref(false); const loading = ref(false);
const options = ref([]); const options = ref([]);
const checked = ref(false); const checked = ref(false);
const regDomain =
/[j-zA-Z0-9][-j-zA-Z0-9]{0,62}(\.[j-zA-Z0-9][-j-zA-Z0-9]{0,62})+\.?/; const Validator = {
regIpv4: new RegExp(
/^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/,
),
regIPv6: new RegExp(/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/),
regDomain: new RegExp(
/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
),
regOnlyNumber: new RegExp(/^\d+$/),
};
const formData = ref<FormDataType>({ const formData = ref<FormDataType>({
name: '', name: '',

View File

@ -38,7 +38,7 @@
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
@click="openRuleEditor" @click="openRuleEditor"
:statusNames="{ :statusNames="{
started: 'success', started: 'processing',
disable: 'error', disable: 'error',
}" }"
> >
@ -88,17 +88,17 @@
</CardBox> </CardBox>
</template> </template>
<template #state="slotProps"> <template #state="slotProps">
<j-badge <BadgeStatus
:text=" :text="
slotProps.state?.value === 'started' slotProps.state?.value === 'started'
? '正常' ? '正常'
: '禁用' : '禁用'
" "
:status=" :status="slotProps.state?.value"
slotProps.state?.value === 'started' :statusNames="{
? 'success' started: 'processing',
: 'error' disable: 'error',
" }"
/> />
</template> </template>
<template #action="slotProps"> <template #action="slotProps">

View File

@ -3,7 +3,7 @@
title='触发规则' title='触发规则'
visible visible
:width='820' :width='820'
@click='save' @ok='save'
@cancel='cancel' @cancel='cancel'
:maskClosable="false" :maskClosable="false"
> >

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type='simple' type='simple'
@search="handleSearch" @search="handleSearch"
class='search' class="scene-search"
target="scene-triggrt-device-device" target="scene-triggrt-device-device"
/> />
<j-divider style='margin: 0' /> <j-divider style='margin: 0' />

View File

@ -22,11 +22,16 @@
<j-form-item>定时调用所选功能</j-form-item> <j-form-item>定时调用所选功能</j-form-item>
</j-col> </j-col>
<j-col :span='24'> <j-col :span='24'>
<j-form-item
name='functionData'
:rules="rules"
>
<FunctionCall <FunctionCall
:value='_value' v-model:value='formModel.functionData'
:data='functionData' :data='functionData'
@change='callDataChange' @change='callDataChange'
/> />
</j-form-item>
</j-col> </j-col>
</j-row> </j-row>
</j-form> </j-form>
@ -66,9 +71,9 @@ const props = defineProps({
const emit = defineEmits<Emit>() const emit = defineEmits<Emit>()
const invokeForm = ref() const invokeForm = ref()
const formModel = reactive({ const formModel = reactive({
functionId: props.functionId functionId: props.functionId,
functionData: props.functionParameters
}) })
const _value = ref<any[]>(props.functionParameters)
/** /**
* 获取当前选择功能属性 * 获取当前选择功能属性
@ -94,13 +99,29 @@ const functionData = computed(() => {
return arrCache return arrCache
}) })
const rules = [{
validator(_: string, value: any) {
if (!value?.length && functionData.value.length) {
return Promise.reject('请输入功能值')
} else {
let hasValue = value.find((item: { name: string, value: any}) => !item.value)
if (hasValue) {
const functionItem = functionData.value.find((item: any) => item.id === hasValue.name)
return Promise.reject(functionItem?.name ? `请输入${functionItem?.name}` : '请输入功能值')
}
}
return Promise.resolve();
}
}]
const onSelect = (v: string, item: any) => { const onSelect = (v: string, item: any) => {
formModel.functionData = []
emit('update:action', `执行${item.name}`) emit('update:action', `执行${item.name}`)
emit('update:functionId', v) emit('update:functionId', v)
emit('update:functionParameters', [])
} }
const callDataChange = (v: any[]) => { const callDataChange = (v: any[]) => {
_value.value = v
emit('update:functionParameters', v) emit('update:functionParameters', v)
} }

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type='simple' type='simple'
@search="handleSearch" @search="handleSearch"
class='search' class="scene-search"
target="scene-triggrt-device-device" target="scene-triggrt-device-device"
/> />
<j-divider style='margin: 0' /> <j-divider style='margin: 0' />

View File

@ -1,5 +1,5 @@
<template> <template>
<div class='actions-branches-item'> <div class='manual actions-branches-item'>
<j-form-item <j-form-item
:rules="actionRules" :rules="actionRules"
:name="['branches', 0, 'then']" :name="['branches', 0, 'then']"
@ -32,6 +32,21 @@ const actionRules = [{
</script> </script>
<style scoped> <style scoped lang='less'>
@minWidth: 75%;
.manual {
&.actions-branches-item {
width: 100%;
}
}
@media (min-width: 1600px) {
.manual {
&.actions-branches-item {
width: @minWidth;
}
}
}
</style> </style>

View File

@ -3,7 +3,7 @@
title='触发规则' title='触发规则'
visible visible
:width='820' :width='820'
@click='save' @ok='save'
@cancel='cancel' @cancel='cancel'
> >
<Timer <Timer
@ -15,17 +15,16 @@
<script setup lang="ts" name="timerAddModel"> <script setup lang="ts" name="timerAddModel">
import Timer from '../components/Timer' import Timer from '../components/Timer'
import type { OperationTimer } from '@/views/rule-engine/Scene/typings' import type { OperationTimer } from '@/views/rule-engine/Scene/typings'
import {nextTick, PropType} from "vue"; import { PropType} from "vue";
import {TriggerDevice} from "@/views/rule-engine/Scene/typings";
import {handleTimerOptions} from "@/views/rule-engine/Scene/Save/components/Timer/util"; import {handleTimerOptions} from "@/views/rule-engine/Scene/Save/components/Timer/util";
type Emit = { type Emit = {
(e: 'cancel'): void (e: 'cancel'): void
(e: 'save', data: TriggerDevice, options: Record<string, any>): void (e: 'save', data: OperationTimer, options: Record<string, any>): void
} }
const props = defineProps({ const props = defineProps({
timer: { value: {
type: Object as PropType<OperationTimer>, type: Object as PropType<OperationTimer>,
default: () => ({}) default: () => ({})
} }
@ -39,14 +38,14 @@ interface AddModelType {
} }
const addModel = reactive<AddModelType>({ const addModel = reactive<AddModelType>({
timer: props.timer timer: props.value
}) })
const save = async () => { const save = async () => {
const timerData = await timerRef.value?.validateFields() const timerData = await timerRef.value?.validateFields()
if (timerData) { if (timerData) {
const options = handleTimerOptions(timerData) const options = handleTimerOptions(addModel.timer)
emit("save", timerData, options) emit("save", addModel.timer, options)
} }
} }
@ -54,9 +53,9 @@ const cancel = () => {
emit("cancel") emit("cancel")
} }
nextTick(() => { // watchEffect(() => {
Object.assign(addModel, props.timer) // addModel.timer = props.value
}) // })
</script> </script>

View File

@ -0,0 +1,58 @@
<template>
<div :class='["trigger-options-content", isAdd ? "is-add" : ""]'>
<span v-if='!isAdd'> 点击配置定时触发 </span>
<template v-else>
<div class='center-item'>
<AIcon v-if='options.selectorIcon' :type='options.selectorIcon' class='icon-padding-right' />
<span class='trigger-options-name'>
<Ellipsis style='max-width: 310px'>
{{ options.name }}
</Ellipsis>
</span>
<span v-if='options.extraName'>{{ options.extraName }}</span>
</div>
<div v-if='options.when'>
<span className='trigger-options-when'>{{ options.when }}</span>
</div>
<div v-if='options.time'>
<span className='trigger-options-time'>{{ options.time }}</span>
</div>
<div v-if='options.extraTime'>
<span className='trigger-options-extraTime'>{{ options.extraTime }}</span>
</div>
</template>
</div>
</template>
<script setup lang='ts' name='TimerTitle'>
const props = defineProps({
options: {
type: Object,
default: () => ({})
}
})
const isAdd = computed(() => {
console.log(props.options, Object.keys(props.options).length)
return !!Object.keys(props.options).length
})
</script>
<style scoped lang='less'>
.trigger-options-content {
display: inline-flex;
gap: 16px;
.center-item {
display: flex;
align-items: center;
}
.icon-padding-right {
padding-right: 4px;
}
}
</style>

View File

@ -2,7 +2,7 @@
<div class='timer'> <div class='timer'>
<j-form-item <j-form-item
:rules="rules" :rules="rules"
name="timer" :name="['trigger', 'timer']"
> >
<template #label> <template #label>
<TitleComponent data='触发规则' style='font-size: 14px;' /> <TitleComponent data='触发规则' style='font-size: 14px;' />
@ -29,8 +29,7 @@
v-if="visible" v-if="visible"
@cancel='visible = false' @cancel='visible = false'
@save="save" @save="save"
:value="data.trigger.device" :value="data.trigger.timer"
:options="data.options.trigger"
/> />
</div> </div>
</template> </template>
@ -41,6 +40,7 @@ import { storeToRefs } from 'pinia';
import Action from '../action/index.vue'; import Action from '../action/index.vue';
import AddModel from './AddModal.vue' import AddModel from './AddModal.vue'
import AddButton from '../components/AddButton.vue' import AddButton from '../components/AddButton.vue'
import Title from './Title.vue'
import type { OperationTimer, BranchesThen } from '@/views/rule-engine/Scene/typings' import type { OperationTimer, BranchesThen } from '@/views/rule-engine/Scene/typings'
const sceneStore = useSceneStore(); const sceneStore = useSceneStore();
@ -49,6 +49,7 @@ const visible = ref(false)
const rules = [{ const rules = [{
validator(_: any, v: any) { validator(_: any, v: any) {
console.log(v)
if (!v) { if (!v) {
return Promise.reject(new Error('请配置定时触发规则')); return Promise.reject(new Error('请配置定时触发规则'));
} }
@ -85,5 +86,21 @@ const save = (_data: OperationTimer, options: Record<string, any>) => {
} }
</script> </script>
<style scoped> <style scoped lang='less'>
@minWidth: 75%;
.timer {
.actions-branches-item {
width: 100%;
}
}
@media (min-width: 1600px) {
.timer {
.actions-branches-item {
width: @minWidth;
}
}
}
</style> </style>

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type="simple" type="simple"
@search="handleSearch" @search="handleSearch"
class="search" class="scene-search"
target="scene-trigger-device-product" target="scene-trigger-device-product"
/> />
<j-divider style="margin: 0" /> <j-divider style="margin: 0" />

View File

@ -22,6 +22,7 @@
showSearch showSearch
placeholder="请选择功能" placeholder="请选择功能"
v-model:value="modelRef.message.functionId" v-model:value="modelRef.message.functionId"
@select='functionSelect'
> >
<j-select-option <j-select-option
v-for="item in metadata?.functions || []" v-for="item in metadata?.functions || []"
@ -34,7 +35,7 @@
<j-form-item <j-form-item
v-if="modelRef.message.functionId" v-if="modelRef.message.functionId"
:name="['message', 'inputs']" :name="['message', 'inputs']"
:rules="[{ required: true, message: '请输入功能值' }]" :rules="functionRules"
> >
<EditTable <EditTable
:functions="functions" :functions="functions"
@ -138,6 +139,25 @@ const modelRef = reactive({
}, },
}); });
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<{ const metadata = ref<{
functions: any[]; functions: any[];
properties: any[]; properties: any[];

View File

@ -3,7 +3,7 @@
:columns="columns" :columns="columns"
type="simple" type="simple"
@search="handleSearch" @search="handleSearch"
class="search" class="scene-search"
target="scene-trigger-device-device" target="scene-trigger-device-device"
/> />
<j-divider style="margin: 0" /> <j-divider style="margin: 0" />

View File

@ -101,6 +101,7 @@ const valueChange = debounce(() => {
const _value = dataSource.value.map(item => ({ const _value = dataSource.value.map(item => ({
name: item.id, value: item.value name: item.id, value: item.value
})) }))
console.log(_value)
emit('change', _value) emit('change', _value)
emit('update:value', _value) emit('update:value', _value)
}, 500) }, 500)

View File

@ -17,6 +17,7 @@ import { numberToString } from './util'
type Emit = { type Emit = {
(e: 'update:value', data: Array<number>):void (e: 'update:value', data: Array<number>):void
(e: 'change', data: Array<number>):void
} }
const props = defineProps({ const props = defineProps({
@ -48,6 +49,7 @@ const change = (number: number) => {
} }
rowKeys.value = [..._keys.values()] rowKeys.value = [..._keys.values()]
emit('update:value', rowKeys.value) emit('update:value', rowKeys.value)
emit('change', rowKeys.value)
} }
const allActive = computed(() => { const allActive = computed(() => {
@ -57,7 +59,9 @@ const allActive = computed(() => {
watch(() => props.type, () => { watch(() => props.type, () => {
const isMonth = props.type === 'month' const isMonth = props.type === 'month'
const day = isMonth ? 31 : 7 const day = isMonth ? 31 : 7
if (!props.value.length) {
change(0) change(0)
}
timeOptions.value = new Array(day) timeOptions.value = new Array(day)
.fill(1) .fill(1)
.map((_, index) => { .map((_, index) => {

View File

@ -15,6 +15,7 @@
]' ]'
option-type='button' option-type='button'
button-style='solid' button-style='solid'
@change='updateValue'
/> />
</j-form-item> </j-form-item>
<j-form-item v-if='showCron' name='cron' :rules="[ <j-form-item v-if='showCron' name='cron' :rules="[
@ -32,11 +33,11 @@
} }
} }
]"> ]">
<j-input placeholder='corn表达式' v-model:value='formModel.cron' /> <j-input placeholder='corn表达式' v-model:value='formModel.cron' @change='updateValue' />
</j-form-item> </j-form-item>
<template v-else> <template v-else>
<j-form-item name='when'> <j-form-item name='when'>
<WhenOption v-model:value='formModel.when' :type='formModel.trigger' /> <WhenOption v-model:value='formModel.when' :type='formModel.trigger' @change='updateValue' />
</j-form-item> </j-form-item>
<j-form-item name='mod'> <j-form-item name='mod'>
<j-radio-group <j-radio-group
@ -47,13 +48,19 @@
]' ]'
option-type='button' option-type='button'
button-style='solid' button-style='solid'
@change='updateValue'
/> />
</j-form-item> </j-form-item>
</template> </template>
<j-space v-if='showOnce' style='display: flex;gap: 24px'> <j-space v-if='showOnce' style='display: flex;gap: 24px'>
<j-form-item :name="['once', 'time']"> <j-form-item :name="['once', 'time']">
<j-time-picker valueFormat='HH:mm:ss' v-model:value='formModel.once.time' style='width: 100%' <j-time-picker
format='HH:mm:ss' /> valueFormat='HH:mm:ss'
v-model:value='formModel.once.time'
style='width: 100%'
format='HH:mm:ss'
@change='updateValue'
/>
</j-form-item> </j-form-item>
<j-form-item> 执行一次</j-form-item> <j-form-item> 执行一次</j-form-item>
</j-space> </j-space>
@ -68,6 +75,7 @@
@change='(v) => { @change='(v) => {
formModel.period.from = v[0] formModel.period.from = v[0]
formModel.period.to = v[1] formModel.period.to = v[1]
updateValue()
}' }'
/> />
</j-form-item> </j-form-item>
@ -83,6 +91,7 @@
:min='1' :min='1'
:max='59' :max='59'
v-model:value='formModel.period.every' v-model:value='formModel.period.every'
@change='updateValue'
> >
<template #addonAfter> <template #addonAfter>
<j-select <j-select
@ -92,6 +101,7 @@
{ label: "分", value: "minutes" }, { label: "分", value: "minutes" },
{ label: "小时", value: "hours" }, { label: "小时", value: "hours" },
]' ]'
@select='updateValue'
/> />
</template> </template>
</j-input-number> </j-input-number>
@ -103,7 +113,7 @@
<script setup lang='ts' name='Timer'> <script setup lang='ts' name='Timer'>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import moment from 'moment' import dayjs from 'dayjs'
import WhenOption from './WhenOption.vue' import WhenOption from './WhenOption.vue'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import type { OperationTimer } from '../../../typings' import type { OperationTimer } from '../../../typings'
@ -131,23 +141,21 @@ const emit = defineEmits<Emit>()
const formModel = reactive<OperationTimer>({ const formModel = reactive<OperationTimer>({
trigger: 'week', trigger: 'week',
when: [], when: props.value.when || [],
mod: 'period', mod: 'period',
cron: undefined, cron: undefined,
once: { once: {
time: moment(new Date()).format('HH:mm:ss') time: dayjs(new Date()).format('HH:mm:ss')
}, },
period: { period: {
from: moment(new Date()).startOf('day').format('HH:mm:ss'), from: dayjs(new Date()).startOf('day').format('HH:mm:ss'),
to: moment(new Date()).endOf('day').format('HH:mm:ss'), to: dayjs(new Date()).endOf('day').format('HH:mm:ss'),
every: 1, every: 1,
unit: 'seconds' unit: 'seconds'
} }
}) })
const timerForm = ref() const timerForm = ref()
Object.assign(formModel, props.value)
const showCron = computed(() => { const showCron = computed(() => {
return formModel.trigger === 'cron' return formModel.trigger === 'cron'
}) })
@ -160,7 +168,7 @@ const showPeriod = computed(() => {
return formModel.trigger !== 'cron' && formModel.mod === 'period' return formModel.trigger !== 'cron' && formModel.mod === 'period'
}) })
watch(() => formModel, () => { const updateValue = () => {
const cloneValue = cloneDeep(formModel) const cloneValue = cloneDeep(formModel)
if (cloneValue.trigger === 'cron') { if (cloneValue.trigger === 'cron') {
delete cloneValue.when delete cloneValue.when
@ -174,7 +182,7 @@ watch(() => formModel, () => {
delete cloneValue.period delete cloneValue.period
} }
emit('update:value', cloneValue) emit('update:value', cloneValue)
}, { deep: true }) }
defineExpose({ defineExpose({
validateFields: () => new Promise(async (resolve) => { validateFields: () => new Promise(async (resolve) => {
@ -182,6 +190,8 @@ defineExpose({
resolve(data) resolve(data)
}) })
}) })
Object.assign(formModel, props.value)
formModel.when = props.value.when || []
</script> </script>

View File

@ -57,15 +57,17 @@ const classNames = computed(() => {
}) })
const handleClick = (type: string) => { const handleClick = (type: string) => {
if (!props.disabled) {
emit('update:modelValue', type) emit('update:modelValue', type)
} }
}
</script> </script>
<style scoped lang='less'> <style scoped lang='less'>
// @import 'ant-design-vue/es/style/themes/default.less';
.scene-trigger-way-warp {display: flex; .scene-trigger-way-warp {
display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 16px 24px; gap: 16px 24px;
width: 100%; width: 100%;
@ -115,5 +117,16 @@ const handleClick = (type: string) => {
} }
} }
} }
&.disabled {
color: rgba(#000, .8);
.way-item-image {
opacity: 0.6;
}
.trigger-way-item {
cursor: not-allowed;
}
}
} }
</style> </style>

View File

@ -115,3 +115,6 @@ onUnmounted(() => {
} }
} }
</style> </style>
<style lang='less'>
@import "./style.less";
</style>

View File

@ -0,0 +1,3 @@
.scene-search {
padding: 24px 0 0 0;
}

View File

@ -300,7 +300,7 @@ const getActions = (
]; ];
if (data.triggerType === 'manual') { if (data.triggerType === 'manual') {
const _item: ActionsType = { const _item: ActionsType = {
key: 'trigger', key: 'tigger',
text: '手动触发', text: '手动触发',
disabled: data.state?.value === 'disable', disabled: data.state?.value === 'disable',
tooltip: { tooltip: {

View File

@ -70,7 +70,7 @@
:status="slotProps.state?.value" :status="slotProps.state?.value"
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
:statusNames="{ :statusNames="{
online: 'success', online: 'processing',
offline: 'error', offline: 'error',
notActive: 'warning', notActive: 'warning',
}" }"
@ -147,7 +147,7 @@
:status="slotProps.state.value" :status="slotProps.state.value"
:text="slotProps.state.text" :text="slotProps.state.text"
:statusNames="{ :statusNames="{
online: 'success', online: 'processing',
offline: 'error', offline: 'error',
notActive: 'warning', notActive: 'warning',
}" }"
@ -247,6 +247,9 @@ const columns = [
search: { search: {
rename: 'productId$product-info', rename: 'productId$product-info',
type: 'select', type: 'select',
handleValue(value: string) {
return `id is ${value}`
},
options: () => options: () =>
new Promise((resolve) => { new Promise((resolve) => {
const params = { const params = {
@ -288,14 +291,9 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: [ options: [
{ { label: '禁用', value: 'notActive' },
label: '正常', { label: '离线', value: 'offline' },
value: 1, { label: '在线', value: 'online' },
},
{
label: '禁用',
value: 0,
},
], ],
}, },
scopedSlots: true, scopedSlots: true,

View File

@ -68,9 +68,8 @@
:status="slotProps.state?.value" :status="slotProps.state?.value"
:statusText="slotProps.state?.text" :statusText="slotProps.state?.text"
:statusNames="{ :statusNames="{
online: 'success', 1: 'processing',
offline: 'error', 0: 'error',
notActive: 'warning',
}" }"
> >
<template #img> <template #img>
@ -172,9 +171,8 @@
:status="slotProps.state.value" :status="slotProps.state.value"
:text="slotProps.state.text" :text="slotProps.state.text"
:statusNames="{ :statusNames="{
online: 'success', 1: 'processing',
offline: 'error', 0: 'error',
notActive: 'warning',
}" }"
></BadgeStatus> ></BadgeStatus>
</template> </template>

View File

@ -4,7 +4,7 @@
<pro-search <pro-search
:columns="columns" :columns="columns"
target="category" target="category"
@search="(params:any)=>queryParams = {...params}" @search="handleParams"
/> />
<j-pro-table <j-pro-table
@ -262,6 +262,23 @@ type dictType = {
name: string; name: string;
}; };
type modalType = '' | 'add' | 'edit' | 'reset'; type modalType = '' | 'add' | 'edit' | 'reset';
const handleParams = (params: any)=> {
const newParams = (params?.terms as any[])?.map(item1 => {
item1.terms = item1.terms.map((item2: any) => {
if (['telephone', 'email'].includes(item2.column)) {
return {
column: 'id$user-detail',
value: [item2]
}
}
return item2
})
return item1
})
queryParams.value = { terms: newParams || [] }
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -92,8 +92,8 @@ export default defineConfig(({ mode}) => {
[env.VITE_APP_BASE_API]: { [env.VITE_APP_BASE_API]: {
// target: 'http://192.168.33.22:8800', // target: 'http://192.168.33.22:8800',
// target: 'http://192.168.32.244:8881', // target: 'http://192.168.32.244:8881',
// target: 'http://120.77.179.54:8844', // 120测试 target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境 // target: 'http://192.168.33.46:8844', // 本地开发环境
ws: 'ws://192.168.33.46:8844', ws: 'ws://192.168.33.46:8844',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') rewrite: (path) => path.replace(/^\/api/, '')

View File

@ -3700,8 +3700,8 @@ jetlinks-store@^0.0.3:
jetlinks-ui-components@^1.0.5: jetlinks-ui-components@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#682711e0f69c141fff2c256db61a060c82539611" resolved "http://47.108.170.157:9013/jetlinks-ui-components/-/jetlinks-ui-components-1.0.5.tgz#77bd58156212d793fdebe41fc8864eeed5db7182"
integrity sha512-rQxD/YlE+XSAG7BWIcFTtKrCQJXk5o+TUgejyuUT/baBThJB6xYt1k2dQEdXyiwpukYen5FzaoLpelSD9SUegw== integrity sha512-VXuCMJlMV/bbmBhPtBzY/BUBIvGebQbguLPE06xps79i5Pjq46+HBW8VPsJ9MwvyMhYkhzPlQJu9Ft7v5uo+yA==
dependencies: dependencies:
"@vueuse/core" "^9.12.0" "@vueuse/core" "^9.12.0"
ant-design-vue "^3.2.15" ant-design-vue "^3.2.15"