Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
XieYongHong 2023-06-29 17:09:07 +08:00
commit 08efcf29eb
45 changed files with 552 additions and 573 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

View File

@ -99,7 +99,7 @@ export const templateDownload = (productId: string, type: string) => server.get(
* @param type * @param type
* @returns * @returns
*/ */
export const deviceImport = (productId: string, fileUrl: string, autoDeploy: boolean) => `${BASE_API_PATH}/device-instance/${productId}/import?fileUrl=${fileUrl}&autoDeploy=${autoDeploy}&:X_Access_Token=${LocalStore.get(TOKEN_KEY)}` export const deviceImport = (productId: string, fileUrl: string, autoDeploy: boolean) => `${BASE_API_PATH}/device-instance/${productId}/import/_withlog?fileUrl=${fileUrl}&autoDeploy=${autoDeploy}&:X_Access_Token=${LocalStore.get(TOKEN_KEY)}`
/** /**
* *

View File

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

View File

@ -1,14 +1,11 @@
<template> <template>
<div class="box"> <div class="box">
<div class="box-item" v-if="data.length > showLength"> <div class="box-item" v-if="pageIndex > 0">
<j-button <j-button
@click="onLeft" @click="onLeft"
type="primary"
:disabled="!(pageIndex > 0)"
shape="circle" shape="circle"
class="box-item-action" class="box-item-action"
><AIcon type="LeftOutlined" >+{{ pageIndex * showLength }}</j-button>
/></j-button>
</div> </div>
<div class="box-item" v-for="item in getData" :key="item.id"> <div class="box-item" v-for="item in getData" :key="item.id">
<slot name="card" v-bind="item"></slot> <slot name="card" v-bind="item"></slot>
@ -16,15 +13,12 @@
<div class="box-item"> <div class="box-item">
<slot name="add"></slot> <slot name="add"></slot>
</div> </div>
<div class="box-item" v-if="data.length > showLength"> <div class="box-item" v-if="(pageIndex + 1) * showLength < data.length">
<j-button <j-button
:disabled="!(pageIndex + showLength < data.length)"
type="primary"
shape="circle" shape="circle"
class="box-item-action" class="box-item-action"
@click="onRight" @click="onRight"
><AIcon type="RightOutlined" >+{{ data.length - (pageIndex + 1) * showLength }}</j-button>
/></j-button>
</div> </div>
</div> </div>
</template> </template>
@ -43,15 +37,13 @@ const props = defineProps({
}, },
}); });
// const emit = defineEmits(['add']);
const pageIndex = ref<number>(0); const pageIndex = ref<number>(0);
const getData = computed(() => { const getData = computed(() => {
const start = pageIndex.value >= 0 ? pageIndex.value : 0; const start = pageIndex.value >= 0 ? pageIndex.value * props.showLength : 0;
const end = const end =
props.showLength + pageIndex.value < props.data.length (pageIndex.value + 1) * props.showLength < props.data.length
? props.showLength + pageIndex.value ? props.showLength * (pageIndex.value + 1)
: props.data.length; : props.data.length;
return props.data.slice(start, end); return props.data.slice(start, end);
}); });
@ -69,10 +61,6 @@ const onLeft = () => {
pageIndex.value -= 1; pageIndex.value -= 1;
} }
}; };
// const onAdd = () => {
// emit('add');
// };
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -37,7 +37,7 @@
:src=" :src="
iconMap.get( iconMap.get(
bindUser?.applicationProvider, bindUser?.applicationProvider,
) || getImage('/apply/provider1.png') ) || getImage('/apply/internal-standalone.png')
" "
/> />
<p>账号{{ bindUser?.result?.userId || '-' }}</p> <p>账号{{ bindUser?.result?.userId || '-' }}</p>
@ -153,8 +153,8 @@ interface formData {
const iconMap = new Map() const iconMap = new Map()
iconMap.set('dingtalk-ent-app', getImage('/notice/dingtalk.png')) iconMap.set('dingtalk-ent-app', getImage('/notice/dingtalk.png'))
iconMap.set('wechat-webapp', getImage('/notice/wechat.png')) iconMap.set('wechat-webapp', getImage('/notice/wechat.png'))
iconMap.set('internal-standalone', getImage('/apply/provider1.png')) iconMap.set('internal-standalone', getImage('/apply/internal-standalone.png'))
iconMap.set('third-party', getImage('/apply/provider5.png')) iconMap.set('third-party', getImage('/apply/third-party.png'))
const token = computed(() => LocalStore.get(TOKEN_KEY)) const token = computed(() => LocalStore.get(TOKEN_KEY))

View File

@ -2,27 +2,32 @@
<div class="box"> <div class="box">
<div class="content"> <div class="content">
<template v-if="bindList.length"> <template v-if="bindList.length">
<div class="content-item" v-for="item in bindList" :key="item.id"> <div
class="content-item"
v-for="item in bindList"
:key="item.id"
>
<div class="content-item-left"> <div class="content-item-left">
<img <img
:src="item.logoUrl || getImage(bindIcon[item.provider])" :src="
style="height: 50px; width: 50px" item.logoUrl ||
width="50px" getImage(bindIcon[item.provider])
height="50px" "
style="height: 24px; width: 24px"
alt="" alt=""
/> />
<Ellipsis style="max-width: 200px; font-size: 22px">{{ <Ellipsis style="max-width: 200px; color: #333; margin: 0 8px 0 6px">{{
item?.name item?.name
}}</Ellipsis> }}</Ellipsis>
<div> <div>
<j-tag v-if="item.bound">已绑定</j-tag> <span v-if="item.bound" style="color: #2BA245">已绑定</span>
<j-tag v-else>未绑定</j-tag> <span v-else style="color: #999">未绑定</span>
</div> </div>
<div v-if="item.others?.name"> <div v-if="item.others?.name" style="color: #666666">
绑定名{{ item.others?.name }} {{ item.others?.name }}已绑定的用户名
</div> </div>
</div> </div>
<div> <div class="content-item-right">
<j-popconfirm <j-popconfirm
v-if="item.bound" v-if="item.bound"
title="确认解除绑定嘛?" title="确认解除绑定嘛?"
@ -30,13 +35,17 @@
> >
<j-button>解除绑定</j-button> <j-button>解除绑定</j-button>
</j-popconfirm> </j-popconfirm>
<j-button v-else type="primary" @click="clickBind(item.id)" <j-button
v-else
ghost
type="primary"
@click="clickBind(item.id)"
>立即绑定</j-button >立即绑定</j-button
> >
</div> </div>
</div> </div>
</template> </template>
<j-empty v-else style="margin: 200px 0;" /> <j-empty v-else style="margin: 200px 0" />
</div> </div>
</div> </div>
</template> </template>
@ -51,8 +60,8 @@ const bindList = ref<any[]>([]);
const bindIcon = { const bindIcon = {
'dingtalk-ent-app': '/notice/dingtalk.png', 'dingtalk-ent-app': '/notice/dingtalk.png',
'wechat-webapp': '/notice/wechat.png', 'wechat-webapp': '/notice/wechat.png',
'internal-standalone': '/apply/provider1.png', 'internal-standalone': '/apply/internal-standalone.png',
'third-party': '/apply/provider5.png', 'third-party': '/apply/third-party.png',
}; };
const unBind = (id: string) => { const unBind = (id: string) => {
unBind_api(id).then((resp) => { unBind_api(id).then((resp) => {
@ -93,18 +102,19 @@ onMounted(() => {
.box { .box {
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%;
.content { .content {
margin-top: 24px; width: 100%;
width: 80%;
.content-item { .content-item {
width: 100%; width: 100%;
margin: 10px 0; margin-bottom: 16px;
padding: 15px; padding: 15px;
border: 1px solid #f0f0f0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 60px;
border-radius: 6px;
background: #f7f8fa;
.content-item-left { .content-item-left {
display: flex; display: flex;
@ -112,6 +122,13 @@ onMounted(() => {
align-items: center; align-items: center;
} }
} }
.content-item-right {
button:hover {
background-color: @primary-color;
color: #fff;
}
}
} }
} }
</style> </style>

View File

@ -1,34 +1,31 @@
<template> <template>
<div class="choose-view"> <div class="choose-view">
<j-row class="view-content" :gutter="24"> <div class="view-content">
<j-col <div
:span="8" :span="8"
class="select-item" class="select-item"
:class="{ selected: currentView === 'device' }"
@click="currentView = 'device'" @click="currentView = 'device'"
> >
<img :src="getImage('/home/device.png')" alt="" /> <img :src="getImage(`/home/home-view/device${currentView === 'device' ? '-active' : ''}.png`)" alt="" />
</j-col> </div>
<j-col <div
:span="8" :span="8"
class="select-item" class="select-item"
:class="{ selected: currentView === 'ops' }"
@click="currentView = 'ops'" @click="currentView = 'ops'"
> >
<img :src="getImage('/home/ops.png')" alt="" /> <img :src="getImage(`/home/home-view/ops${currentView === 'ops' ? '-active' : ''}.png`)" alt="" />
</j-col> </div>
<j-col <div
:span="8" :span="8"
class="select-item" class="select-item"
:class="{
selected: currentView === 'comprehensive',
}"
@click="currentView = 'comprehensive'" @click="currentView = 'comprehensive'"
> >
<img :src="getImage('/home/comprehensive.png')" alt="" /> <img :src="getImage(`/home/home-view/comprehensive${currentView === 'comprehensive' ? '-active' : ''}.png`)" alt="" />
</j-col> </div>
</j-row> </div>
<j-button type="primary" class="btn" @click="confirm">确定</j-button> <div class="btn">
<j-button type="primary" @click="confirm">保存修改</j-button>
</div>
</div> </div>
</template> </template>
@ -76,28 +73,26 @@ onMounted(() => {
<style lang="less" scoped> <style lang="less" scoped>
.choose-view { .choose-view {
width: 100%; width: 100%;
margin-top: 30px; padding: 4% 9% 0 9%;
padding: 48px 150px;
box-sizing: border-box; box-sizing: border-box;
.view-content { .view-content {
display: flex; display: flex;
flex-flow: row wrap; justify-content: space-between;
.select-item { .select-item {
border: 2px solid transparent; cursor: pointer;
width: 30%;
img { img {
width: 100%; width: 100%;
background-size: cover; background-size: cover;
} }
&.selected {
border-color: #10239e;
}
} }
} }
.btn { .btn {
display: block; display: flex;
margin: 48px auto; justify-content: flex-end;
margin-top: 68px;
} }
} }
</style> </style>

View File

@ -1,88 +1,81 @@
<template> <template>
<page-container> <div class="notification-record-container">
<div class="notification-record-container"> <pro-search
<pro-search :columns="columns"
:columns="columns" :target="type"
target="category" style="padding: 0"
style="padding: 0" @search="(e) => (queryParams = e)"
@search="queryParams" />
/> <j-pro-table
ref="tableRef"
<j-pro-table :columns="columns"
ref="tableRef" :request="getList_api"
:columns="columns" model="TABLE"
:request="getList_api" :params="queryParams"
model="TABLE" :bodyStyle="{ padding: 0 }"
:params="queryParams" :defaultParams="defaultParams"
:bodyStyle="{ padding: 0 }" >
:defaultParams="defaultParams" <template #rightExtraRender>
> <j-popconfirm title="确认全部已读?" @confirm="onAllRead">
<template #headerTitle> <j-button type="primary">全部已读</j-button>
<j-popconfirm title="确认全部已读?" @confirm="onAllRead"> </j-popconfirm>
<j-button type="primary">全部已读</j-button> </template>
</j-popconfirm> <template #topicProvider="slotProps">
</template> {{ slotProps.topicName }}
<template #topicProvider="slotProps"> </template>
{{ slotProps.topicName }} <template #notifyTime="slotProps">
</template> {{ dayjs(slotProps.notifyTime).format('YYYY-MM-DD HH:mm:ss') }}
<template #notifyTime="slotProps"> </template>
{{ <template #state="slotProps">
dayjs(slotProps.notifyTime).format( <BadgeStatus
'YYYY-MM-DD HH:mm:ss', :status="slotProps.state.value"
) :text="slotProps.state.text"
}} :statusNames="{
</template> read: 'success',
<template #state="slotProps"> unread: 'error',
<BadgeStatus }"
:status="slotProps.state.value" ></BadgeStatus>
:text="slotProps.state.text" </template>
:statusNames="{ <template #action="slotProps">
read: 'success', <j-space :size="16">
unread: 'error', <PermissionButton
type="link"
:popConfirm="{
title: `确认标为${
slotProps.state.value === 'read'
? '未读'
: '已读'
}`,
onConfirm: () => changeStatus(slotProps),
}" }"
></BadgeStatus> :tooltip="{
</template> title:
<template #action="slotProps"> slotProps.state.value === 'read'
<j-space :size="16"> ? '标为未读'
<PermissionButton : '标为已读',
type="link" }"
:popConfirm="{ >
title: `确认标为${ <AIcon type="icon-a-PIZHU1" />
slotProps.state.value === 'read' </PermissionButton>
? '未读' <PermissionButton
: '已读' type="link"
}`, :tooltip="{
onConfirm: () => changeStatus(slotProps), title: '查看',
}" }"
:tooltip="{ @click="view(slotProps)"
title: >
slotProps.state.value === 'read' <AIcon type="SearchOutlined" />
? '标为未读' </PermissionButton>
: '标为已读', </j-space>
}" </template>
> </j-pro-table>
<AIcon type="icon-a-PIZHU1" /> <ViewDialog
</PermissionButton> v-if="viewVisible"
<PermissionButton v-model:visible="viewVisible"
type="link" :data="viewItem"
:tooltip="{ :type="type"
title: '查看', />
}" </div>
@click="view(slotProps)"
>
<AIcon type="SearchOutlined" />
</PermissionButton>
</j-space>
</template>
</j-pro-table>
<ViewDialog
v-if="viewVisible"
v-model:visible="viewVisible"
:data="viewItem"
:type="type"
/>
</div>
</page-container>
</template> </template>
<script setup lang="ts" name="NotificationRecord"> <script setup lang="ts" name="NotificationRecord">
@ -91,7 +84,7 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
import { import {
getList_api, getList_api,
changeStatus_api, changeStatus_api,
changeAllStatus changeAllStatus,
} from '@/api/account/notificationRecord'; } from '@/api/account/notificationRecord';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useUserInfo } from '@/store/userInfo'; import { useUserInfo } from '@/store/userInfo';
@ -115,11 +108,11 @@ const getType = computed(() => {
return ['system-event']; return ['system-event'];
} else { } else {
return [ return [
'alarm',
'alarm-product', 'alarm-product',
'alarm-device', 'alarm-device',
'alarm-other', 'alarm-other',
'alarm-org', 'alarm-org',
'alarm',
]; ];
} }
}); });
@ -132,16 +125,18 @@ const columns = [
search: { search: {
type: 'select', type: 'select',
options: () => options: () =>
getTypeList_api().then((resp: any) => getTypeList_api().then((resp: any) => {
resp.result return resp.result
.map((item: any) => ({ .map((item: any) => ({
label: item.name, label: item.name,
value: item.id, value: item.id,
})) }))
.filter((item: any) => .filter((item: any) => {
[...getType.value].includes(item?.value), return [...getType.value].includes(item?.value)
), }).sort((a: any, b: any) => {
), return b?.value?.length - a?.value?.length
});
}),
}, },
scopedSlots: true, scopedSlots: true,
ellipsis: true, ellipsis: true,
@ -240,18 +235,18 @@ const changeStatus = (row: any) => {
}; };
watchEffect(() => { watchEffect(() => {
if(user.messageInfo?.id) { if (user.messageInfo?.id) {
view(user.messageInfo) view(user.messageInfo);
} }
}) });
const onAllRead = async () => { const onAllRead = async () => {
const resp = await changeAllStatus('_read', getType.value) const resp = await changeAllStatus('_read', getType.value);
if(resp.status === 200){ if (resp.status === 200) {
onlyMessage('操作成功!') onlyMessage('操作成功!');
refresh() refresh();
} }
} };
onMounted(() => { onMounted(() => {
if (routerParams.params?.value.row) { if (routerParams.params?.value.row) {

View File

@ -1,5 +1,5 @@
<template> <template>
<div style="margin-top: 24px"> <div>
<j-tabs <j-tabs
tab-position="left" tab-position="left"
v-if="tabs.length" v-if="tabs.length"

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="child-item"> <div class="child-item">
<div class="child-item-left"> <div class="child-item-left">
<div style="font-weight: 600"> <div style="color: #333333">
{{ data?.name }} {{ data?.name }}
</div> </div>
<div class="child-item-left-auth" v-if="data?.description"> <div class="child-item-left-auth" v-if="data?.description">
<j-tooltip :title="data.description"> <j-tooltip :title="data.description">
<AIcon <AIcon
style="font-size: 20px" style="font-size: 16px"
type="ExclamationCircleOutlined" type="ExclamationCircleOutlined"
/> />
</j-tooltip> </j-tooltip>
@ -19,31 +19,22 @@
<div class="box-item"> <div class="box-item">
<div class="box-item-img"> <div class="box-item-img">
<j-dropdown placement="top" :trigger="['click']"> <j-dropdown placement="top" :trigger="['click']">
<!-- :visible="show?.[slotProps?.id]" <div
@visibleChange="onVisibleChange(slotProps)" --> :class="{
<div> disabled: !notifyChannels?.includes(
slotProps?.id,
),
}"
>
<img <img
:src=" :src="
iconMap.get( iconMap.get(
slotProps?.channelProvider, slotProps?.channelProvider,
) )
" "
style="width: 32px"
/> />
<div
:class="{
disabled: !notifyChannels?.includes(
slotProps?.id,
),
}"
></div>
</div> </div>
<!-- v-if="
notifyChannels?.includes(
slotProps?.id,
) &&
slotProps?.channelProvider !==
'inside-mail'
" -->
<template #overlay> <template #overlay>
<j-menu> <j-menu>
<j-menu-item <j-menu-item
@ -97,13 +88,6 @@
</j-menu> </j-menu>
</template> </template>
</j-dropdown> </j-dropdown>
<div class="box-item-checked">
<j-checkbox
:checked="
notifyChannels?.includes(slotProps?.id)
"
></j-checkbox>
</div>
</div> </div>
<div class="box-item-text"> <div class="box-item-text">
{{ slotProps?.name }} {{ slotProps?.name }}
@ -140,12 +124,12 @@ import { useUserInfo } from '@/store/userInfo';
import EditInfo from '../../EditInfo/index.vue'; import EditInfo from '../../EditInfo/index.vue';
const iconMap = new Map(); const iconMap = new Map();
iconMap.set('notifier-dingTalk', getImage('/notice/dingtalk.png')); iconMap.set('notifier-dingTalk', getImage('/notice-rule/dingtalk.png'));
iconMap.set('notifier-weixin', getImage('/notice/wechat.png')); iconMap.set('notifier-weixin', getImage('/notice-rule/wechat.png'));
iconMap.set('notifier-email', getImage('/notice/email.png')); iconMap.set('notifier-email', getImage('/notice-rule/email.png'));
iconMap.set('notifier-voice', getImage('/notice/voice.png')); iconMap.set('notifier-voice', getImage('/notice-rule/voice.png'));
iconMap.set('notifier-sms', getImage('/notice/sms.png')); iconMap.set('notifier-sms', getImage('/notice-rule/sms.png'));
iconMap.set('inside-mail', getImage('/notice/inside-mail.png')); iconMap.set('inside-mail', getImage('/notice-rule/inside-mail.png'));
const current = ref<any>({}); const current = ref<any>({});
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
@ -272,17 +256,15 @@ const onSave = () => {
<style lang="less" scoped> <style lang="less" scoped>
.child-item { .child-item {
padding: 10px 20px; padding: 0 20px;
margin: 5px;
background: #f7f7f7;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
box-shadow: 0px 1px 0px 0px #E2E2E2;
.child-item-left { .child-item-left {
display: flex; display: flex;
align-items: center; align-items: center;
height: 80px;
div { div {
display: flex; display: flex;
@ -303,40 +285,29 @@ const onSave = () => {
.box-item { .box-item {
margin-left: 10px; margin-left: 10px;
.box-item-img { .box-item-img {
width: 48px; width: 60px;
height: 48px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
position: relative; position: relative;
img { img {
width: 100%;
z-index: 1; z-index: 1;
} }
.box-item-checked {
position: absolute;
top: -10px;
right: -10px;
z-index: 3;
}
.disabled { .disabled {
background-color: rgba(#000, 0.38); filter: grayscale(100%);
position: absolute; // filter: brightness(0);
width: 48px; // opacity: 50%;
height: 48px;
z-index: 2;
top: 0;
left: 0;
} }
} }
.box-item-text { .box-item-text {
width: 100%; width: 100%;
text-align: center; text-align: center;
height: 20px; color: #666666;
font-size: 12px;
} }
} }
} }

View File

@ -1,13 +1,13 @@
<template> <template>
<j-spin :spinning="loading"> <j-spin :spinning="loading">
<div style="margin-top: 24px"> <div>
<div class="alert"> <div class="alert">
<AIcon type="InfoCircleOutlined" /> <AIcon type="InfoCircleOutlined" />
你可以在该页面选择需要订阅的主题及接收通知的方式 你可以在该页面选择需要订阅的主题及接收通知的方式
</div> </div>
<div style="margin-top: 20px"> <div class="content-collapse">
<template v-if="dataSource.length"> <template v-if="dataSource.length">
<j-collapse :bordered="false" v-model:activeKey="activeKey"> <j-collapse :bordered="false" v-model:activeKey="activeKey" expand-icon-position="right">
<template #expandIcon="{ isActive }"> <template #expandIcon="{ isActive }">
<AIcon <AIcon
type="CaretRightOutlined" type="CaretRightOutlined"
@ -22,6 +22,7 @@
v-for="item in dataSource" v-for="item in dataSource"
:key="item.provider" :key="item.provider"
class="custom" class="custom"
:header="item.name"
> >
<template #header <template #header
><h3>{{ item.name }}</h3></template ><h3>{{ item.name }}</h3></template
@ -120,13 +121,19 @@ onMounted(() => {
background-color: #f6f6f6; background-color: #f6f6f6;
} }
.custom { .custom {
background: #f7f7f7; background: #F7F8FA;
border-radius: 4px;
border: 0; border: 0;
overflow: hidden; overflow: hidden;
color: #333333;
} }
.child { .child {
background-color: white; background-color: white;
padding: 10px; padding-bottom: 24px;
}
.content-collapse {
:deep(.ant-collapse-content > .ant-collapse-content-box) {
padding: 0;
}
} }
</style> </style>

View File

@ -154,8 +154,8 @@ const saveImage = (url: string) => {
<style lang="less" scoped> <style lang="less" scoped>
@border: 1px dashed @border-color-base; @border: 1px dashed @border-color-base;
@mask-color: rgba(#000, 0.35); @mask-color: rgba(#000, 0.35);
@with: 66px; @with: 96px;
@height: 66px; @height: 96px;
.flex-center() { .flex-center() {
align-items: center; align-items: center;

View File

@ -16,73 +16,58 @@
</div> </div>
<div class="person-header-item-info-right"> <div class="person-header-item-info-right">
<div class="person-header-item-info-right-top"> <div class="person-header-item-info-right-top">
<span>{{ _org }}部门 · {{ _role }}角色</span> Hi, {{ user.userInfos?.name }}
</div> </div>
<div class="person-header-item-info-right-info"> <div class="person-header-item-info-right-info">
<div>用户名 {{ user.userInfos?.username }}</div> <div>
<div>账号ID {{ user.userInfos?.id }}</div> <j-tag
v-for="i in user.userInfos?.orgList || []"
:key="i?.id"
>{{ i?.name }}</j-tag
>
</div>
<div>
<j-tag
v-for="i in user.userInfos?.roleList || []"
:key="i?.id"
>{{ i?.name }}</j-tag
>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="person-header-item-action"> <div class="person-header-item-action">
<div class="person-header-item-action-left"> <j-space :size="24">
<j-space> <j-button class="btn" @click="visible = true"
<j-button >查看详情</j-button
@click="onActivated(item.key)" >
v-for="item in list" <j-button @click="editInfoVisible = true"
:type=" >编辑资料</j-button
user.tabKey === item.key >
? 'primary' <j-button
: 'default' v-if="permission"
" @click="editPasswordVisible = true"
:key="item.key" >
>{{ item.title }}</j-button 修改密码
> </j-button>
</j-space> </j-space>
</div>
<div class="person-header-item-action-right">
<j-space :size="24">
<j-tooltip title="查看详情"
><j-button
@click="visible = true"
shape="circle"
><AIcon
style="font-size: 24px"
type="FileSearchOutlined" /></j-button
></j-tooltip>
<j-tooltip title="编辑资料"
><j-button
shape="circle"
@click="editInfoVisible = true"
><AIcon
style="font-size: 24px"
type="FormOutlined" /></j-button
></j-tooltip>
<PermissionButton
shape="circle"
v-if="permission"
:tooltip="{
title: '修改密码'
}"
@click="editPasswordVisible = true"
>
<AIcon
style="font-size: 24px"
type="LockOutlined"
/>
</PermissionButton>
</j-space>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="person-content"> <div class="person-content">
<div class="person-content-item"> <div class="person-content-item">
<FullPage> <div class="person-content-item-content">
<div class="person-content-item-content"> <j-tabs v-model:activeKey="user.tabKey" type="card">
<j-tab-pane
v-for="item in list"
:key="item.key"
:tab="item.title"
/>
</j-tabs>
<j-scrollbar :height="`calc(100% - 51px)`">
<component :is="tabs[user.tabKey]" /> <component :is="tabs[user.tabKey]" />
</div> </j-scrollbar>
</FullPage> </div>
</div> </div>
</div> </div>
<Detail v-if="visible" @close="visible = false" /> <Detail v-if="visible" @close="visible = false" />
@ -114,9 +99,9 @@ import { updateMeInfo_api } from '@/api/account/center';
import { onlyMessage } from '@/utils/comm'; import { onlyMessage } from '@/utils/comm';
import { useRouterParams } from '@/utils/hooks/useParams'; import { useRouterParams } from '@/utils/hooks/useParams';
import { import {
USER_CENTER_MENU_BUTTON_CODE, USER_CENTER_MENU_BUTTON_CODE,
USER_CENTER_MENU_CODE USER_CENTER_MENU_CODE,
} from '@/utils/consts' } from '@/utils/consts';
import { usePermissionStore } from '@/store/permission'; import { usePermissionStore } from '@/store/permission';
const imageTypes = reactive([ const imageTypes = reactive([
@ -164,28 +149,9 @@ const visible = ref<boolean>(false);
const editInfoVisible = ref<boolean>(false); const editInfoVisible = ref<boolean>(false);
const editPasswordVisible = ref<boolean>(false); const editPasswordVisible = ref<boolean>(false);
const hasPermission = usePermissionStore().hasPermission; const permission = usePermissionStore().hasPermission(
const permission = () => hasPermission(`${USER_CENTER_MENU_CODE}:${USER_CENTER_MENU_BUTTON_CODE}`); `${USER_CENTER_MENU_CODE}:${USER_CENTER_MENU_BUTTON_CODE}`,
);
const onActivated = (_key: KeyType) => {
user.tabKey = _key;
};
const _org = computed(() => {
return user.userInfos?.orgList
?.map((item: any) => {
return item?.name;
})
.join(',');
});
const _role = computed(() => {
return user.userInfos?.roleList
?.map((item: any) => {
return item?.name;
})
.join(',');
});
const onSave = () => { const onSave = () => {
user.getUserInfo(); user.getUserInfo();
@ -213,21 +179,27 @@ watchEffect(() => {
user.tabKey = router.params.value?.tabKey; user.tabKey = router.params.value?.tabKey;
} }
}); });
onUnmounted(() => {
user.tabKey = 'HomeView'
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@padding: 14%;
.person { .person {
.person-header { .person-header {
width: 100%; width: 100%;
height: 150px; height: 156px;
padding: 0 150px; padding: 0 @padding;
background-color: rgba(2, 125, 180, 0.368); background-color: #fff;
.person-header-item { .person-header-item {
position: relative; display: flex;
justify-content: space-between;
align-items: center;
height: 100%; height: 100%;
.person-header-item-info { .person-header-item-info {
padding-top: 30px;
display: flex; display: flex;
.person-header-item-info-left { .person-header-item-info-left {
margin-right: 30px; margin-right: 30px;
@ -236,64 +208,52 @@ watchEffect(() => {
.person-header-item-info-right { .person-header-item-info-right {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between;
.person-header-item-info-right-top { .person-header-item-info-right-top {
span { display: flex;
background-color: rgba( font-size: 26px;
255, color: #1d2129;
255, font-weight: 500;
128,
0.43137254901960786
);
border-radius: 5px;
padding: 0 10px;
}
} }
.person-header-item-info-right-info { .person-header-item-info-right-info {
color: #fff; div {
display: flex; margin: 5px 0;
font-size: 16px;
> :not(:last-child) {
margin-right: 20px;
} }
} }
} }
} }
.person-header-item-action { .person-header-item-action {
position: absolute; button {
width: 100%; width: 110px;
height: 50px; background-color: #ebeef4;
z-index: 2; color: #333333;
left: 0; border: none;
bottom: -25px;
padding: 0 50px;
display: flex;
justify-content: space-between;
align-items: center;
.person-header-item-action-left {
button {
height: 35px;
padding: 0 40px;
}
} }
.person-header-item-action-right { .btn {
:deep(button) { background-color: @primary-color;
height: 50px; color: #fff;
width: 50px;
}
} }
} }
} }
} }
.person-content-item {
padding: 10px 20px;
background-color: #fff;
overflow: hidden;
}
.person-content { .person-content {
width: 100%; width: 100%;
padding: 0 150px; padding: 0 @padding;
.person-content-item-content { margin-top: 15px;
padding: 20px; }
}
.person-content-item-content {
height: calc(100vh - 251px);
width: 100%;
padding: 10px 0;
} }
} }
</style> </style>

View File

@ -50,7 +50,7 @@
功能说明 功能说明
</div> </div>
<p> <p>
该功能用于将{{'协议包'}}中的 该功能用于将协议包中的
<b>物模型属性标识</b> <b>物模型属性标识</b>
<b>平台物模型属性标识</b>进行映射,当两方属性标识不一致时可在当前页面直接修改映射管理系统将以映射后的物模型属性进行数据处理 <b>平台物模型属性标识</b>进行映射,当两方属性标识不一致时可在当前页面直接修改映射管理系统将以映射后的物模型属性进行数据处理
</p> </p>

View File

@ -83,6 +83,10 @@ watch(
{ immediate: true, deep: true }, { immediate: true, deep: true },
); );
const productName = computed(() => {
return productList.value.find(item => item.id === modelRef.product)?.name || ''
})
const handleOk = async () => { const handleOk = async () => {
const params = encodeQuery(props.data); const params = encodeQuery(props.data);
// downloadFile( // downloadFile(
@ -97,7 +101,7 @@ const handleOk = async () => {
if (res) { if (res) {
const blob = new Blob([res], { type: modelRef.fileType }); const blob = new Blob([res], { type: modelRef.fileType });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
downloadFileByUrl(url, `设备实例`, modelRef.fileType); downloadFileByUrl(url, `${productName.value ? (productName.value + '下设备') : '设备实例'}`, modelRef.fileType);
emit('close'); emit('close');
} }
}; };

View File

@ -1,70 +1,68 @@
<template> <template>
<div class='file'> <div class="file">
<j-form layout='vertical'> <j-form layout="vertical">
<!-- <j-form-item label='文件格式' > <j-form-item label="下载模板">
<div class='file-type-label'> <div class="file-download">
<a-radio-group class='file-type-radio' v-model:value="modelRef.file.fileType" > <j-button @click="downFile('xlsx')">模板格式.xlsx</j-button>
<a-radio-button value="xlsx">xlsx</a-radio-button> <j-button @click="downFile('csv')">模板格式.csv</j-button>
<a-radio-button value="csv">csv</a-radio-button> </div>
</a-radio-group> </j-form-item>
<a-checkbox v-model:checked="modelRef.file.autoDeploy">自动启用</a-checkbox> <j-form-item label="文件上传">
<!-- 导入系统已存在的设备数据不会更改已存在设备的所属产品信息 -->
<a-upload-dragger
v-model:fileList="modelRef.upload"
name="file"
:action="FILE_UPLOAD"
:headers="{
'X-Access-Token': LocalStore.get(TOKEN_KEY),
}"
:maxCount="1"
:showUploadList="false"
@change="uploadChange"
:accept="
modelRef?.file?.fileType
? `.${modelRef?.file?.fileType}`
: '.xlsx'
"
:before-upload="beforeUpload"
:disabled="disabled"
@drop="handleDrop"
>
<div
style="
height: 115px;
line-height: 115px;
color: #666666;
"
>
<!-- <AIcon style="font-size: 20px;" type="PlusCircleOutlined" /> -->
点击或拖拽上传文件
</div>
</a-upload-dragger>
</j-form-item>
<div style="margin-bottom: 16px">
<j-checkbox v-model:checked="modelRef.file.autoDeploy"
>导入并启用</j-checkbox
>
</div>
</j-form>
<div v-if="importLoading" class="result">
<div v-if="flag">
<j-spin size="small" style="margin-right: 10px" />正在导入
</div>
<div v-else>
<AIcon
style="color: #08e21e; margin-right: 10px; font-size: 16px"
type="CheckCircleOutlined"
/>
</div>
<div>导入成功{{ count }} </div>
<div>
导入失败<span style="color: #ff595e">{{ errCount }}</span>
<a v-if="errMessage && !flag && errCount > 0" style="margin-left: 20px" @click="downError">下载</a>
</div>
</div> </div>
</j-form-item> -->
<j-form-item label='下载模板'>
<div class='file-download'>
<j-button @click="downFile('xlsx')">模板格式.xlsx</j-button>
<j-button @click="downFile('csv')">模板格式.csv</j-button>
</div>
</j-form-item>
<j-row type="flex">
<j-col :flex="600">
<j-form-item label="文件上传">
<a-upload-dragger
v-model:fileList="modelRef.upload"
name="file"
:action="FILE_UPLOAD"
:headers="{
'X-Access-Token': LocalStore.get(TOKEN_KEY),
}"
:maxCount="1"
:showUploadList="false"
@change="uploadChange"
:accept="
modelRef?.file?.fileType ? `.${modelRef?.file?.fileType}` : '.xlsx'
"
:before-upload="beforeUpload"
:disabled='disabled'
@drop="handleDrop"
>
<div style="height: 115px; line-height: 115px;">
<template v-if="!modelRef.upload.length">
点击或拖拽上传文件
</template>
<template v-else>
重传
</template>
</div>
</a-upload-dragger>
</j-form-item>
</j-col>
<j-col flex="auto">
<j-form-item>
<a-checkbox style="margin: 30px 0 0 30px;" v-model:checked="modelRef.file.autoDeploy">导入并启用</a-checkbox>
</j-form-item>
</j-col>
</j-row>
</j-form>
<div v-if="importLoading">
<!-- <j-badge v-if="flag" status="processing" text="正在导入" />
<j-badge v-else status="success" text="导入完成" /> -->
<div v-if="flag">正在导入</div>
<div v-else>导入完成</div>
<!-- <span>总数量{{ count }}</span>
<p style="color: red">{{ errMessage }}</p> -->
<div>导入成功{{ count }} </div>
<div>导入失败{{ count }} <a style="margin-left: 20px;">下载</a></div>
</div> </div>
</div>
</template> </template>
<script setup lang='ts' name='DeviceImportFile'> <script setup lang='ts' name='DeviceImportFile'>
@ -72,129 +70,143 @@ import { FILE_UPLOAD } from '@/api/comm';
import { TOKEN_KEY } from '@/utils/variable'; import { TOKEN_KEY } from '@/utils/variable';
import { LocalStore, onlyMessage } from '@/utils/comm'; import { LocalStore, onlyMessage } from '@/utils/comm';
import { downloadFileByUrl } from '@/utils/utils'; import { downloadFileByUrl } from '@/utils/utils';
import { import { deviceImport, templateDownload } from '@/api/device/instance';
deviceImport,
templateDownload,
} from '@/api/device/instance';
import { EventSourcePolyfill } from 'event-source-polyfill'; import { EventSourcePolyfill } from 'event-source-polyfill';
import { message } from 'jetlinks-ui-components' import { message } from 'jetlinks-ui-components';
const props = defineProps({ const props = defineProps({
product: { product: {
type: String, type: String,
default: undefined default: undefined,
} },
}) });
const modelRef = reactive({ const modelRef = reactive({
product: props.product, product: props.product,
upload: [], upload: [],
file: { file: {
fileType: 'xlsx', fileType: 'xlsx',
autoDeploy: false, autoDeploy: false,
}, },
}); });
const importLoading = ref<boolean>(false); const importLoading = ref<boolean>(false);
const flag = ref<boolean>(false); const flag = ref<boolean>(false);
const count = ref<number>(0); const count = ref<number>(0);
const errCount = ref<number>(0);
const errMessage = ref<string>(''); const errMessage = ref<string>('');
const disabled = ref(false) const disabled = ref(false);
const downFile = async (type: string) => { const downFile = async (type: string) => {
const res: any = await templateDownload(props.product!, type); const res: any = await templateDownload(props.product!, type);
if (res) { if (res) {
const blob = new Blob([res], { type: type }); const blob = new Blob([res], { type: type });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
downloadFileByUrl(url, `设备导入模板`, type); downloadFileByUrl(url, `设备导入模板`, type);
} }
}; };
const beforeUpload = (_file: any) => { const beforeUpload = (_file: any) => {
const fileType = modelRef.file?.fileType === 'csv' ? 'csv' : 'xlsx'; const fileType = modelRef.file?.fileType === 'csv' ? 'csv' : 'xlsx';
const isCsv = _file.type === 'text/csv'; const isCsv = _file.type === 'text/csv';
const isXlsx = _file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; const isXlsx =
if (!isCsv && fileType !== 'xlsx') { _file.type ===
onlyMessage('请上传.csv格式文件', 'warning'); 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
} if (!isCsv && fileType !== 'xlsx') {
if (!isXlsx && fileType !== 'csv') { onlyMessage('请上传.csv格式文件', 'warning');
onlyMessage('请上传.xlsx格式文件', 'warning'); }
} if (!isXlsx && fileType !== 'csv') {
return (isCsv && fileType !== 'xlsx') || (isXlsx && fileType !== 'csv'); onlyMessage('请上传.xlsx格式文件', 'warning');
}
return (isCsv && fileType !== 'xlsx') || (isXlsx && fileType !== 'csv');
}; };
const handleDrop = () => { const handleDrop = () => {};
const downError = () => {
window.open(errMessage.value)
} }
const submitData = async (fileUrl: string) => { const submitData = async (fileUrl: string) => {
if (!!fileUrl) { if (!!fileUrl) {
count.value = 0; count.value = 0;
errMessage.value = ''; errCount.value = 0;
const autoDeploy = !!modelRef?.file?.autoDeploy || false; const autoDeploy = !!modelRef?.file?.autoDeploy || false;
importLoading.value = true; importLoading.value = true;
let dt = 0; let dt = 0;
const source = new EventSourcePolyfill( let et = 0;
deviceImport(props.product!, fileUrl, autoDeploy), const source = new EventSourcePolyfill(
); deviceImport(props.product!, fileUrl, autoDeploy),
source.onmessage = (e: any) => { );
const res = JSON.parse(e.data); source.onmessage = (e: any) => {
if (res.success) { const res = JSON.parse(e.data);
const temp = res.result.total; if (res.success) {
dt += temp; const temp = res.result.total;
count.value = dt; dt += temp;
} else { count.value = dt;
errMessage.value = res.message || '失败'; } else {
} if (res.detailFile) {
disabled.value = false errMessage.value = res.detailFile
}; } else {
source.onerror = (e: { status: number }) => { et += 1;
if (e.status === 403) errMessage.value = '暂无权限,请联系管理员'; errCount.value = et;
flag.value = false; }
disabled.value = false }
source.close(); disabled.value = false;
}; };
source.onopen = () => {}; source.onerror = (e: { status: number }) => {
} else { if (e.status === 403) errMessage.value = '暂无权限,请联系管理员';
message.error('请先上传文件'); flag.value = false;
} disabled.value = false;
source.close();
};
source.onopen = () => {};
} else {
message.error('请先上传文件');
}
}; };
const uploadChange = async (info: Record<string, any>) => { const uploadChange = async (info: Record<string, any>) => {
disabled.value = true disabled.value = true;
// console.log(info.file) if (info.file.status === 'done') {
if (info.file.status === 'done') { const resp: any = info.file.response || { result: '' };
const resp: any = info.file.response || { result: '' }; await submitData(resp?.result || '');
await submitData(resp?.result || ''); }
}
}; };
</script> </script>
<style scoped lang='less'> <style scoped lang='less'>
.file { .file {
.file-type-label { .file-type-label {
display: flex; display: flex;
gap: 16px; gap: 16px;
align-items: center; align-items: center;
.file-type-radio { .file-type-radio {
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
:deep(.ant-radio-button-wrapper) { :deep(.ant-radio-button-wrapper) {
width: 50%; width: 50%;
} }
}
} }
}
.file-download { .file-download {
display: flex; display: flex;
gap: 16px; gap: 16px;
>button { > button {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
}
}
.result {
div {
margin: 5px 0;
color: #605e5c;
}
} }
}
} }
</style> </style>

View File

@ -172,7 +172,7 @@ const saveImage = (url: string) => {
width: @with; width: @with;
height: @height; height: @height;
overflow: hidden; overflow: hidden;
//border-radius: 50%; border-radius: 5px;
// border: @border; // border: @border;
transition: all 0.3s; transition: all 0.3s;
@ -197,9 +197,9 @@ const saveImage = (url: string) => {
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(#000, 0.06); // background-color: rgba(#000, 0.06);
cursor: pointer; cursor: pointer;
padding: 8px; // padding: 8px;
.upload-image-mask { .upload-image-mask {
.flex-center(); .flex-center();
@ -211,7 +211,7 @@ const saveImage = (url: string) => {
width: 100%; width: 100%;
height: 100%; height: 100%;
color: #fff; color: #fff;
font-size: 16px; font-size: 14px;
background-color: @mask-color; background-color: @mask-color;
} }

View File

@ -47,7 +47,7 @@ const props = defineProps({
}, },
photoUrl: { photoUrl: {
type: String, type: String,
default: getImage('/apply/provider1.png'), default: getImage('/apply/internal-standalone.png'),
}, },
options: { options: {
type: Array as PropType<any[]>, type: Array as PropType<any[]>,
@ -58,12 +58,12 @@ const props = defineProps({
const emit = defineEmits(['update:value', 'update:photoUrl']); const emit = defineEmits(['update:value', 'update:photoUrl']);
const defaultImg = { const defaultImg = {
'internal-standalone': getImage('/apply/provider1.png'), 'internal-standalone': getImage('/apply/internal-standalone.png'),
'internal-integrated': getImage('/apply/provider2.png'), 'internal-integrated': getImage('/apply/internal-integrated.png'),
'wechat-webapp': getImage('/apply/provider4.png'), 'wechat-webapp': getImage('/apply/wechat-webapp.png'),
'dingtalk-ent-app': getImage('/apply/provider3.png'), 'dingtalk-ent-app': getImage('/apply/dingtalk-ent-app.png'),
'third-party': getImage('/apply/provider5.png'), 'third-party': getImage('/apply/third-party.png'),
'wechat-miniapp': getImage('/apply/provider1.png'), 'wechat-miniapp': getImage('/apply/wechat-miniapp.png'),
} }
const urlValue = ref<any>({...defaultImg}); const urlValue = ref<any>({...defaultImg});

View File

@ -1427,7 +1427,7 @@ const loading = ref<boolean>(false);
const initForm: formType = { const initForm: formType = {
name: '', name: '',
provider: 'internal-standalone', provider: 'internal-standalone',
logoUrl: getImage('/apply/provider1.png'), logoUrl: getImage('/apply/internal-standalone.png'),
integrationModes: [], integrationModes: [],
description: '', description: '',
page: { page: {

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="child-item"> <div class="child-item" :class="{ border: !isLast }">
<div class="child-item-left"> <div class="child-item-left">
<div style="font-weight: 600"> <div style="color: #333333">
{{ data?.name }} {{ data?.name }}
</div> </div>
<div> <div>
@ -14,7 +14,7 @@
</j-tooltip> </j-tooltip>
</div> </div>
</div> </div>
<div class="child-item-right" v-if="checked"> <div class="child-item-right" :class="{ 'disabled': !checked }">
<MCarousel :data="data?.channels"> <MCarousel :data="data?.channels">
<template #card="slotProps"> <template #card="slotProps">
<div class="box-item"> <div class="box-item">
@ -74,29 +74,22 @@
</div> </div>
</div> </div>
</template> </template>
<template #add>
<div class="box-item">
<div class="box-item-img">
<j-tooltip
:title="!add ? '暂无权限,请联系管理员' : ''"
>
<j-button
:disabled="!add"
type="text"
@click="onAdd"
>
<AIcon
style="font-size: 20px"
type="PlusOutlined"
/>
</j-button>
</j-tooltip>
</div>
<div class="box-item-text"></div>
</div>
</template>
</MCarousel> </MCarousel>
<div class="box-item-add">
<div class="box-item-img">
<j-tooltip :title="!add ? '暂无权限,请联系管理员' : ''">
<j-button :disabled="!add" type="text" @click="onAdd">
<AIcon
style="font-size: 20px"
type="PlusOutlined"
/>
</j-button>
</j-tooltip>
</div>
<div class="box-item-text"></div>
</div>
<div class="child-item-right-auth"> <div class="child-item-right-auth">
<j-tooltip :title="!update ? '暂无权限,请联系管理员' : ''"> <j-tooltip :title="!update ? '暂无权限,请联系管理员' : ''">
<j-button :disabled="!update" type="text" @click="onAuth"> <j-button :disabled="!update" type="text" @click="onAuth">
@ -156,6 +149,10 @@ const props = defineProps({
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
isLast: {
type: Boolean,
default: false,
},
}); });
const emits = defineEmits(['refresh']); const emits = defineEmits(['refresh']);
@ -345,9 +342,7 @@ const onSave = (_data: any) => {
<style lang="less" scoped> <style lang="less" scoped>
.child-item { .child-item {
padding: 10px 20px; padding: 0 20px;
margin: 5px;
background: #f7f7f7;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
@ -368,14 +363,15 @@ const onSave = (_data: any) => {
.child-item-right { .child-item-right {
display: flex; display: flex;
align-items: center;
.box-item { .box-item {
margin-left: 10px; margin-left: 10px;
cursor: pointer; cursor: pointer;
.box-item-img { .box-item-img {
background-color: #fff; background-color: #fff;
width: 48px; width: 32px;
height: 48px; height: 32px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -384,10 +380,21 @@ const onSave = (_data: any) => {
.box-item-text { .box-item-text {
width: 100%; width: 100%;
text-align: center; text-align: center;
height: 20px; color: #666666;
font-size: 12px;
} }
} }
.box-item-add {
cursor: pointer;
margin-left: 16px;
background-color: #F8F9FC;
width: 54px;
height: 54px;
display: flex;
align-items: center;
}
.child-item-right-auth { .child-item-right-auth {
margin-left: 20px; margin-left: 20px;
@ -409,6 +416,14 @@ const onSave = (_data: any) => {
} }
} }
} }
&.disabled {
filter: grayscale(100%);
}
}
&.border {
box-shadow: 0px 1px 0px 0px #e2e2e2;
} }
} }
</style> </style>

View File

@ -1,12 +1,12 @@
import { getImage } from "@/utils/comm"; import { getImage } from "@/utils/comm";
const iconMap = new Map(); const iconMap = new Map();
iconMap.set('notifier-dingTalk', getImage('/notice/dingtalk.png')); iconMap.set('notifier-dingTalk', getImage('/notice-rule/dingtalk.png'));
iconMap.set('notifier-weixin', getImage('/notice/wechat.png')); iconMap.set('notifier-weixin', getImage('/notice-rule/wechat.png'));
iconMap.set('notifier-email', getImage('/notice/email.png')); iconMap.set('notifier-email', getImage('/notice-rule/email.png'));
iconMap.set('notifier-voice', getImage('/notice/voice.png')); iconMap.set('notifier-voice', getImage('/notice-rule/voice.png'));
iconMap.set('notifier-sms', getImage('/notice/sms.png')); iconMap.set('notifier-sms', getImage('/notice-rule/sms.png'));
iconMap.set('inside-mail', getImage('/notice/inside-mail.png')); iconMap.set('inside-mail', getImage('/notice-rule/inside-mail.png'));
const noticeType = new Map(); const noticeType = new Map();
noticeType.set('notifier-dingTalk', 'dingTalk'); noticeType.set('notifier-dingTalk', 'dingTalk');

View File

@ -9,8 +9,8 @@
</div> </div>
</div> </div>
<div class="content-collapse"> <div class="content-collapse">
<j-collapse :bordered="false" v-model:activeKey="activeKey"> <j-collapse :bordered="false" v-model:activeKey="activeKey" expand-icon-position="right">
<template #expandIcon="{ isActive }"> <!-- <template #expandIcon="{ isActive }">
<AIcon <AIcon
type="CaretRightOutlined" type="CaretRightOutlined"
:style="{ :style="{
@ -19,23 +19,22 @@
}deg)`, }deg)`,
}" }"
/> />
</template> </template> -->
<j-collapse-panel <j-collapse-panel
v-for="item in dataSource" v-for="item in dataSource"
:key="item.provider" :key="item.provider"
class="custom" class="custom"
:header="item.name"
> >
<template #header
><h3>{{ item.name }}</h3></template
>
<div class="child"> <div class="child">
<template <template
v-for="child in item.children" v-for="(child, index) in item.children"
:key="child.provider" :key="child.provider"
> >
<Item <Item
:data="data.find(i => i?.provider === child?.provider)" :data="data.find(i => i?.provider === child?.provider)"
@refresh="onRefresh" @refresh="onRefresh"
:isLast="index === item.children?.length"
/> />
</template> </template>
</div> </div>
@ -156,13 +155,19 @@ onMounted(() => {
background-color: #f6f6f6; background-color: #f6f6f6;
} }
.custom { .custom {
background: #f7f7f7; background: #F7F8FA;
border-radius: 4px;
border: 0; border: 0;
overflow: hidden; overflow: hidden;
color: #333333;
} }
.child { .child {
background-color: white; background-color: white;
padding: 10px; padding-bottom: 24px;
}
.content-collapse {
:deep(.ant-collapse-content > .ant-collapse-content-box) {
padding: 0;
}
} }
</style> </style>

View File

@ -239,12 +239,13 @@ const loading = ref(false);
const bindings = ref<any[]>(); const bindings = ref<any[]>();
// const basis = ref<any>({}); // const basis = ref<any>({});
const defaultImg = getImage('/apply/provider1.png'); const defaultImg = getImage('/apply/internal-standalone.png');
const iconMap = new Map(); const iconMap = new Map();
iconMap.set('dingtalk-ent-app', getImage('/bind/dingtalk.png')); iconMap.set('dingtalk-ent-app', getImage('/bind/dingtalk.png'));
iconMap.set('wechat-webapp', getImage('/bind/wechat-webapp.png')); iconMap.set('wechat-webapp', getImage('/bind/wechat-webapp.png'));
iconMap.set('internal-standalone', getImage('/apply/provider1.png')); iconMap.set('internal-standalone', getImage('/apply/internal-standalone.png'));
iconMap.set('third-party', getImage('/apply/provider5.png')); iconMap.set('third-party', getImage('/apply/third-party.png'));
iconMap.set('wechat-miniapp', getImage('/apply/wechat-miniapp.png'));
const onFinish = async () => { const onFinish = async () => {
try { try {