diff --git a/package.json b/package.json index 387d6753..c3cc14ad 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "event-source-polyfill": "^1.0.31", "global": "^4.4.0", "jetlinks-store": "^0.0.3", - "jetlinks-ui-components": "^1.0.1", + "jetlinks-ui-components": "^1.0.3", "js-cookie": "^3.0.1", "less": "^4.1.3", "less-loader": "^11.1.0", diff --git a/src/api/data-collect/channel.ts b/src/api/data-collect/channel.ts new file mode 100644 index 00000000..08bbbd8c --- /dev/null +++ b/src/api/data-collect/channel.ts @@ -0,0 +1,27 @@ +import server from '@/utils/request'; + +export const query = (data: any) => + server.post(`/data-collect/channel/_query`, data); + +export const remove = (id: string) => + server.remove(`/data-collect/channel/${id}`); + +export const save = (data: any) => server.post(`/data-collect/channel`, data); + +export const update = (id: string, data: any) => + server.put(`/data-collect/channel/${id}`, data); + +export const getProviders = () => server.get(`/gateway/device/providers`); + +export const queryOptionsList = (type: strimg) => + server.get(`/data-collect/opc/${type}`); + +export const validateField = (data: any) => + server.post(`/data-collect/opc/endpoint/_validate`, data, null, { + headers: { + 'Content-Type': 'text/plain;charset=UTF-8', + }, + }); + +export const queryCertificateList = () => + server.get(`/network/certificate/_query/no-paging?paging=false`, {}); diff --git a/src/api/data-collect/dashboard.ts b/src/api/data-collect/dashboard.ts new file mode 100644 index 00000000..ceb68ca0 --- /dev/null +++ b/src/api/data-collect/dashboard.ts @@ -0,0 +1,6 @@ +import server from '@/utils/request'; + +export const queryCount = (type: string, data: any) => + server.post(`/data-collect/${type}/_count`, data); + +export const dashboard = (data: any) => server.post(`/dashboard/_multi`, data); diff --git a/src/api/login.ts b/src/api/login.ts index 6391c6b8..fd3eed95 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -51,4 +51,9 @@ export const settingDetail = (scopes: string) => server.get(`/system/config/${sc /** * 获取当前登录用户信息 */ -export const userDetail = () => server.get('/user/detail') \ No newline at end of file +export const userDetail = () => server.get('/user/detail') + +/** + * 退出登录 + */ +export const loginout_api = () => server.get('/user-token/reset') diff --git a/src/api/media/cascade.ts b/src/api/media/cascade.ts index c8ba0a56..9881f689 100644 --- a/src/api/media/cascade.ts +++ b/src/api/media/cascade.ts @@ -35,8 +35,12 @@ export default { // 更改国标ID updateGbChannelId: (id: string, data: any): any => server.put(`/media/gb28181-cascade/binding/${id}`, data), // 查询通道分页列表 - queryChannelList: (data: any): any => server.post(`media/channel/_query`, data), + queryChannelList: (data: any): any => server.post(`/media/channel/_query`, data), // 推送 - publish: (id: string, params: any) => server.get(`/media/gb28181-cascade/${id}/bindings/publish`, params) + publish: (id: string, params: any) => server.get(`/media/gb28181-cascade/${id}/bindings/publish`, params), + + // 分屏展示接口 + // 设备树 + getMediaTree: (data?: any) => server.post(`/media/device/_query/no-paging`, data), } \ No newline at end of file diff --git a/src/components/AIcon/index.tsx b/src/components/AIcon/index.tsx index 6f2e9734..dd5e389f 100644 --- a/src/components/AIcon/index.tsx +++ b/src/components/AIcon/index.tsx @@ -70,6 +70,10 @@ const iconKeys = [ 'CaretDownOutlined', 'MinusOutlined', 'AudioOutlined', + 'BellOutlined', + 'UserOutlined', + 'LogoutOutlined', + 'ReadIconOutlined' ] const Icon = (props: {type: string}) => { diff --git a/src/components/Layout/BasicLayoutPage.vue b/src/components/Layout/BasicLayoutPage.vue index 59079f75..7d00fd37 100644 --- a/src/components/Layout/BasicLayoutPage.vue +++ b/src/components/Layout/BasicLayoutPage.vue @@ -1,84 +1,102 @@ - - + diff --git a/src/components/Layout/components/Notice.vue b/src/components/Layout/components/Notice.vue new file mode 100644 index 00000000..509c502a --- /dev/null +++ b/src/components/Layout/components/Notice.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/src/components/Layout/components/NoticeInfo.vue b/src/components/Layout/components/NoticeInfo.vue new file mode 100644 index 00000000..1ea0b308 --- /dev/null +++ b/src/components/Layout/components/NoticeInfo.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/components/Layout/components/UserInfo.vue b/src/components/Layout/components/UserInfo.vue new file mode 100644 index 00000000..e0a2b4dc --- /dev/null +++ b/src/components/Layout/components/UserInfo.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/src/components/Layout/components/nodata.svg b/src/components/Layout/components/nodata.svg new file mode 100644 index 00000000..4fb16097 --- /dev/null +++ b/src/components/Layout/components/nodata.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Player/ScreenPlayer.vue b/src/components/Player/ScreenPlayer.vue new file mode 100644 index 00000000..1e8e38f4 --- /dev/null +++ b/src/components/Player/ScreenPlayer.vue @@ -0,0 +1,466 @@ + + + + + + diff --git a/src/components/Player/index.less b/src/components/Player/index.less new file mode 100644 index 00000000..b199fe65 --- /dev/null +++ b/src/components/Player/index.less @@ -0,0 +1,82 @@ +.live-player-warp { + display: flex; + + .live-player-content { + display: flex; + flex: 1; + flex-direction: column; + + .player-screen-tool { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; + + .ant-radio-button-wrapper { + height: auto; + padding: 4px 20px; + } + } + + .player-body { + flex: 1; + + .player-screen { + position: relative; + display: grid; + box-sizing: border-box; + + &.screen-1 { + grid-template-columns: 1fr; + } + + &.screen-4 { + grid-template-rows: 1fr 1fr; + grid-template-columns: 1fr 1fr; + } + + &.screen-9 { + grid-template-rows: 1fr 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr; + } + + &.screen-4, + &.screen-9 { + grid-gap: 12px; + } + + .active { + border: 2px solid red; + } + + .full-screen { + border: 1px solid #fff; + } + + .player-screen-item { + position: relative; + + .media-btn-refresh { + position: absolute; + top: 4px; + right: 4px; + z-index: 2; + padding: 2px 4px; + font-size: 12px; + background-color: #f0f0f0; + border-radius: 2px; + cursor: pointer; + + &:hover { + background-color: #d9d9d9; + } + + &:active { + background-color: #bfbfbf; + } + } + } + } + } + } +} diff --git a/src/components/Player/index.vue b/src/components/Player/index.vue index 1eff1f56..ef5d2bcf 100644 --- a/src/components/Player/index.vue +++ b/src/components/Player/index.vue @@ -10,6 +10,8 @@ import vue3videoPlay from 'vue3-video-play'; const props = defineProps({ src: { type: String, default: '' }, type: { type: String, default: 'mp4' }, + width: { type: String, default: '500px' }, + height: { type: String, default: '280px' }, }); watch( @@ -21,8 +23,6 @@ watch( const options = reactive({ ...props, - width: '500px', //播放器高度 - height: '280px', //播放器高度 color: '#409eff', //主题色 title: '', //视频名称 // src: props.src, diff --git a/src/router/menu.ts b/src/router/menu.ts index f849eee7..3e2b8935 100644 --- a/src/router/menu.ts +++ b/src/router/menu.ts @@ -1,5 +1,49 @@ export const LoginPath = '/login' +export const AccountMenu = { + path: '/account', + component: () => import('@/components/Layout/BasicLayoutPage.vue'), + redirect: '/account/center', + name: 'account', + meta: { + title: '个人中心', + icon: '', + hideInMenu: true + }, + children: [ + { + path: '/account/center', + name: 'account/center', + meta: { + title: '基本设置', + icon: '', + hideInMenu: false + }, + component: () => import('@/views/account/Center/index.vue') + }, + { + path: '/account/NotificationSubscription', + name: 'account/NotificationSubscription', + meta: { + title: '通知订阅', + icon: '', + hideInMenu: false + }, + component: () => import('@/views/account/NotificationSubscription/index.vue') + }, + { + path: '/account/NotificationRecord', + name: 'account/NotificationRecord', + meta: { + title: '通知记录', + icon: '', + hideInMenu: false + }, + component: () => import('@/views/account/NotificationRecord/index.vue') + }, + ] +} + export default [ { path: '/*', redirect: '/'}, // start: 测试用, 可删除 @@ -27,18 +71,6 @@ export default [ path: '/system/Api', component: () => import('@/views/system/Platforms/index.vue') }, - { - path: '/account/center', - component: () => import('@/views/account/Center/index.vue') - }, - { - path: '/account/NotificationSubscription', - component: () => import('@/views/account/NotificationSubscription/index.vue') - }, - { - path: '/account/NotificationRecord', - component: () => import('@/views/account/NotificationRecord/index.vue') - }, // end: 测试用, 可删除 // 初始化 diff --git a/src/store/menu.ts b/src/store/menu.ts index 49445fb8..9be0bff5 100644 --- a/src/store/menu.ts +++ b/src/store/menu.ts @@ -4,8 +4,8 @@ import { filterAsnycRouter, MenuItem } from '@/utils/menu' import { isArray } from 'lodash-es' import { usePermissionStore } from './permission' import router from '@/router' -import { message } from 'ant-design-vue' import { onlyMessage } from '@/utils/comm' +import { AccountMenu } from '@/router/menu' const defaultOwnParams = [ { @@ -115,8 +115,11 @@ export const useMenuStore = defineStore({ hideInMenu: true } }) + menusData.push(AccountMenu) + silderMenus.push(AccountMenu) this.siderMenus = silderMenus console.log('menusData', menusData) + console.log('silderMenus', silderMenus) res(menusData) } }) diff --git a/src/store/userInfo.ts b/src/store/userInfo.ts index 3a6de12e..6fe3c602 100644 --- a/src/store/userInfo.ts +++ b/src/store/userInfo.ts @@ -20,7 +20,9 @@ export const useUserInfo = defineStore('userInfo', { token: '', user: {}, }, + alarmUpdateCount: 0 }), + actions: { login(userInfo: any) { const username = userInfo.userName.trim(); @@ -49,6 +51,9 @@ export const useUserInfo = defineStore('userInfo', { } }).catch(() => rej()) }) + }, + updateAlarm(){ + this.alarmUpdateCount += 1 } }, }); diff --git a/src/utils/comm.ts b/src/utils/comm.ts index 25e54bab..85f9273f 100644 --- a/src/utils/comm.ts +++ b/src/utils/comm.ts @@ -1,6 +1,6 @@ import type { Slots } from 'vue' import { TOKEN_KEY } from '@/utils/variable' -import { message } from 'ant-design-vue' +import { message } from 'jetlinks-ui-components'; /** * 静态图片资源处理 diff --git a/src/utils/request.ts b/src/utils/request.ts index 9e43cb5b..5eaf3044 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -24,12 +24,17 @@ export const request = axios.create({ * @param {String} url * @param {Object} [data] * @param {String} responseType 如果接口是需要导出文件流,那么responseType = 'blob' + * @param {Object|String} [ext] 扩展参数,如果是配置headers,ext对象内包含headers对象,如下 + * { + headers: {'Content-Type': 'text/plain;charset=UTF-8'}, + } * @returns {AxiosInstance} */ -export const post = function(url: string, data = {}, params = {}) { - params = typeof params === 'string' ? { responseType: params } : params +export const post = function(url: string, data = {}, params = {}, ext={}) { + ext = typeof ext === 'string' ? { responseType: ext } : ext return request>({ - ...params, + ...ext, + params, method: 'POST', url, data diff --git a/src/views/DataCollect/Channel/Save/index.vue b/src/views/DataCollect/Channel/Save/index.vue new file mode 100644 index 00000000..350091e2 --- /dev/null +++ b/src/views/DataCollect/Channel/Save/index.vue @@ -0,0 +1,315 @@ + + + + diff --git a/src/views/DataCollect/Channel/data.ts b/src/views/DataCollect/Channel/data.ts new file mode 100644 index 00000000..27c3d759 --- /dev/null +++ b/src/views/DataCollect/Channel/data.ts @@ -0,0 +1,141 @@ +import { validateField } from '@/api/data-collect/channel'; +import { FormDataType } from './type.d'; + +export const FormState: FormDataType = { + name: '', + provider: undefined, + configuration: { + host: '', + port: '502', + endpoint: '', + securityPolicy: undefined, + securityMode: undefined, + certificate: undefined, + authType: undefined, + username: '', + password: '', + }, + description: '', +}; + +export const StatusColorEnum = { + running: 'success', + disabled: 'error', + partialError: 'processing', + failed: 'warning', + stopped: 'default', +}; +export const updateStatus = { + disabled: { + state: 'enabled', + runningState: 'running', + }, + enabled: { + state: 'disabled', + runningState: 'stopped', + }, +}; + +export const TiTlePermissionButtonStyle = { + padding: 0, + color: ' #1890ff !important', + 'font-weight': 700, + 'font-size': '16px', + overflow: 'hidden', + 'text-overflow': 'ellipsis', + 'white-space': 'nowrap', + width: 'calc(100%-100px)', + // width: '60%', +}; + +export const regOnlyNumber = new RegExp(/^\d+$/); + +export const regIP = new RegExp( + /^([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/, +); +export const regIPv6 = new RegExp( + /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/, +); +export const regDomain = new RegExp( + /([0-9a-z-]{2,}\.[0-9a-z-]{2,3}\.[0-9a-z-]{2,3}|[0-9a-z-]{2,}\.[0-9a-z-]{2,3})$/i, +); +export const checkEndpoint = (_rule: Rule, value: string): Promise => + new Promise(async (resolve, reject) => { + if (value) { + const res = await validateField(value); + return res.result.passed ? resolve('') : reject(res.result.reason); + } + }); +export const FormValidate = { + name: [ + { required: true, message: '请输入名称', trigger: 'blur' }, + { max: 64, message: '最多可输入64个字符' }, + ], + provider: [{ required: true, message: '请选择通讯协议' }], + host: [ + { + required: true, + message: '请输入Modbus主机IP', + }, + { + pattern: regIP || regIPv6 || regDomain, + message: '请输入正确格式的Modbus主机IP地址', + }, + ], + port: [ + { + required: true, + message: '请输入端口', + }, + { + pattern: regOnlyNumber, + message: '请输入1-65535之间的正整数', + }, + ], + + endpoint: [ + { + required: true, + message: '请输入端点url', + }, + { + validator: checkEndpoint, + trigger: 'blur', + }, + ], + + securityPolicy: [ + { + required: true, + message: '请选择安全策略', + }, + ], + securityMode: [ + { + required: true, + message: '请选择安全模式', + }, + ], + certificate: [ + { + required: true, + message: '请选择证书', + }, + ], + authType: [ + { + required: true, + message: '请选择权限认证', + }, + ], + username: [ + { required: true, message: '请输入用户名', trigger: 'blur' }, + { max: 64, message: '最多可输入64个字符' }, + ], + password: [ + { required: true, message: '请输入密码', trigger: 'blur' }, + { max: 64, message: '最多可输入64个字符' }, + ], + + description: [{ max: 200, message: '最多可输入200个字符' }], +}; diff --git a/src/views/DataCollect/Channel/index.vue b/src/views/DataCollect/Channel/index.vue new file mode 100644 index 00000000..5c6782e5 --- /dev/null +++ b/src/views/DataCollect/Channel/index.vue @@ -0,0 +1,341 @@ + + + diff --git a/src/views/DataCollect/Channel/type.d.ts b/src/views/DataCollect/Channel/type.d.ts new file mode 100644 index 00000000..f4270317 --- /dev/null +++ b/src/views/DataCollect/Channel/type.d.ts @@ -0,0 +1,19 @@ +export interface ConfigurationType { + port: string | undefined; + host: string | undefined;; + username: string; + password: string; + endpoint: string, + securityPolicy: string | undefined, + securityMode: string | undefined, + certificate: string | undefined, + authType: string | undefined, + +} + +export interface FormDataType { + name: string; + provider: string | undefined, + configuration: ConfigurationType; + description?: string; +} diff --git a/src/views/DataCollect/Dashboard/components/Card.vue b/src/views/DataCollect/Dashboard/components/Card.vue new file mode 100644 index 00000000..57448ee1 --- /dev/null +++ b/src/views/DataCollect/Dashboard/components/Card.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/views/DataCollect/Dashboard/components/TopCard.vue b/src/views/DataCollect/Dashboard/components/TopCard.vue new file mode 100644 index 00000000..1138eae1 --- /dev/null +++ b/src/views/DataCollect/Dashboard/components/TopCard.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/src/views/DataCollect/Dashboard/index.less b/src/views/DataCollect/Dashboard/index.less new file mode 100644 index 00000000..9709ead9 --- /dev/null +++ b/src/views/DataCollect/Dashboard/index.less @@ -0,0 +1,38 @@ +.media-dash-board { + .top-card-items { + margin-bottom: 12px; + height: 100px; + .top-card-item { + width: 25%; + padding: 6px 24px; + border: 1px solid #e3e3e3; + + .top-card-top { + display: flex; + padding: 12px 0; + + .top-card-top-left { + width: 80px; + } + + .top-card-top-right { + .top-card-total { + font-weight: bold; + font-size: 20px; + } + } + } + + .top-card-bottom { + display: flex; + justify-content: space-between; + padding: 12px 0; + border-top: 1px solid #e3e3e3; + } + } + } + + .media-dash-board-body { + border: 1px solid #f0f0f0; + } +} diff --git a/src/views/DataCollect/Dashboard/index.vue b/src/views/DataCollect/Dashboard/index.vue new file mode 100644 index 00000000..adacc21f --- /dev/null +++ b/src/views/DataCollect/Dashboard/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/views/DataCollect/Dashboard/tool.ts b/src/views/DataCollect/Dashboard/tool.ts new file mode 100644 index 00000000..076c6947 --- /dev/null +++ b/src/views/DataCollect/Dashboard/tool.ts @@ -0,0 +1,155 @@ +import moment from 'moment'; +import * as echarts from 'echarts'; + +const getParams = (dt: any) => { + switch (dt.type) { + case 'today': + return { + limit: 24, + interval: '1h', + format: 'HH:mm', + }; + case 'week': + return { + limit: 7, + interval: '1d', + format: 'MM-dd', + }; + case 'hour': + return { + limit: 60, + interval: '1m', + format: 'HH:mm', + }; + default: + const time = dt.end - dt.start; + const hour = 60 * 60 * 1000; + const days = hour * 24; + const year = days * 365; + if (time <= hour) { + return { + limit: Math.abs(Math.ceil(time / (60 * 60))), + interval: '1m', + format: 'HH:mm', + }; + } else if (time > hour && time <= days) { + return { + limit: Math.abs(Math.ceil(time / hour)), + interval: '1h', + format: 'HH:mm', + }; + } else if (time >= year) { + return { + limit: Math.abs(Math.ceil(time / days / 31)) + 1, + interval: '1M', + format: 'yyyy年-M月', + }; + } else { + return { + limit: Math.abs(Math.ceil(time / days)) + 1, + interval: '1d', + format: 'MM-dd', + }; + } + } +}; + +export const getTimeByType = (type) => { + switch (type) { + case 'hour': + return moment().subtract(1, 'hours'); + case 'week': + return moment().subtract(6, 'days'); + case 'month': + return moment().subtract(29, 'days'); + case 'year': + return moment().subtract(365, 'days'); + default: + return moment().startOf('day'); + } +}; + +export const pointParams = (data) => [ + { + dashboard: 'collector', + object: 'pointData', + measurement: 'quantity', + dimension: 'agg', + params: { + limit: getParams(data.time).limit, + from: data.time.start, + to: data.time.end, + interval: getParams(data.time).interval, + format: getParams(data.time).format, + }, + }, +]; + +export const pointOptionsSeries = { + type: 'line', + smooth: true, + color: '#60DFC7', + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: '#60DFC7', // 100% 处的颜色 + }, + { + offset: 1, + color: '#FFFFFF', // 0% 处的颜色 + }, + ], + global: false, // 缺省为 false + }, + }, +}; + +export const defaultParams = { + terms: [ + { + column: 'runningState', + termType: 'not', + value: 'running', + }, + ], +}; + +export const statusData = ref([ + [ + { + type: 'channel', + title: '异常通道', + status: 'error', + label: '通道数量', + value: 0, + total: 0, + }, + ], + [ + { + type: 'collector', + title: '异常采集器', + status: 'error', + label: '采集器数量', + value: 0, + total: 0, + }, + ], + [ + { + type: 'point', + title: '异常点位', + status: 'error', + label: '采集点位', + value: 0, + total: 0, + }, + ], +]); diff --git a/src/views/DataCollect/Dashboard/typings.d.ts b/src/views/DataCollect/Dashboard/typings.d.ts new file mode 100644 index 00000000..dd0a9484 --- /dev/null +++ b/src/views/DataCollect/Dashboard/typings.d.ts @@ -0,0 +1,18 @@ +export type Agg = { + duration: number; + total: number; +}; + +export type AggPlaying = { + playerTotal: number; + playingTotal: number; +}; + +export type Footer = { + title: string; + value: number | string; + total: number | string; + status?: 'default' | 'error' | 'success' | 'warning' | 'processing' | ''; + type: string; + label: string; +}; diff --git a/src/views/account/NotificationRecord/components/ViewDialog.vue b/src/views/account/NotificationRecord/components/ViewDialog.vue index 2085f3b6..d5bc7010 100644 --- a/src/views/account/NotificationRecord/components/ViewDialog.vue +++ b/src/views/account/NotificationRecord/components/ViewDialog.vue @@ -5,50 +5,55 @@ width="1000px" @ok="emits('update:visible', false)" @cancel="emits('update:visible', false)" + class="view-dialog-container" > - 告警设备 - + 告警设备 + {{ data?.targetName || '' }} - 设备ID - + 设备ID + {{ data?.targetId || '' }} - 告警名称 - + 告警名称 + {{ data?.alarmName || data?.alarmConfigName || '' }} - 告警时间 - + 告警时间 + {{ moment(data?.alarmTime).format('YYYY-MM-DD HH:mm:ss') }} - 告警级别 - + 告警级别 + {{ (levelList.length > 0 && getLevelLabel(data.level)) || '' }} - 告警说明 - {{ data?.description || '' }} + 告警说明 + {{ data?.description || '' }} - 告警流水 - - - + 告警流水 + + - + diff --git a/src/views/account/NotificationRecord/index.vue b/src/views/account/NotificationRecord/index.vue index 977ec8b3..ec4167a6 100644 --- a/src/views/account/NotificationRecord/index.vue +++ b/src/views/account/NotificationRecord/index.vue @@ -3,7 +3,7 @@
- - - + - +
@@ -97,7 +89,10 @@ import { optionItem } from '@/views/rule-engine/Scene/typings'; import { dictItemType } from '@/views/system/DataSource/typing'; import moment from 'moment'; import { message } from 'ant-design-vue'; +import NoticeCp from '@/components/Layout/components/Notice.vue'; +import { useUserInfo } from '@/store/userInfo'; +const { updateAlarm } = useUserInfo(); const columns = [ { title: '类型', @@ -181,6 +176,7 @@ const table = { if (resp.status === 200) { message.success('操作成功!'); table.refresh(); + updateAlarm(); } }); }, diff --git a/src/views/account/NotificationSubscription/index.vue b/src/views/account/NotificationSubscription/index.vue index bee5dd42..ba131175 100644 --- a/src/views/account/NotificationSubscription/index.vue +++ b/src/views/account/NotificationSubscription/index.vue @@ -2,7 +2,7 @@
- - + - +
@@ -25,9 +25,9 @@
脚本语言: - - JavaScript(ECMAScript 5) - + + JavaScript(ECMAScript 5) +
@@ -42,7 +42,7 @@ }) }">
- +
@@ -51,25 +51,25 @@
-
运行结果
- +
@@ -87,13 +87,13 @@ 保存 - + diff --git a/src/views/iot-card/Recharge/Detail.vue b/src/views/iot-card/Recharge/Detail.vue index 7e9531fd..5cfd5ee7 100644 --- a/src/views/iot-card/Recharge/Detail.vue +++ b/src/views/iot-card/Recharge/Detail.vue @@ -1,5 +1,5 @@ + + diff --git a/src/views/media/SplitScreen/tree.vue b/src/views/media/SplitScreen/tree.vue new file mode 100644 index 00000000..79d84e52 --- /dev/null +++ b/src/views/media/SplitScreen/tree.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/src/views/rule-engine/Scene/Save/Device/AddModal.vue b/src/views/rule-engine/Scene/Save/Device/AddModal.vue index 349266c3..2521b9a8 100644 --- a/src/views/rule-engine/Scene/Save/Device/AddModal.vue +++ b/src/views/rule-engine/Scene/Save/Device/AddModal.vue @@ -19,7 +19,12 @@
- +
@@ -26,9 +26,10 @@ import AddModel from './AddModal.vue' import AddButton from '../components/AddButton.vue' import Title from '../components/Title.vue' import Action from '../action/index.vue' +import type { TriggerDevice } from '@/views/rule-engine/Scene/typings' const sceneStore = useSceneStore() -const { data } = storeToRefs(sceneStore) +const { data } = storeToRefs(sceneStore) const visible = ref(false) @@ -41,6 +42,11 @@ const rules = [{ }, }] +const save = (device: TriggerDevice, options: Record) => { + data.value.trigger!.device = device + data.value.options!.trigger = options +} + \ No newline at end of file diff --git a/src/views/rule-engine/Scene/Save/action/Device/device/index.vue b/src/views/rule-engine/Scene/Save/action/Device/device/index.vue new file mode 100644 index 00000000..89582ec0 --- /dev/null +++ b/src/views/rule-engine/Scene/Save/action/Device/device/index.vue @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/src/views/rule-engine/Scene/Save/action/Device/index.vue b/src/views/rule-engine/Scene/Save/action/Device/index.vue index 9247a368..91f0afd6 100644 --- a/src/views/rule-engine/Scene/Save/action/Device/index.vue +++ b/src/views/rule-engine/Scene/Save/action/Device/index.vue @@ -1,22 +1,104 @@ \ No newline at end of file + +const stepChange = (step: number) => { + if (step !== 0) { + save(step - 1) + } else { + DeviceModel.current = 0 + } +} + + +const prev = () => { + DeviceModel.current = DeviceModel.current - 1 +} +const saveClick = () => save() + + + \ No newline at end of file diff --git a/src/views/rule-engine/Scene/Save/action/Device/typings.d.ts b/src/views/rule-engine/Scene/Save/action/Device/typings.d.ts new file mode 100644 index 00000000..eeed8153 --- /dev/null +++ b/src/views/rule-engine/Scene/Save/action/Device/typings.d.ts @@ -0,0 +1,28 @@ +import { ProductItem } from '@/views/device/Product/typings'; +import { ActionsDeviceProps } from '../../../typings'; + +type DeviceModelType = { + steps: { + key: string; + title: string; + content: React.ReactNode; + }[]; + current: number; + productId: string; + deviceId: string; + productDetail: ProductItem | any; + device: Partial; + deviceDetail: any; + options: any; + selector: string; + selectorValues: any; + upperKey: string; + source: string; + relationName: string; + message: any; + propertiesName: string; + propertiesValue: string | any; + columns: string[]; + actionName: string; + tagList: any[]; +} \ No newline at end of file diff --git a/src/views/rule-engine/Scene/Save/components/Timer/util.ts b/src/views/rule-engine/Scene/Save/components/Timer/util.ts index 861859ba..369c160e 100644 --- a/src/views/rule-engine/Scene/Save/components/Timer/util.ts +++ b/src/views/rule-engine/Scene/Save/components/Timer/util.ts @@ -1,3 +1,4 @@ +import { isArray } from 'lodash-es' export const numberToString = { 1: '星期一', 2: '星期二', @@ -6,4 +7,46 @@ export const numberToString = { 5: '星期五', 6: '星期六', 7: '星期日', +}; + +export const timeUnitEnum = { + seconds: '秒', + minutes: '分', + hours: '小时', +}; + +type continuousValueFn = (data: (string | number)[], type: string) => (number | string)[]; + +export const continuousValue: continuousValueFn = (data, type) => { + let start = 0; + const newArray: (number | string)[] = []; + const isWeek = type === 'week'; + if (isArray(data)) { + data.forEach((item, index) => { + const _item = Number(item); + const nextValue = data[index + 1]; + const previousValue = data[index - 1]; + const nextItemValue = _item + 1; + const previousItemValue = _item - 1; + if (nextItemValue === nextValue && previousItemValue !== previousValue) { + start = _item; + } else if (previousItemValue === previousValue && nextItemValue !== nextValue) { + // 表示前start和item连续,并且item与nextValue不连续 + if (_item - start >= 2) { + // 至少三位连续 + newArray.push( + isWeek + ? `${numberToString[start]} - ${numberToString[_item]}` + : `${start} - ${_item}号`, + ); + } else { + newArray.push(isWeek ? numberToString[start] : `${start}号`); + newArray.push(isWeek ? numberToString[_item] : `${_item}号`); + } + } else if (previousItemValue !== previousValue && nextItemValue !== nextValue) { + newArray.push(isWeek ? numberToString[_item] : `${_item}号`); + } + }); + } + return newArray; }; \ No newline at end of file diff --git a/src/views/rule-engine/Scene/Save/components/Title.vue b/src/views/rule-engine/Scene/Save/components/Title.vue index daa19758..b4a492d4 100644 --- a/src/views/rule-engine/Scene/Save/components/Title.vue +++ b/src/views/rule-engine/Scene/Save/components/Title.vue @@ -5,13 +5,13 @@
- + {{ options.name }} - + {{ options.extraName }}
-