fix: 修改ui

This commit is contained in:
100011797 2023-07-04 18:25:04 +08:00
parent 0d6e9d8ae8
commit f295104d8e
32 changed files with 858 additions and 677 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 776 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,9 +0,0 @@
<template>
123
</template>
<script lang="ts" setup>
const props = defineProps({
// options:
})
</script>

View File

@ -150,10 +150,11 @@ const read = (type: '_read' | '_unread') => {
.list-item-right {
width: 100px;
padding: 12px 12px 12px 0;
padding: 6px 12px 6px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
</style>

View File

@ -75,6 +75,10 @@ const onLeft = () => {
font-size: 12px;
color: #666666;
cursor: pointer;
&:hover {
background-color: #EFF2FE;
color: @primary-color;
}
}
}
}

View File

@ -1,14 +1,46 @@
<template>
<j-modal visible title="查看详情" @cancel="emit('close')">
<j-descriptions :column="1">
<j-descriptions-item label="用户名">{{ userInfos?.username }}</j-descriptions-item>
<j-descriptions-item label="账号ID">{{ userInfos?.id }}</j-descriptions-item>
<j-descriptions-item label="姓名">{{ userInfos.name }}</j-descriptions-item>
<j-descriptions-item label="角色">{{ role }}</j-descriptions-item>
<j-descriptions-item label="组织">{{ org }}</j-descriptions-item>
<j-descriptions-item label="手机号">{{ userInfos?.telephone || '--' }}</j-descriptions-item>
<j-descriptions-item label="邮箱">{{ userInfos?.email || '--' }}</j-descriptions-item>
</j-descriptions>
<j-modal visible @cancel="emit('close')" :closable="false">
<div style="padding: 30px;">
<div style="display: flex; margin-bottom: 32px">
<j-avatar :size="100" :src="userInfos.avatar"></j-avatar>
<div style="margin-left: 24px;">
<div class="name">{{ userInfos.name }}</div>
<div class="subTitle">用户名: {{ userInfos?.username }}</div>
<div class="subTitle">账号ID: {{ userInfos?.id }}</div>
</div>
</div>
<j-descriptions
:column="1"
:labelStyle="{
color: 'rgba(0, 0, 0, 0.6)',
width: '70px',
}"
:contentStyle="{
color: '#333333',
}"
>
<j-descriptions-item label="角色">
<j-ellipsis :lineClamp="2">
{{ role }}
</j-ellipsis>
</j-descriptions-item>
<j-descriptions-item label="组织">
<j-ellipsis :lineClamp="2">
{{ org }}
</j-ellipsis>
</j-descriptions-item>
<j-descriptions-item label="手机号">
<j-ellipsis :lineClamp="2">
{{ userInfos?.telephone }}
</j-ellipsis>
</j-descriptions-item>
<j-descriptions-item label="邮箱">
<j-ellipsis :lineClamp="2">
{{ userInfos?.email }}
</j-ellipsis>
</j-descriptions-item>
</j-descriptions>
</div>
<template #footer>
<j-button type="primary" @click="emit('close')">关闭</j-button>
</template>
@ -22,12 +54,25 @@ const { userInfos } = useUserInfo();
const emit = defineEmits(['close', 'save']);
const role = computed(() => {
const _role = userInfos?.roleList.map((item: any) => item?.name).join(',')
return _role || '暂无角色'
})
const _role = userInfos?.roleList.map((item: any) => item?.name).join(';');
return _role || '暂无角色';
});
const org = computed(() => {
const _role = userInfos?.orgList.map((item: any) => item?.name).join(',')
return _role || '暂无组织'
})
</script>
const _role = userInfos?.orgList.map((item: any) => item?.name).join(';');
return _role || '暂无组织';
});
</script>
<style lang="less" scoped>
.name {
color: #1D2129;
font-weight: 500;
font-size: 26px;
}
.subTitle {
color: rgba(0, 0, 0, 0.6);
margin-top: 5px;
}
</style>

View File

@ -122,7 +122,7 @@ const handleOk = () => {
.then((resp) => {
if (resp.status === 200) {
onlyMessage('保存成功', 'success');
emits('save');
emits('save', form.value);
}
})
.finally(() => (loading.value = false));

View File

@ -1,141 +0,0 @@
<template>
<j-modal
visible
title="重置密码"
@ok="handleOk"
width="520px"
:confirmLoading="loading"
@cancel="emits('close')"
>
<j-form :model="form" layout="vertical" ref="formRef">
<j-form-item
label="旧密码"
name="oldPassword"
:rules="[
{ required: true, message: '请输入密码' },
{ validator: checkMethods.old, trigger: 'blur' },
]"
>
<j-input
v-model:value="form.oldPassword"
placeholder="请输入旧密码"
/>
</j-form-item>
<j-form-item
label="密码"
name="newPassword"
:rules="[
{ required: true, message: '请输入密码' },
{ validator: checkMethods.new, trigger: 'blur' },
]"
>
<j-input-password
v-model:value="form.newPassword"
placeholder="请输入密码"
/>
</j-form-item>
<j-form-item
label="确认密码"
name="confirmPassword"
:rules="[
{ required: true, message: '请输入确认密码' },
{ validator: checkMethods.confirm, trigger: 'blur' },
]"
>
<j-input-password
v-model:value="form.confirmPassword"
placeholder="请再次输入密码"
/>
</j-form-item>
</j-form>
</j-modal>
</template>
<script setup lang="ts">
import {
updateMepsd_api,
checkOldPassword_api,
validateField_api,
} from '@/api/account/center';
import { onlyMessage } from '@/utils/comm';
type formType = {
oldPassword: string;
newPassword: string;
confirmPassword: string;
};
const emits = defineEmits(['save', 'close']);
const loading = ref(false);
const formRef = ref<any>();
const form = ref<formType>({
oldPassword: '',
newPassword: '',
confirmPassword: '',
});
const checkMethods = {
old: async (_rule: any, value: string) => {
if (!value) return Promise.reject('请输入密码');
try {
const resp: any = await checkOldPassword_api(value);
if (resp.status === 200 && !resp.result.passed)
return Promise.reject(resp.result.reason);
else return Promise.resolve();
} catch (error) {
return Promise.reject('验证失败');
}
},
new: async (_rule: any, value: string) => {
if (!value) return Promise.reject('请输入密码');
else if (
form.value.confirmPassword &&
value !== form.value.confirmPassword
)
return Promise.reject('两次密码输入不一致');
try {
const resp: any = await validateField_api('password', value);
if (resp.status === 200 && !resp.result.passed)
return Promise.reject(resp.result.reason);
else return Promise.resolve();
} catch (error) {
return Promise.reject('验证失败');
}
},
confirm: async (_rule: any, value: string) => {
if (!value) return Promise.reject();
else if (form.value.newPassword && value !== form.value.newPassword) {
formRef.value?.validate('newPassword');
}
try {
const resp: any = await validateField_api('password', value);
if (resp.status === 200 && !resp.result.passed)
return Promise.reject(resp.result.reason);
else return Promise.resolve();
} catch (error) {
return Promise.reject('验证失败');
}
},
};
const handleOk = () => {
formRef.value?.validate().then(() => {
loading.value = true;
const params = {
oldPassword: form.value.oldPassword,
newPassword: form.value.newPassword,
};
updateMepsd_api(params)
.then((resp) => {
if (resp.status === 200) {
onlyMessage('保存成功', 'success');
emits('save');
}
})
.finally(() => (loading.value = false));
});
};
</script>
<style scoped></style>

View File

@ -2,66 +2,89 @@
<j-modal
visible
title="重置密码"
width="520px"
width="615px"
:bodyStyle="{
padding: 0,
}"
:confirmLoading="loading"
@cancel="emits('close')"
>
<j-steps :current="current" size="small" progress-dot @change="onChange">
<j-step title="验证密码" />
<j-step title="设置密码" />
<j-step title="二次确认" />
</j-steps>
<div class="content">
<j-form :model="form" layout="vertical" ref="formRef">
<j-form-item
label="请输入当前密码"
name="oldPassword"
v-show="current === 0"
:rules="[
{ required: true, message: '请输入当前密码' },
{ validator: checkMethods.old, trigger: 'blur' },
]"
<div>
<div style="background-color: #f8f9fc; padding: 24px">
<j-steps
:current="current"
size="small"
progress-dot
@change="onChange"
>
<j-input
v-model:value="form.oldPassword"
placeholder="请输入当前密码"
/>
</j-form-item>
<j-form-item
label="请输入新密码"
name="newPassword"
v-show="current === 1"
:rules="[
{ required: true, message: '请输入新密码' },
{ validator: checkMethods.new, trigger: 'blur' },
]"
>
<j-input-password
v-model:value="form.newPassword"
placeholder="请输入新密码"
/>
</j-form-item>
<j-form-item
label="请确认新密码"
v-show="current === 2"
name="confirmPassword"
:rules="[
{ required: true, message: '请确认新密码' },
{ validator: checkMethods.confirm, trigger: 'blur' },
]"
>
<j-input-password
v-model:value="form.confirmPassword"
placeholder="请确认新密码"
/>
</j-form-item>
</j-form>
<j-step :title="item" v-for="(item, index) in list" :key="item">
<template #description>
<span v-if="current === index">进行中</span>
<span v-if="current < index">未开始</span>
<span v-if="current > index">已完成</span>
</template>
</j-step>
</j-steps>
</div>
<div class="content">
<j-form :model="form" layout="vertical" ref="formRef">
<j-form-item
label="当前密码"
name="oldPassword"
v-show="current === 0"
:rules="[
{ required: true, message: '请输入当前密码' },
{ validator: checkMethods.old, trigger: 'blur' },
]"
>
<j-input
v-model:value="form.oldPassword"
placeholder="请输入当前密码"
/>
</j-form-item>
<j-form-item
label="新密码"
name="newPassword"
v-show="current === 1"
:rules="[
{ required: true, message: '请输入新密码' },
{ validator: checkMethods.new, trigger: 'blur' },
]"
>
<j-input-password
v-model:value="form.newPassword"
placeholder="请输入新密码"
/>
</j-form-item>
<j-form-item
label="确认新密码"
v-show="current === 2"
name="confirmPassword"
:rules="[
{ required: true, message: '请确认新密码' },
{
validator: checkMethods.confirm,
trigger: 'blur',
},
]"
>
<j-input
v-model:value="form.confirmPassword"
placeholder="请确认新密码"
/>
</j-form-item>
</j-form>
</div>
</div>
<template #footer>
<j-button v-if="current === 0" @click="emits('close')">取消</j-button>
<j-button v-if="current === 0" @click="emits('close')"
>取消</j-button
>
<j-button v-if="current === 2" @click="onPrev">上一步</j-button>
<j-button type="primary" v-else @click="onNext">下一步</j-button>
<j-button v-if="current === 2" type="primary" @click="handleOk">完成</j-button>
<j-button v-if="current === 2" type="primary" @click="handleOk"
>完成</j-button
>
</template>
</j-modal>
</template>
@ -82,6 +105,8 @@ type formType = {
const emits = defineEmits(['save', 'close']);
const list = ['验证密码', '设置密码', '二次确认']
const loading = ref(false);
const formRef = ref<any>();
@ -93,28 +118,28 @@ const form = ref<formType>({
const current = ref<number>(0);
const onPrev = () => {
current.value -= 1
}
current.value -= 1;
};
const jumpStep = (val: number) => {
if(val === 1) {
if (val === 1) {
formRef.value?.validate('oldPassword').then(() => {
current.value += 1
})
} else if(val === 2) {
current.value += 1;
});
} else if (val === 2) {
formRef.value?.validate('newPassword').then(() => {
current.value += 1
})
current.value += 1;
});
}
}
};
const onNext = () => {
jumpStep(current.value + 1)
}
jumpStep(current.value + 1);
};
const onChange = (cur: number) => {
jumpStep(cur)
}
jumpStep(cur);
};
const checkMethods = {
old: async (_rule: any, value: string) => {
@ -141,10 +166,7 @@ const checkMethods = {
},
confirm: async (_rule: any, value: string) => {
if (!value) return Promise.resolve();
else if (
form.value.newPassword &&
value !== form.value.newPassword
) {
else if (form.value.newPassword && value !== form.value.newPassword) {
return Promise.reject('两次密码输入不一致');
}
try {
@ -177,8 +199,8 @@ const handleOk = () => {
};
</script>
<style scoped>
<style scoped lang="less">
.content {
padding: 20px 50px;
padding: 30px 50px;
}
</style>

View File

@ -32,6 +32,7 @@
<script lang="ts" setup>
import { getMe_api, getView_api, setView_api } from '@/api/home';
import { getImage, onlyMessage } from '@/utils/comm';
const currentView = ref<string>('');
const isApiUser = ref<boolean>();
@ -93,6 +94,11 @@ onMounted(() => {
display: flex;
justify-content: flex-end;
margin-top: 68px;
button {
background-color: @primary-2;
color: @primary-color-hover;
}
}
}
</style>

View File

@ -2,75 +2,72 @@
<j-modal
visible
title="详情"
width="1000px"
@ok="emits('update:visible', false)"
width="754px"
@cancel="emits('update:visible', false)"
class="view-dialog-container"
>
<template v-if="type === 'alarm'">
<j-row v-if="data?.topicProvider === 'alarm-device'">
<j-col :span="4" class="label">告警设备</j-col>
<j-col :span="8" class="value">
{{ data?.targetName || '' }}
</j-col>
<j-col :span="4" class="label">设备ID</j-col>
<j-col :span="8" class="value">
{{ data?.targetId || '' }}
</j-col>
</j-row>
<j-row>
<j-col :span="4" class="label">告警名称</j-col>
<j-col :span="8" class="value">
{{ data?.alarmName || data?.alarmConfigName || '' }}
</j-col>
<j-col :span="4" class="label">告警时间</j-col>
<j-col :span="8" class="value">
{{ dayjs(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss') }}
</j-col>
<j-col :span="4" class="label">告警级别</j-col>
<j-col :span="8" class="value">
{{
(levelList.length > 0 && getLevelLabel(data.level)) ||
''
}}
</j-col>
<j-col :span="4" class="label">告警说明</j-col>
<j-col :span="8" class="value">{{
data?.description || ''
}}</j-col>
<j-col
:span="4"
class="label"
style="display: flex; height: 440px; align-items: center"
>告警流水</j-col
>
<j-col
:span="20"
class="value"
style="max-height: 440px; overflow: auto"
>
<JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" />
</j-col>
</j-row>
<j-descriptions
:column="2"
:contentStyle="{
color: '#333333',
}"
:labelStyle="{
color: 'rgba(0, 0, 0, 0.6)',
width: '72px',
}"
>
<template v-if="data?.topicProvider === 'alarm-device'">
<j-descriptions-item label="告警设备">
<j-ellipsis>{{ data?.targetName || ''}}</j-ellipsis>
</j-descriptions-item>
<j-descriptions-item label="设备ID">
<j-ellipsis>
{{ data?.targetId || '' }}
</j-ellipsis>
</j-descriptions-item>
</template>
<j-descriptions-item label="告警名称">
<j-ellipsis>
{{ data?.alarmName || data?.alarmConfigName || '' }}
</j-ellipsis>
</j-descriptions-item>
<j-descriptions-item label="告警时间">{{
dayjs(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss')
}}</j-descriptions-item>
<j-descriptions-item label="告警级别">{{
(levelList.length > 0 && getLevelLabel(data.level)) || ''
}}</j-descriptions-item>
<j-descriptions-item label="告警说明">
<j-ellipsis>
{{ data?.description || '' }}
</j-ellipsis>
</j-descriptions-item>
</j-descriptions>
<div>
<div class="label">告警流水:</div>
<div style="padding: 10px; background-color: #fafafa">
<j-scrollbar height="200px">
<JsonViewer
style="background-color: #fafafa"
:value="JSON.parse(data?.alarmInfo || '{}')"
/>
</j-scrollbar>
</div>
</div>
</template>
<template v-else>
<j-row>
<j-col
:span="4"
class="label"
style="display: flex; height: 440px; align-items: center"
>通知流水</j-col
>
<j-col
:span="20"
class="value"
style="max-height: 440px; overflow: auto"
>
<JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" />
</j-col>
</j-row>
<div>
<div class="label">通知流水:</div>
<div style="padding: 10px; background-color: #fafafa">
<j-scrollbar height="200px">
<JsonViewer :value="JSON.parse(data?.alarmInfo || '{}')" />
</j-scrollbar>
</div>
</div>
</template>
<template #footer>
<j-button type="primary" @click="emits('update:visible', false)">确定</j-button>
</template>
</j-modal>
</template>
@ -110,14 +107,14 @@ const getLevelLabel = (id: number) => {
<style lang="less" scoped>
.view-dialog-container {
.ant-row {
.ant-col {
padding: 16px 24px;
border-right: 1px solid #f0f0f0;
}
.label {
background-color: #fafafa;
}
.label {
width: 100%;
color: rgba(0, 0, 0, 0.6);
margin-bottom: 8px;
}
.value {
color: #333333;
}
}
</style>

View File

@ -0,0 +1,199 @@
<template>
<j-modal :width="'900px'" visible @cancel="emit('close')">
<template v-if="getType === 'notifier-dingTalk'">
<j-spin :spinning="loading">
<div class="code" style="height: 600px;">
<iframe
id="notifier_iframe"
class="code-item"
width="100%"
height="100%"
:src="url"
v-if="!loading"
></iframe>
</div>
</j-spin>
</template>
<template v-else-if="getType === 'notifier-weixin'">
<j-spin :spinning="loading">
<div class="code" style="height: 450px">
<iframe
id="notifier_iframe"
class="code-item"
width="100%"
height="100%"
:src="url"
v-if="!loading"
></iframe>
</div>
</j-spin>
</template>
<template #footer>
<j-button @click="emit('close')">关闭</j-button>
<!-- <j-button type="primary" @click="emit('close')">确定</j-button> -->
</template>
</j-modal>
</template>
<script lang="ts" setup>
import {
bindThirdParty,
getDingTalkOAuth2,
getUserBind,
getWechatOAuth2,
} from '@/api/account/notificationSubscription';
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: {
//
type: Object,
default: () => {},
},
current: {
//
type: Object,
default: () => {},
},
});
const url = ref<string>('');
const loading = ref<boolean>(false);
const getType = computed(() => {
return props.current?.channelProvider;
});
const onBindHandle = (
type: 'notifier-weixin' | 'notifier-dingTalk',
code: string,
) => {
getUserBind(type === 'notifier-dingTalk' ? 'dingtalk' : 'wechat', {
authCode: code,
configId: props.current?.channelConfiguration.notifierId,
})
.then((resp) => {
if (resp.status === 200) {
const _bindCode = (resp?.result || '') as string;
if (_bindCode) {
bindThirdParty(
type,
props.current?.channelConfiguration.notifierId,
_bindCode,
)
.then((response) => {
if (response.status === 200) {
//
emit('save', props.current);
}
})
.finally(() => {
loading.value = false;
});
}
}
})
.catch(() => {
loading.value = false;
});
};
const updateIframeStyle = () => {
const iframe = document.querySelector(
'#notifier_iframe',
) as HTMLIFrameElement;
iframe.onload = () => {
const currentUrl = iframe?.contentWindow?.location?.search || '';
let authCode = '';
if (currentUrl.startsWith('?')) {
currentUrl
.substring(1)
.split('&')
.map((item) => {
if (
props.current?.channelProvider === 'notifier-dingTalk'
) {
if (item.split('=')?.[0] === 'authCode') {
authCode = item.split('=')?.[1] || '';
}
} else {
if (item.split('=')?.[0] === 'code') {
authCode = item.split('=')?.[1] || '';
}
}
});
}
if (authCode) {
loading.value = true;
onBindHandle(props.current?.channelProvider, authCode);
}
};
};
const handleSearch = async () => {
if (props.current?.channelProvider === 'notifier-weixin') {
loading.value = true;
const resp = await getWechatOAuth2(
props.current?.channelConfiguration.notifierId,
props.current?.channelConfiguration.templateId,
location.href
).finally(() => {
loading.value = false;
});
if (resp.status === 200) {
url.value = resp.result as string;
nextTick(() => {
updateIframeStyle();
});
}
}
if (props.current?.channelProvider === 'notifier-dingTalk') {
loading.value = true;
const resp = await getDingTalkOAuth2(
props.current?.channelConfiguration.notifierId,
location.href,
).finally(() => {
loading.value = false;
});
if (resp.status === 200) {
url.value = resp.result as string;
nextTick(() => {
updateIframeStyle();
});
}
}
};
watch(
() => props.current,
() => {
handleSearch();
},
{
immediate: true,
deep: true,
},
);
</script>
<style lang="less" scoped>
.tip {
width: 100%;
margin: 80px 0;
text-align: center;
font-size: 14px;
color: #7f7f7f;
}
.code {
width: 100%;
display: flex;
margin-top: 30px;
justify-content: center;
.code-item {
border: none;
}
}
</style>

View File

@ -1,10 +1,10 @@
<template>
<j-modal visible @cancel="emit('close')">
<j-modal width="350px" visible @cancel="emit('close')" :footer="null">
<template v-if="getType === 'notifier-dingTalk'">
<!-- <div class="tip">请先绑定钉钉账号</div> -->
<div class="tip">暂未开发</div>
</template>
<template v-else-if="getType === 'notifier-weixin'">
<!-- <div class="tip">请先绑定企业微信账号</div> -->
<div class="tip">暂未开发</div>
</template>
<template v-else-if="getType === 'notifier-email'">
<div class="tip"> 绑定账号{{ user.userInfos?.email }}</div>
@ -12,7 +12,7 @@
<template v-else>
<div class="tip">绑定账号{{ user.userInfos?.telephone }}</div>
</template>
<template #footer>
<div class="btn">
<j-button @click="emit('unsubscribe', current)">取消订阅</j-button>
<j-button
@click="onBind"
@ -27,7 +27,7 @@
>更换绑定账号</j-button
>
<j-button v-else @click="emit('close')">确定</j-button>
</template>
</div>
</j-modal>
<EditInfo
v-if="editInfoVisible"
@ -102,19 +102,12 @@ watch(
<style lang="less" scoped>
.tip {
width: 100%;
margin-top: 30px;
// font-size: 14px;
// color: #7f7f7f;
margin: 30px 0;
}
.code {
width: 100%;
.btn {
display: flex;
margin-top: 30px;
justify-content: center;
.code-item {
border: none;
}
gap: 12px;
justify-content: flex-end;
}
</style>

View File

@ -7,7 +7,7 @@
<div class="child-item-left-auth" v-if="data?.description">
<j-tooltip :title="data.description">
<AIcon
style="font-size: 16px"
style="font-size: 16px; color: rgba(0, 0, 0, .3)"
type="ExclamationCircleOutlined"
/>
</j-tooltip>
@ -86,7 +86,9 @@
</j-dropdown>
</div>
<div class="box-item-text">
{{ slotProps?.name }}
<j-ellipsis style="width: 50px;">
{{ slotProps?.name }}
</j-ellipsis>
</div>
</div>
</template>
@ -245,7 +247,7 @@ const onSave = (dt: any) => {
div {
display: flex;
margin-right: 30px;
margin-right: 8px;
flex-direction: column;
justify-content: center;
align-items: center;
@ -260,7 +262,7 @@ const onSave = (dt: any) => {
display: flex;
.box-item {
margin-left: 10px;
// margin: 0 5px;
.box-item-img {
width: 50px;
display: flex;

View File

@ -1,34 +1,10 @@
<template>
<j-modal width="900px" visible @cancel="emit('close')">
<j-modal :width="'384px'" visible @cancel="emit('close')" :footer="null">
<template v-if="getType === 'notifier-dingTalk'">
<!-- <div class="tip">请先绑定钉钉账号</div> -->
<j-spin :spinning="loading">
<div class="code" style="height: 600px;">
<iframe
id="notifier_iframe"
class="code-item"
width="100%"
height="100%"
:src="url"
v-if="!loading"
></iframe>
</div>
</j-spin>
<div class="tip">请先绑定钉钉账号</div>
</template>
<template v-else-if="getType === 'notifier-weixin'">
<!-- <div class="tip">请先绑定企业微信账号</div> -->
<j-spin :spinning="loading">
<div class="code" style="height: 450px">
<iframe
id="notifier_iframe"
class="code-item"
width="100%"
height="100%"
:src="url"
v-if="!loading"
></iframe>
</div>
</j-spin>
<div class="tip">请先绑定企业微信账号</div>
</template>
<template v-else-if="getType === 'notifier-email'">
<div class="tip">请先绑定邮箱</div>
@ -36,43 +12,37 @@
<template v-else>
<div class="tip">请先绑定手机号</div>
</template>
<template #footer>
<j-button @click="emit('close')">取消</j-button>
<j-button
@click="onBind"
type="primary"
v-if="
[
'notifier-email',
'notifier-voice',
'notifier-sms',
].includes(getType)
"
>立即绑定</j-button
>
<j-button v-else @click="emit('close')">确定</j-button>
</template>
<div class="btn">
<j-space>
<j-button @click="emit('close')">取消</j-button>
<j-button @click="onBind" type="primary">立即绑定</j-button>
</j-space>
</div>
<EditInfo
v-if="editInfoVisible"
:data="user.userInfos"
@close="editInfoVisible = false"
@save="onSave"
/>
<Bind
@close="visible = false"
v-if="visible"
:data="props.data"
:current="current"
@save="onBindSave"
/>
</j-modal>
</template>
<script lang="ts" setup>
import {
bindThirdParty,
getDingTalkOAuth2,
getUserBind,
getWechatOAuth2,
} from '@/api/account/notificationSubscription';
import { useUserInfo } from '@/store/userInfo';
import { onlyMessage } from '@/utils/comm';
import EditInfo from '../../EditInfo/index.vue';
import Bind from './Bind.vue';
const user = useUserInfo();
const emit = defineEmits(['close', 'save']);
const props = defineProps({
data: {
//
@ -87,146 +57,42 @@ const props = defineProps({
});
const editInfoVisible = ref<boolean>(false);
const url = ref<string>('');
const loading = ref<boolean>(false);
const visible = ref<boolean>(false);
const getType = computed(() => {
return props.current?.channelProvider;
});
const onBind = () => {
editInfoVisible.value = true;
if (!['notifier-dingTalk', 'notifier-weixin'].includes(getType.value)) {
editInfoVisible.value = true;
} else {
visible.value = true;
}
};
const onSave = () => {
const onSave = (info: any) => {
editInfoVisible.value = false;
user.getUserInfo();
emit('save', props.current);
if((getType.value === 'notifier-email' && info.email) || (['notifier-voice', 'notifier-sms'].includes(getType.value) && info.telephone)) {
emit('save', props.current);
emit('close');
}
};
const onBindSave = (cur: any) => {
visible.value = false;
emit('save', cur);
emit('close');
};
const onBindHandle = (
type: 'notifier-weixin' | 'notifier-dingTalk',
code: string,
) => {
getUserBind(type === 'notifier-dingTalk' ? 'dingtalk' : 'wechat', {
authCode: code,
configId: props.current?.channelConfiguration.notifierId,
})
.then((resp) => {
if (resp.status === 200) {
const _bindCode = (resp?.result || '') as string;
if (_bindCode) {
bindThirdParty(
type,
props.current?.channelConfiguration.notifierId,
_bindCode,
)
.then((response) => {
if (response.status === 200) {
//
emit('save', props.current);
emit('close');
}
})
.finally(() => {
loading.value = false;
});
}
}
})
.catch(() => {
loading.value = false;
});
};
const updateIframeStyle = () => {
const iframe = document.querySelector(
'#notifier_iframe',
) as HTMLIFrameElement;
iframe.onload = () => {
const currentUrl = iframe?.contentWindow?.location?.search || '';
console.log(currentUrl)
let authCode = '';
if (currentUrl.startsWith('?')) {
currentUrl
.substring(1)
.split('&')
.map((item) => {
if (
props.current?.channelProvider === 'notifier-dingTalk'
) {
if (item.split('=')?.[0] === 'authCode') {
authCode = item.split('=')?.[1] || '';
}
} else {
if (item.split('=')?.[0] === 'code') {
authCode = item.split('=')?.[1] || '';
}
}
});
}
if (authCode) {
loading.value = true;
onBindHandle(props.current?.channelProvider, authCode);
}
};
};
const handleSearch = async () => {
if (props.current?.channelProvider === 'notifier-weixin') {
loading.value = true;
const resp = await getWechatOAuth2(
props.current?.channelConfiguration.notifierId,
props.current?.channelConfiguration.templateId,
location.href
).finally(() => {
loading.value = false;
});
if (resp.status === 200) {
url.value = resp.result as string;
nextTick(() => {
updateIframeStyle();
});
}
}
if (props.current?.channelProvider === 'notifier-dingTalk') {
loading.value = true;
const resp = await getDingTalkOAuth2(
props.current?.channelConfiguration.notifierId,
location.href,
).finally(() => {
loading.value = false;
});
if (resp.status === 200) {
url.value = resp.result as string;
nextTick(() => {
updateIframeStyle();
});
}
}
};
watch(
() => props.current,
() => {
handleSearch();
},
{
immediate: true,
deep: true,
},
);
}
</script>
<style lang="less" scoped>
.tip {
width: 100%;
margin: 80px 0;
text-align: center;
font-size: 14px;
color: #7f7f7f;
margin: 30px 0;
font-size: 16px;
color: #333333;
}
.code {
@ -234,9 +100,10 @@ watch(
display: flex;
margin-top: 30px;
justify-content: center;
}
.code-item {
border: none;
}
.btn {
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<j-spin :spinning="loading">
<div>
<div style="padding: 0 10px">
<div class="alert">
<AIcon type="InfoCircleOutlined" />
你可以在该页面选择需要订阅的主题及接收通知的方式
@ -24,9 +24,6 @@
class="custom"
:header="item.name"
>
<template #header
><h3>{{ item.name }}</h3></template
>
<div class="child">
<template
v-for="child in item.children"
@ -116,9 +113,8 @@ onMounted(() => {
height: 40px;
padding-left: 10px;
margin-bottom: 10px;
color: rgba(0, 0, 0, 0.55);
color: #999999;
line-height: 40px;
background-color: #f6f6f6;
}
.custom {
background: #F7F8FA;

View File

@ -39,6 +39,7 @@
:img="cropperImg"
@cancel="cropperVisible = false"
@ok="saveImage"
title="更换头像"
/>
</template>

View File

@ -12,6 +12,7 @@ export const USER_CENTER_MENU_DATA = {
url: '/account/center',
sortIndex: 9999,
granted: true,
owner: 'iot',
buttons: [
{
id: USER_CENTER_MENU_BUTTON_CODE,

View File

@ -0,0 +1,102 @@
<template>
<j-modal
:width="810"
visible
title="选择应用类型"
@cancel="emit('close')"
@ok="onSave"
>
<ApplyList v-model:photoUrl="photoUrl" :options="typeOptions" v-model:value="type" />
</j-modal>
</template>
<script lang="ts" setup>
import { queryType } from '@/api/system/apply';
import { useMenuStore } from '@/store/menu';
import { onlyMessage } from '@/utils/comm';
import ApplyList from './components/ApplyList/index.vue';
const emit = defineEmits(['close']);
const menuStory = useMenuStore();
const type = ref('');
const photoUrl = ref('');
const typeOptions = ref<any[]>([]);
const onSave = () => {
if(type.value){
menuStory.jumpPage('system/Apply/Save', {}, { provider: type.value, photoUrl: photoUrl.value });
} else {
onlyMessage('请先选择应用类型', 'error')
}
};
onMounted(() => {
queryType().then((resp: any) => {
if (resp.status === 200) {
const arr = resp.result.map((item: any) => ({
label: item.name,
value: item.provider,
}));
typeOptions.value = arr;
}
});
});
</script>
<style lang="less" scoped>
.type-warp {
display: flex;
flex-wrap: wrap;
gap: 16px 24px;
width: 100%;
.type-item {
display: flex;
flex-direction: column;
align-items: center;
width: 172px;
border: 1px solid #e0e4e8;
border-radius: 2px;
cursor: pointer;
transition: all 0.3s;
.type-item-title {
margin-bottom: 8px;
font-weight: 500;
font-size: 14px;
}
.type-item-image {
width: 106px;
margin: 16px 33px;
}
&:hover {
color: @primary-color-hover;
opacity: 0.8;
}
&.active {
border-color: @primary-color-active;
opacity: 1;
}
}
&.disabled {
.notify-type-item {
cursor: not-allowed;
&:hover {
color: initial;
opacity: 0.6;
}
&.active {
opacity: 1;
}
}
}
}
</style>

View File

@ -40,7 +40,7 @@
:options="typeOptions"
v-model:photoUrl="form.data.logoUrl"
v-model:value="form.data.provider"
:disabled="!!routeQuery.id"
:disabled="!!routeQuery.id || !!routeQuery?.provider"
/>
</j-form-item>
<j-form-item
@ -53,13 +53,14 @@
},
]"
>
<j-checkbox-group
<j-check-button
v-model:value="form.data.integrationModes"
:options="joinOptions"
:multiple="true"
/>
</j-form-item>
<j-collapse style="margin-bottom: 20px">
<j-collapse style="margin-bottom: 20px;">
<j-collapse-panel
v-for="(item, index) in form.data.integrationModes"
:key="item + index"
@ -1417,7 +1418,7 @@ import FormLabel from './FormLabel.vue';
import RequestTable from './RequestTable.vue';
import MenuDialog from '../../componenets/MenuDialog.vue';
import { getImage } from '@/utils/comm';
import type { formType, dictType, optionsType } from '../typing';
import type { formType, dictType, optionsType, applyType } from '../typing';
import { getRoleList_api } from '@/api/system/user';
import { message } from 'jetlinks-ui-components';
import { randomString } from '@/utils/utils';
@ -1554,32 +1555,39 @@ const paramsValidator = () => {
paramsValid.value ? resolve('') : reject('请输入完整的请求参数');
});
};
const getType = () => {
queryType().then((resp: any) => {
if (resp.status === 200) {
const arr = resp.result.map((item: any) => ({
label: item.name,
value: item.provider,
integrationModes: item.integrationModes?.map((i: any) => {
return {
label: i.text,
value: i.value,
};
}),
}));
typeOptions.value = arr;
}
});
const getType = async () => {
const resp: any = await queryType();
if (resp.status === 200) {
const arr = resp.result.map((item: any) => ({
label: item.name,
value: item.provider,
integrationModes: item.integrationModes?.map((i: any) => {
return {
label: i.text,
value: i.value,
};
}),
}));
typeOptions.value = arr;
}
}
onMounted(() => {
getType();
onMounted(async () => {
await getType();
getRoleIdList();
getOrgIdList();
if (routeQuery.id) {
getInfo(routeQuery.id as string);
}
if(routeQuery.provider){
form.data.provider = routeQuery?.provider as applyType;
if(routeQuery.photoUrl) {
form.data.logoUrl = routeQuery?.photoUrl as string;
}
typeOptions.value = typeOptions.value.filter((i: any) => {
return i.value === routeQuery.provider;
});
}
});
//

View File

@ -20,8 +20,8 @@ export type optionsType = {
export type formType = {
id?:string,
name: string;
provider: applyType;
logoUrl: string,
provider: applyType | undefined;
logoUrl: string | undefined,
integrationModes: string[];
config?: string;
description: string;

View File

@ -22,7 +22,7 @@
<PermissionButton
:hasPermission="`${permission}:add`"
type="primary"
@click="() => table.toSave()"
@click="() => table.toAdd()"
>
<AIcon type="PlusOutlined" />新增
</PermissionButton>
@ -134,7 +134,7 @@
</j-tooltip>
</template>
<template #mark>
<!-- <template #mark>
<AIcon
type="EyeOutlined"
style="font-size: 24px"
@ -142,7 +142,7 @@
() => table.toSave(slotProps.id, true)
"
/>
</template>
</template> -->
</CardBox>
</template>
@ -190,6 +190,7 @@
@refresh="table.refresh"
/>
</div>
<Add v-if="visible" @close="visible = false" />
</page-container>
</template>
@ -206,37 +207,13 @@ import { getImage } from '@/utils/comm';
import { useMenuStore } from '@/store/menu';
import { message } from 'jetlinks-ui-components';
import BadgeStatus from '@/components/BadgeStatus/index.vue';
import Add from './Save/Add.vue';
const menuStory = useMenuStore();
const permission = 'system/Apply';
// const typeOptions = [
// {
// label: '',
// value: 'internal-standalone',
// },
// {
// label: '',
// value: 'wechat-webapp',
// },
// {
// label: '',
// value: 'internal-integrated',
// },
// {
// label: '',
// value: 'dingtalk-ent-app',
// },
// {
// label: '',
// value: 'third-party',
// },
// {
// label: '',
// value: 'wechat-miniapp',
// },
// ];
const typeOptions = ref<any[]>([])
const visible = ref<boolean>(false)
onMounted(() => {
queryType().then((resp: any) => {
@ -329,6 +306,9 @@ const table = {
refresh: () => {
tableRef.value.reload(queryParams.value);
},
toAdd: () => {
visible.value = true
},
toSave: (id?: string, view = false) => {
if (id) menuStory.jumpPage('system/Apply/Save', {}, { id, view });
else menuStory.jumpPage('system/Apply/Save');

View File

@ -1,12 +1,12 @@
<template>
<j-modal
:width="900"
:width="540"
visible
title="权限控制"
@cancel="emit('close')"
@ok="onSave"
>
<Role v-model="_selectedRowKeys" />
<Role v-model="_selectedRowKeys" :gridColumn="1" />
</j-modal>
</template>
@ -29,10 +29,6 @@ watchEffect(() => {
});
const onSave = () => {
// if(_selectedRowKeys.value.length) {
emit('save', _selectedRowKeys.value);
// } else {
// onlyMessage('', 'error')
// }
};
</script>

View File

@ -87,7 +87,7 @@
</template>
</j-dropdown>
<div class="box-item-text">
{{ slotProps?.name }}
<j-ellipsis>{{ slotProps?.name }}</j-ellipsis>
</div>
</div>
</template>
@ -453,8 +453,13 @@ const onSave = (_data: any) => {
align-items: center;
.box-item {
margin-left: 10px;
cursor: pointer;
width: 48px;
margin: 0 2px;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
.box-item-img {
background-color: #fff;
width: 32px;
@ -474,7 +479,6 @@ const onSave = (_data: any) => {
.box-item-add {
cursor: pointer;
margin-left: 16px;
background-color: #f8f9fc;
width: 54px;
height: 54px;

View File

@ -1,37 +1,63 @@
<template>
<pro-search
style="padding: 0"
type="simple"
:columns="columns"
target="category"
@search="onSearch"
/>
<j-pro-table
ref="tableRef"
:columns="columns"
:request="queryRoleList"
model="TABLE"
:params="params"
:bodyStyle="{ padding: 0 }"
:scroll="{ y: 500 }"
:defaultParams="{
// pageSize: 10,
sorts: [
{ name: 'createTime', order: 'desc' },
{ name: 'id', order: 'desc' },
],
}"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
onSelect: onSelect,
onSelectAll: onSelectAll,
onSelectNone: cancelSelect,
}"
>
<!-- <template #headerTitle>
<j-checkbox v-model:checked="checked">全选</j-checkbox>
</template> -->
</j-pro-table>
<div class="role">
<j-input-search
allowClear
@search="onSearch"
placeholder="请输入名称"
/>
<div class="role-alert">
<j-alert type="info">
<template #message>
<div class="header">
<j-checkbox
:indeterminate="indeterminate"
:checked="checked"
@change="onSelectAll"
>全选</j-checkbox
>
<j-space v-if="_selectedRowKeys.length">
<span>已选择{{ _selectedRowKeys.length }}</span>
<j-button
style="padding: 0; height: 22px"
type="link"
@click="cancelSelect"
>取消选择</j-button
>
</j-space>
</div>
</template>
</j-alert>
</div>
<j-scrollbar height="400px">
<j-pro-table
ref="tableRef"
:columns="columns"
:request="queryRoleList"
model="CARD"
:params="params"
:bodyStyle="{ padding: 0 }"
:gridColumn="gridColumn"
:alertRender="false"
:defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }],
}"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
}"
>
<template #card="slotProps">
<div class="card">
<j-checkbox
:checked="_selectedRowKeys.includes(slotProps?.id)"
@change="(e) => onSelect(e, slotProps)"
>
<j-ellipsis>{{ slotProps.name }}</j-ellipsis>
</j-checkbox>
</div>
</template>
</j-pro-table>
</j-scrollbar>
</div>
</template>
<script lang="ts" setup>
@ -43,12 +69,35 @@ const props = defineProps({
type: Array as PropType<string[]>,
default: () => [],
},
gridColumn: {
type: Number,
default: 2
}
});
const emit = defineEmits(['update:modelValue']);
const params = ref<any>();
const _selectedRowKeys = ref<string[]>([]);
const tableRef = ref();
const dataSource = computed(() => {
return tableRef.value?._dataSource || [];
});
const indeterminate = computed(() => {
return (
dataSource.value.some((item: any) => {
return _selectedRowKeys.value.includes(item.id);
}) && !checked.value
);
});
const checked = computed(() => {
return dataSource.value.every((item: any) => {
return _selectedRowKeys.value.includes(item.id);
});
});
watchEffect(() => {
_selectedRowKeys.value = props?.modelValue || [];
@ -79,8 +128,16 @@ const columns = [
},
];
const onSearch = (e: any) => {
params.value = e;
const onSearch = (val: any) => {
params.value = {
terms: [
{
column: 'name',
termType: 'like',
value: `%${val}%`,
},
],
};
};
//
@ -89,8 +146,9 @@ const cancelSelect = () => {
emit('update:modelValue', []);
};
const onSelect = (record: any, selected: boolean) => {
const onSelect = (e: any, record: any) => {
const _set = new Set(_selectedRowKeys.value);
const selected = e.target.checked;
if (selected) {
_set.add(record.id);
} else {
@ -99,9 +157,10 @@ const onSelect = (record: any, selected: boolean) => {
emit('update:modelValue', [..._set.values()]);
};
const onSelectAll = (selected: boolean, _: any[], _keys: any[]) => {
const onSelectAll = (e: any) => {
const selected = e.target.checked;
const _set = new Set(_selectedRowKeys.value);
const arr = _keys.map((item: any) => item?.id);
const arr = dataSource.value.map((item: any) => item?.id);
if (selected) {
arr.map((i: any) => {
_set.add(i);
@ -113,4 +172,28 @@ const onSelectAll = (selected: boolean, _: any[], _keys: any[]) => {
}
emit('update:modelValue', [..._set.values()]);
};
</script>
</script>
<style lang="less" scoped>
.role {
:deep(.jtable-content) {
padding: 2px 0 16px 0;
}
:deep(.jtable-card-items) {
grid-gap: 2px !important;
}
.role-alert {
margin-top: 16px;
}
.header {
display: flex;
}
}
.card {
width: 100%;
background-color: #f8f9fc;
padding: 10px 16px;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<j-spin :spinning="loading">
<div class="notify-type-warp" :class="{ disabled: disabled }">
<div class="notify-type-warp">
<div
:key="item.id"
v-for="item in options"
@ -9,7 +9,7 @@
@click="onSelect(item.value, item.label)"
>
<div class="notify-type-item-image">
<img :width="106" :src="item.iconUrl" />
<img :width="64" :src="item.iconUrl" />
</div>
<div class="notify-type-item-title">{{item.label}}</div>
</div>
@ -80,53 +80,40 @@ onMounted(() => {
.notify-type-warp {
display: flex;
flex-wrap: wrap;
gap: 16px 24px;
width: 100%;
padding: 50px 200px;
gap: 16px 24px;
.notify-type-item {
display: flex;
flex-direction: column;
align-items: center;
width: 172px;
border: 1px solid #e0e4e8;
border-radius: 2px;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
width: 96px;
padding: 8px 16px;
.notify-type-item-title {
margin-bottom: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
width: 100%;
text-align: center;
}
.notify-type-item-image {
width: 106px;
margin: 16px 33px;
width: 100%;
margin-bottom: 8px;
display: flex;
justify-content: center;
}
&:hover {
color: @primary-color-hover;
opacity: 0.8;
background-color: #F2F4F7;
}
&.active {
border-color: @primary-color-active;
opacity: 1;
}
}
&.disabled {
.notify-type-item {
cursor: not-allowed;
&:hover {
color: initial;
opacity: 0.6;
}
&.active {
opacity: 1;
}
background-color: #F2F4F7;
border: 1px solid #ADB8C7;
}
}
}

View File

@ -1,19 +1,30 @@
<template>
<j-modal
:width="900"
:width="1056"
visible
title="配置通知方式"
@cancel="emit('close')"
@ok="onSave"
:bodyStyle="{ padding: 0 }"
>
<j-steps :current="current" size="small" @change="onChange">
<j-step v-for="item in stepList" :title="item" :key="item" />
</j-steps>
<div style="background-color: #f8f9fc; padding: 25px 100px">
<j-steps :current="current" size="small" @change="onChange">
<j-step
v-for="(item, index) in stepList"
:title="item"
:key="item"
>
<template #description>
<span v-if="current === index">进行中</span>
<span v-if="current < index">未开始</span>
<span v-if="current > index">已完成</span>
</template>
</j-step>
</j-steps>
</div>
<div style="margin: 20px">
<template v-if="current === 0">
<NotifyWay
:value="formModel.channelProvider"
:name="formModel.name"
@change="onWayChange"
/>
</template>
@ -43,6 +54,38 @@
<template v-if="current === 4">
<Role v-model="formModel.grant.role.idList" />
</template>
<template v-if="current === 5">
<div>
<div class="alert">
<AIcon type="InfoCircleOutlined" />
被分配了接收权限的用户将根据名称判断是否订阅该通知
</div>
<div style="margin: 50px 200px">
<j-form
ref="formRef"
:model="formModel"
layout="vertical"
>
<j-form-item
name="name"
label="名称"
:rules="[
{ required: true, message: '请输入名称' },
{
max: 8,
message: '最多可输入8个字符',
},
]"
>
<j-input
v-model:value="formModel.name"
placeholder="请输入名称"
/>
</j-form-item>
</j-form>
</div>
</div>
</template>
</div>
<template #footer>
<j-space>
@ -115,6 +158,7 @@ const stepList = [
'选择通知模板',
'配置模板变量',
'配置用户权限',
'完成',
];
const current = ref<number>(0);
const variable = ref([]);
@ -134,6 +178,7 @@ const formModel = reactive<{
channelConfiguration: {},
});
const variableRef = ref();
const formRef = ref();
const _variableDefinitions = computed(() => {
const arr = ['user', 'org'];
@ -172,50 +217,41 @@ const handleVariable = (obj: any) => {
};
const jumpStep = async (val: number) => {
if (val === 0) {
current.value = val;
} else if (val === 1) {
if (formModel.channelProvider) {
current.value = val;
} else {
if (val >= 1) {
if (!formModel.channelProvider) {
onlyMessage('请选择通知方式', 'error');
return;
}
} else if (val === 2) {
if (formModel.channelConfiguration.notifierId) {
current.value = val;
} else {
}
if (val >= 2) {
if (!formModel.channelConfiguration.notifierId) {
onlyMessage('请选择通知配置', 'error');
return;
}
} else if (val === 3) {
if (formModel.channelConfiguration.templateId) {
const resp = await Template.getTemplateDetail(
formModel.channelConfiguration.templateId,
);
if (resp.status === 200) {
variable.value = resp.result?.variableDefinitions || [];
current.value = val;
}
} else {
}
if (val >= 3) {
if (!formModel.channelConfiguration.templateId) {
onlyMessage('请选择通知模板', 'error');
return;
}
} else if (val === 4) {
}
if (val >= 4) {
if (variable.value.length) {
if (_variableDefinitions.value.length) {
const obj = await variableRef.value.onSave();
if (obj) {
handleVariable(obj);
current.value = val;
} else {
onlyMessage('请配置模版变量', 'error');
return;
}
} else {
handleVariable({});
current.value = val;
}
} else {
current.value = val;
}
}
current.value = val;
};
const onWayChange = (obj: any) => {
@ -262,7 +298,8 @@ const onChange = (cur: number) => {
jumpStep(cur);
};
const onSave = () => {
const onSave = async () => {
await formRef.value?.validate();
formModel.grant.permissions =
props.provider === 'alarm'
? [{ id: 'alarm-config', actions: ['query'] }]