From 3999e4e589be3f05ca38b292098e95ccf1d2141f Mon Sep 17 00:00:00 2001 From: g7hoo Date: Tue, 20 Sep 2022 15:59:45 +0800 Subject: [PATCH 01/14] =?UTF-8?q?init=20=E6=95=B0=E6=8D=AE=E5=88=86?= =?UTF-8?q?=E6=9E=90=E2=80=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/en.js | 4 + src/i18n/zh-CN.js | 4 + .../monitoring/equipmentEfficiency.vue | 445 ++++++++++++++++++ .../modules/monitoring/equipmentException.vue | 445 ++++++++++++++++++ .../monitoring/equipmentTimesequence.vue | 445 ++++++++++++++++++ 5 files changed, 1343 insertions(+) create mode 100644 src/views/modules/monitoring/equipmentEfficiency.vue create mode 100644 src/views/modules/monitoring/equipmentException.vue create mode 100644 src/views/modules/monitoring/equipmentTimesequence.vue diff --git a/src/i18n/en.js b/src/i18n/en.js index 5be8b88..fdd4cb4 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -17,6 +17,7 @@ t.routes['质量管理'] = 'Quality Management' t.routes['权限管理'] = 'Permission Management' t.routes['系统设置'] = 'System Settings' t.routes['日志管理'] = 'Log Management' +t.routes['数据分析'] = 'Data Analysis' // 二级 t.routes['厂务'] = 'Factory Affair' @@ -42,6 +43,9 @@ t.routes['定时任务'] = 'Timed Tasks' t.routes['文件上传'] = 'File Upload' t.routes['登录日志'] = 'Login Records' t.routes['操作日志'] = 'Oprations Records' +t.routes['设备效率分析'] = 'Equipment Efficiency Analysis' +t.routes['设备异常分析'] = 'Equipment Exceptions Analysis' +t.routes['设备状态时序图'] = 'Equipment Status Timesequence' // 三级 t.routes['工厂'] = 'Factory' diff --git a/src/i18n/zh-CN.js b/src/i18n/zh-CN.js index 6acd25b..9657383 100644 --- a/src/i18n/zh-CN.js +++ b/src/i18n/zh-CN.js @@ -18,6 +18,7 @@ t.routes['质量管理'] = '质量管理' t.routes['权限管理'] = '权限管理' t.routes['系统设置'] = '系统设置' t.routes['日志管理'] = '日志管理' +t.routes['数据分析'] = '数据分析' // 二级 t.routes['厂务'] = '厂务' @@ -43,6 +44,9 @@ t.routes['定时任务'] = '定时任务' t.routes['文件上传'] = '文件上传' t.routes['登录日志'] = '登录日志' t.routes['操作日志'] = '操作日志' +t.routes['设备效率分析'] = '设备效率分析' +t.routes['设备异常分析'] = '设备异常分析' +t.routes['设备状态时序图'] = '设备状态时序图' // 三级 t.routes['工厂'] = '工厂' diff --git a/src/views/modules/monitoring/equipmentEfficiency.vue b/src/views/modules/monitoring/equipmentEfficiency.vue new file mode 100644 index 0000000..87b398b --- /dev/null +++ b/src/views/modules/monitoring/equipmentEfficiency.vue @@ -0,0 +1,445 @@ + + + diff --git a/src/views/modules/monitoring/equipmentException.vue b/src/views/modules/monitoring/equipmentException.vue new file mode 100644 index 0000000..f581385 --- /dev/null +++ b/src/views/modules/monitoring/equipmentException.vue @@ -0,0 +1,445 @@ + + + diff --git a/src/views/modules/monitoring/equipmentTimesequence.vue b/src/views/modules/monitoring/equipmentTimesequence.vue new file mode 100644 index 0000000..d9ca13b --- /dev/null +++ b/src/views/modules/monitoring/equipmentTimesequence.vue @@ -0,0 +1,445 @@ + + + -- 2.45.2 From a03d1db029e453baaf1baa777656f0cbc9272c8a Mon Sep 17 00:00:00 2001 From: g7hoo Date: Wed, 21 Sep 2022 10:15:53 +0800 Subject: [PATCH 02/14] =?UTF-8?q?update=20=E8=AE=BE=E5=A4=87=E6=95=88?= =?UTF-8?q?=E7=8E=87=E5=88=86=E6=9E=90=E5=A4=B4=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/equipmentEfficiency.vue | 122 ++++++++++++++++-- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/src/views/modules/monitoring/equipmentEfficiency.vue b/src/views/modules/monitoring/equipmentEfficiency.vue index 87b398b..956cda2 100644 --- a/src/views/modules/monitoring/equipmentEfficiency.vue +++ b/src/views/modules/monitoring/equipmentEfficiency.vue @@ -2,13 +2,39 @@
+ - + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ $t('search') }} - {{ $t('add') }} - {{ $t('export') }} + @@ -38,6 +64,7 @@ import CKEditor from 'ckeditor4-vue' import { calcMaxHeight } from '@/utils' import { timeFilter } from '@/utils/filters' import Cookies from 'js-cookie' +import moment from 'moment' const tableConfigs = [ { @@ -241,11 +268,21 @@ const addOrUpdateConfigs = { export default { data() { return { + /** hfxny part */ + factoryList: [], + productLineList: [], + /** */ calcMaxHeight, tableConfigs, addOrUpdateConfigs, + timeType: 'range', + rawTime: null, // [] or datetime dataForm: { - key: '' + type: 1, + ftId: null, + productlines: [], + startTime: null, + entTime: null }, dataList: [], pageIndex: 1, @@ -260,13 +297,80 @@ export default { AddOrUpdate, BaseTable }, - activated() { - console.log('activated') - this.getDataList() - this.getGroupList() - this.getTypeList() + // activated() { + // console.log('activated') + // this.getDataList() + // this.getGroupList() + // this.getTypeList() + // }, + created() { + this.getFactoryList() + this.getProductLineList() + }, + watch: { + timeType() { + // 防止切换日期类型时报错 + this.rawTime = null + } }, methods: { + // 获取工厂列表 + getFactoryList() { + this.$http({ + url: this.$http.adornUrl('/monitoring/factory/page'), + method: 'get' + }).then(({ data }) => { + if (data && data.code === 0) { + this.factoryList = data.data.list + /** set default */ + if (this.factoryList.length) { + this.dataForm.ftId = this.factoryList[0].id + } + } else { + this.factoryList = [] + this.dataForm.ftId = null + } + }) + }, + // 获取产线列表 + getProductLineList() { + this.$http({ + url: this.$http.adornUrl('/monitoring/productionLine/page'), + method: 'get' + }).then(({ data }) => { + if (data && data.code === 0) { + this.productLineList = data.data.list + /** set default */ + if (this.productLineList.length) { + this.dataForm.productlines = [this.productLineList[0].id] + } + } else { + this.productLineList = [] + } + }) + }, + // 时间类型预处理 + getTimeRange() { + let startTime + let endTime + + if (this.headFormValue.timeValue instanceof Array) { + startTime = this.headFormValue.timeValue[0] ? moment(this.headFormValue.timeValue[0]).format('YYYY-MM-DDTHH:mm:ss') : '' // 强制axios使用北京时间 + endTime = this.headFormValue.timeValue[1] ? moment(this.headFormValue.timeValue[1]).format('YYYY-MM-DDTHH:mm:ss') : '' + } else { + if (this.headFormValue.timeValue) { + startTime = moment(this.headFormValue.timeValue).format('YYYY-MM-DDTHH:mm:ss') + endTime = moment(startTime) + .add(1, 'd') + .format('YYYY-MM-DDTHH:mm:ss') + } else { + startTime = '' + endTime = '' + } + } + + return { startTime, endTime } + }, // 获取设备类型列表 getTypeList() { this.$http({ -- 2.45.2 From cc5aa4d72f177ef00cf5bc7d921fad8890638643 Mon Sep 17 00:00:00 2001 From: g7hoo Date: Wed, 21 Sep 2022 10:34:40 +0800 Subject: [PATCH 03/14] =?UTF-8?q?update=20=E8=AE=BE=E5=A4=87=E6=95=88?= =?UTF-8?q?=E7=8E=87=E8=A1=A8=E6=A0=BC=E5=A4=B4=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/equipmentEfficiency.vue | 387 +++--------------- 1 file changed, 60 insertions(+), 327 deletions(-) diff --git a/src/views/modules/monitoring/equipmentEfficiency.vue b/src/views/modules/monitoring/equipmentEfficiency.vue index 956cda2..6559606 100644 --- a/src/views/modules/monitoring/equipmentEfficiency.vue +++ b/src/views/modules/monitoring/equipmentEfficiency.vue @@ -29,7 +29,15 @@ - + @@ -48,22 +56,17 @@ :total="totalPage" layout="total, sizes, prev, pager, next, jumper" > - -
+ + + \ No newline at end of file diff --git a/src/views/modules/monitoring/equipmentEfficiencyGraph.vue b/src/views/modules/monitoring/equipmentEfficiencyGraph.vue new file mode 100644 index 0000000..4894ca0 --- /dev/null +++ b/src/views/modules/monitoring/equipmentEfficiencyGraph.vue @@ -0,0 +1,308 @@ + + + + + + -- 2.45.2 From 7ed4a512bc8512523c959044b2eade0103ffb69b Mon Sep 17 00:00:00 2001 From: g7hoo Date: Wed, 21 Sep 2022 16:58:26 +0800 Subject: [PATCH 08/14] =?UTF-8?q?update=20=E6=95=B0=E6=8D=AE=E5=88=86?= =?UTF-8?q?=E6=9E=90-=E8=AE=BE=E5=A4=87=E6=95=88=E7=8E=87=E5=88=86?= =?UTF-8?q?=E6=9E=90=E7=9A=84echarts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/equipmentEfficiency.vue | 47 ++++++++-------- .../monitoring/equipmentEfficiencyGraph.vue | 53 ++++++++++++------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/views/modules/monitoring/equipmentEfficiency.vue b/src/views/modules/monitoring/equipmentEfficiency.vue index d022236..a843ce6 100644 --- a/src/views/modules/monitoring/equipmentEfficiency.vue +++ b/src/views/modules/monitoring/equipmentEfficiency.vue @@ -167,7 +167,8 @@ export default { } }, components: { - BaseTable, EquipmentEfficiencyGraph + BaseTable, + EquipmentEfficiencyGraph }, created() { @@ -298,6 +299,9 @@ export default { this.showGraph = true + console.clear() + console.log('inject data: ', injectData) + setTimeout(() => { // console.log('befoer graph: ', this.$refs.eegraph) this.$refs.eegraph.init(injectData) // 注入初始数据,这些数据在组件内部用作条件,有可能会被更改 @@ -336,24 +340,23 @@ export default { - \ No newline at end of file +.slide-to-left-enter-active, +.slide-to-left-leave-active { + transition: all 0.5s; +} + +.slide-to-left-enter { + transform: translateX(10px); + opacity: 0; +} + +.slide-to-left-leave-to { + transform: translateX(-10px); + opacity: 0; +} + +.slide-to-left-leave, +.slide-to-left-enter-to { + transform: translateX(0); +} + diff --git a/src/views/modules/monitoring/equipmentEfficiencyGraph.vue b/src/views/modules/monitoring/equipmentEfficiencyGraph.vue index 4894ca0..cb2b236 100644 --- a/src/views/modules/monitoring/equipmentEfficiencyGraph.vue +++ b/src/views/modules/monitoring/equipmentEfficiencyGraph.vue @@ -15,7 +15,7 @@ -
+
@@ -100,7 +100,7 @@ class EchartConfigs { setSeries(val) { this.series.splice(0) - if (Array.isArray(val)) { + if (Array.isArray(val) && this.series.length === 0) { this.series = val } else { console.error('setSeries() 只接受数组参数') @@ -127,7 +127,7 @@ export default { }, methods: { async initChart() { - this.config.setTitle(this.injectData.equipmentName + ' 时间区间走势') + this.config.setTitle(this.injectData.equipmentName + '时间区间走势') await this.getList() this.setLegend() }, @@ -149,15 +149,11 @@ export default { 无间隔: 1, 按月: 2, 按周: 3, - 按天: 4 + 按天: 4, + 按小时: 5 } return { - // current: 1, - // size: 999, - // ftId: this.injectData.factoryId , // 工厂id - // wsId: this.injectData.workSequenceId , // 工段id - // productlines: ['1409788336610934786'], // 产线ids, 这几项都暂时不需要 type: searchTypeMap[this.searchType], eqId: this.injectData.equipmentId, startTime: this.injectData.startTime, // '2022-06-14T00:00:00' @@ -166,18 +162,32 @@ export default { }, // getOEE - getOEE(params) {}, + getOEE(params) { + return this.$http({ + url: this.$http.adornUrl('/monitoring/eqAnalysis/oee'), + method: 'post', + data: params + }).then(({ data: res }) => { + if (!res.data || res.code === 500) { + this.dataList.splice(0) + this.$message.error(res.msg) + return { data: null } + } + return res.data + }) + }, getList() { const params = this.makeQuerys() // 发起请求 - return this.getOEE(params).then(res => { + return this.getOEE(params).then(datalist => { + console.log('getOEE res:', datalist) this.timeList.splice(0) this.rateList.splice(0) this.xAxis.splice(0) - if (res.data) { + if (datalist.length) { // 分流 - res.data.map(item => { + datalist.map(item => { const time = moment(item.time) if (this.searchType === '按月') { this.xAxis.push(`${time.year()}年${time.month() + 1}月`) @@ -264,8 +274,13 @@ export default { // 重新绘制图形 renderGraph() { - // console.log('latest config: ', this.config) - this.chart.setOption(this.config) + console.log('latest config: ', JSON.stringify(this.config)) + this.$nextTick(() => { + // this.chart.setOption(this.config) + this.chart.setOption(this.config, { + notMerge: true + }) + }) } } } @@ -273,8 +288,6 @@ export default { -- 2.45.2 From 1ad64efe59c03cb773cf2e5c7f01547b6d388d5f Mon Sep 17 00:00:00 2001 From: g7hoo Date: Thu, 22 Sep 2022 10:03:23 +0800 Subject: [PATCH 09/14] =?UTF-8?q?update=20=E8=AE=BE=E5=A4=87=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/request.js | 4 +- .../modules/monitoring/equipmentException.vue | 117 ++++++++---------- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/src/utils/request.js b/src/utils/request.js index 134bf99..d06a328 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -8,8 +8,8 @@ import merge from 'lodash/merge' const http = axios.create({ // baseURL: window.SITE_CONFIG['apiURL'], - // baseURL: '/api', - baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/yd-monitor', + baseURL: '/api', + // baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/yd-monitor', timeout: 1000 * 180, withCredentials: true }) diff --git a/src/views/modules/monitoring/equipmentException.vue b/src/views/modules/monitoring/equipmentException.vue index 03b3425..c05bb1f 100644 --- a/src/views/modules/monitoring/equipmentException.vue +++ b/src/views/modules/monitoring/equipmentException.vue @@ -21,7 +21,8 @@ - + /> -->
+ + -- 2.45.2 From 4f3948169356501f3aee814e623568858c4069bc Mon Sep 17 00:00:00 2001 From: g7hoo Date: Thu, 22 Sep 2022 15:02:38 +0800 Subject: [PATCH 13/14] =?UTF-8?q?update=20=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81=E6=97=B6=E5=BA=8F?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../monitoring/equipmentTimesequence.vue | 127 +++++++++++++++--- 1 file changed, 108 insertions(+), 19 deletions(-) diff --git a/src/views/modules/monitoring/equipmentTimesequence.vue b/src/views/modules/monitoring/equipmentTimesequence.vue index 69074c4..7afe0d9 100644 --- a/src/views/modules/monitoring/equipmentTimesequence.vue +++ b/src/views/modules/monitoring/equipmentTimesequence.vue @@ -24,6 +24,9 @@ {{ $t('search') }} + + {{ '添加设备' }} +
@@ -38,6 +41,16 @@
请先查询数据
+ + + + + +
+ {{ '取消' }} + {{ '确定' }} +
+
@@ -183,7 +196,7 @@ export default { equipments: {}, state: ['正常', '停机', '故障'], colors: ['#4caf50', '#ffb300', '#e53935'], - queryBuffer: {}, + // queryBuffer: {}, // tableConfigs, calcMaxHeight, timeType: 'range', @@ -199,7 +212,11 @@ export default { pageIndex: 1, pageSize: 10, totalPage: 0, - dataListLoading: false + dataListLoading: false, + /** dialog */ + dialogVisible: false, + eqId: null, + dialogEqList: [] } }, components: { @@ -217,6 +234,7 @@ export default { this.getProductLineList().then(() => { this.getWorksetionList() }) + this.getEqList() }, mounted() { this.$nextTick(() => { @@ -312,6 +330,7 @@ export default { console.log('created equipments --- ', equipments) return equipments }, + // 把分组好的设备数据转换为echarts需要的series数据 transformEquipmentsToSeries(equipments) { const seriesData = [] @@ -343,27 +362,20 @@ export default { .add(1, 'd') .format('YYYY-MM-DDTHH:mm:ss') : '' - const productlines = this.dataForm.productlines ? [this.dataForm.productlines] : [] - const wsId = this.dataForm.wsId || '' - /** keep a copy of query conditions */ - this.$set(this.queryBuffer, 'productlines', productlines) - this.$set(this.queryBuffer, 'wsId', wsId) - this.$set(this.queryBuffer, 'startTime', startTime) - this.$set(this.queryBuffer, 'endTime', endTime) - - this.dataListLoading = true + // this.dataListLoading = true + const condition = { + startTime, + endTime, + productlines: [this.dataForm.productlines], + wsId: this.dataForm.wsId + } /** fetch data */ this.$http({ url: this.$http.adornUrl('/monitoring/eqAnalysis/timeAndStatus'), method: 'post', - data: { - startTime, - endTime, - productlines: [this.dataForm.productlines], - wsId: this.dataForm.wsId - } + data: condition }).then(({ data: res }) => { if (res.code === 500) { this.dataList.splice(0) @@ -371,8 +383,8 @@ export default { this.$message.error(res.msg) } else { /** handle actual data */ - this.dataList = res.data - + this.dataList = res.data + /** test data */ // this.dataList = [ // { @@ -417,14 +429,91 @@ export default { } }) }, + handleProductLineChange(val) { this.getWorksetionList() }, + // 渲染 echarts renderChart() { console.log('>>> chart configs: ', this.chartOption) this.chart.setOption(this.chartOption) }, + + // 获取对话框里的设备列表 + getEqList() { + this.$http({ + url: this.$http.adornUrl('/monitoring/equipment/page'), + method: 'get', + params: this.$http.adornParams({ + page: 1, + limit: 99999 + }) + }).then(({ data }) => { + if (data && data.code === 0) { + this.dialogEqList = data.data.list + } else { + this.dialogEqList.splice(0) + } + }) + }, + + // 添加设备 + addEq() { + if (this.equipmentCount) { + this.dialogVisible = true + } else { + this.$message.warning('请先查询数据') + } + }, + + // 确认添加设备 + dialogConfirm() { + let startTime = this.rawTime + ? moment(this.rawTime) + .set({ hour: 0, minute: 0, second: 0, millisecond: 0 }) + .format('YYYY-MM-DDTHH:mm:ss') + : '' + let endTime = startTime + ? moment(startTime) + .add(1, 'd') + .format('YYYY-MM-DDTHH:mm:ss') + : '' + + const condition = { + startTime, + endTime, + productlines: [this.dataForm.productlines], + wsId: this.dataForm.wsId, + eqId: this.eqId + } + + /** fetch data */ + this.$http({ + url: this.$http.adornUrl('/monitoring/eqAnalysis/timeAndStatus'), + method: 'post', + data: condition + }).then(({ data: res }) => { + if (res.code === 500) { + this.$message.error(res.msg) + } else { + /** handle new equipment */ + const newEqStatusList = res.data + console.log('添加设备', res) + const newEq = this.transformDataToEquipments(newEqStatusList) + this.$set(this.equipments, Object.keys(newEq)[0], newEq[Object.keys(newEq)[0]]) + this.chartOption.setYAxis(Object.keys(this.equipments).map(item => this.equipments[item].name)) + this.chartOption.setData(this.transformEquipmentsToSeries(this.equipments)) + + this.$message.success('新设备数据获取成功') + this.$nextTick(() => { + this.dialogVisible = false + this.renderChart() + }) + } + }) + }, + // 每页数 sizeChangeHandle(val) { this.pageSize = val -- 2.45.2 From 31834662c9b53decf8c78b95ebb573670f7e6584 Mon Sep 17 00:00:00 2001 From: g7hoo Date: Fri, 23 Sep 2022 15:02:13 +0800 Subject: [PATCH 14/14] update icon --- public/favicon.ico | Bin 4286 -> 1673 bytes src/assets/img/logo.png | Bin 0 -> 1673 bytes src/assets/img/logo@2x.png | Bin 0 -> 3113 bytes src/assets/img/logo@4x.png | Bin 0 -> 7069 bytes .../modules/monitoring/equipmentException.vue | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/assets/img/logo.png create mode 100644 src/assets/img/logo@2x.png create mode 100644 src/assets/img/logo@4x.png diff --git a/public/favicon.ico b/public/favicon.ico index 2bd581cea17fd10bf80730efcf0563e634494a91..37a3d39dd2cdb28beb1d36e90fcfbf95533fcacd 100644 GIT binary patch literal 1673 zcmV;426p+0P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91AfN*P1ONa40RR91AOHXW0IY^$^8f$^^GQTOR9Fd>S51r*MHGH-dS<$N zH!^@R!ou*A>?S5AVuTz(Tb9XJ!h+~=G zvSrttFIPFP2Wlk!LW*nl3np{qU?Z2~ERtGK`K2pj$I@_YeUV~>s+Y6sngAm zJJkZql&#Y@kGmdB%(FkNC-BCh7v%>Ip zOU1lMue)aAc$%J>3uKlw20Mls<&;5X7D2z)&9gni6kH0l)yWWPfy*B8MB;J6l!2nC zZOj2{b{`}=CWBv}kcax;Ms}rw9(0*ThC&vBrkOO*X>lX>9* zbVxACib%FbC>sUMxS8Pelv|6Y?p=PAD4Y|=teLy}= z=5mhB$a9!?OE(2zRz=8jmXn-NI%Q>rfURMUm;zwc%?+`>y{QAqY@%}4>dDwT_< zJmUmO=SD*Wj-=Zcv=o30aL_6iJdQ`T#ORyZT0ov6*N*J6jiw=oOWb)F^YE~ZJ2M16 z6l8a@)m~Ua~a>sjVxT<;;VbYk+-f7|bScIN2OV~(EhM;cID zAv1-xd|DRWHjuV?=^L2yyjc=+A}Pz0)hlv3FEU_2&6jCU?k8Su(8Ojo)k2TB$%8&tEE{IpV#=MMs?BvEsJ_c>fI!0nz zB4sE`jE*s{0h2fGp*CayHGy%mp!yc7)1D!Tn$Y{$4-0)t|DV3cPpF84N=3e0RvxQQ2KD0`jypo&xS1?}oP zDDsv3cA-`zQWJk@W&uVlEvdAVL=H{Gax804!Y zY5*r}wX!^dr1=0PdFAy0T?UUk{d$PKi#kg10(RE^YFox%Zk+)8#)LeKzla|pkVB!# z@+~)}s}T#_`^|Gxi}KAV-XRa~~61Dn5)V9zS zY^l%`s1Q@d(qdYKrfW;@ug1sYITNQ@=mITfcWrYzvpZ+c?>{qVXLrijKKwQ{G5Ter z2N*lZ7(0Z9r`TaMI?q4Yw%y03kaEKVvYz zHV4wmIE+Mwq08G|!|3%{xo^2#hCsp(XZ^i3KK10uu`v^8RVo!wv-R8>^!s{LW1o<^ zz!iH3q@h?Uf^+F5P`rA$!spHMXbm@+O~UIXCtUh?0a`pqca1j~x&fI&hR0bLH$23(i+)GtZ*XhYj?5!6T=)jE_JGE==8Sn93wx{U=Lka_1hZ1p5uY(i%=*PxQ5vu zA=R;*_%8AGY%vSuUr*ddeCWOub2=P(vMK)k1qW{{l=5&jczIJ>BaGdAgR9e=lSfXU z?+oYAoac%;o^wO4vvHtXa`&Z2P$uFgRFcq=T+QLgD(XmvQ`C zbl2eg`l31p_riB{=hONsaxjrifJN%iRm08DwJM)<*ztL(^}VNzPiru7={>;{cQ^cw zpp3Pbx^?mtJpTNMKOgsUz4989@$cV8?>`LNf8jjveJGZ??>~vTWe=>|{u^_yy8liy Twto=+A|O-h(XuXhX=MKaRZyEC diff --git a/src/assets/img/logo.png b/src/assets/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..37a3d39dd2cdb28beb1d36e90fcfbf95533fcacd GIT binary patch literal 1673 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 D>-@VD literal 0 HcmV?d00001 diff --git a/src/assets/img/logo@4x.png b/src/assets/img/logo@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..36641d430bff8669003a92b8e0584d62ea161ea4 GIT binary patch literal 7069 zcmV;O8)D>%P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91fS>~a1ONa40RR91fB*mh07#AmcK`qz2T4RhRCodHoe7v6RkeUm^~_`? zAri9?Cdmwl0U-$n2vHP>AOfz)$3qmKXh8A+ksUiH`;mWhKLhk(pk(OF9FSPTs0A$2?YlX;QyU^{rLFA}zF}`J&WdYF4X=1e7g>F}n zJmJjG1K=zSa__4LDie6#(IRqKN{m0b0ki;UL2m!rm3N9cai|!r_OtV<`qBqL3%r0h z8`<}^vSk4X3dC+~1IxN@KKy;_RK4B)HePkriCL9-$f96_q`s07^M>3?&Sx}r2#q@I zygmSJBA*4`59|WJYnPaNH;TNXbu2xgo6Zf|SR2)ev5{_>s`mzs=hDaD(TCh*3(Ko_ z-V}4B&Ieh8$8MAhOrJN3oSGKnC(bg91!qRs`%n}p@LYCDT$Pl0?`Zf3EbYeihGLnr*$Q;8E;?tQn&Uvu+vpM42fDrQYo@hhT< zA}?rr7SSvV)=7jf0ZhF}F@C~cWCXe=Ujp_Nqr+<=b_bW8ixSS%_KdElvP*V7`4X_J zTC_XFH@Q&DTITL043&9)75|+h!cLJ5^zY8xHY}K9;Ao_sj}nfx@4c?hiRfxXb)8p5 zxBM5xteY*;4dMpOohG+BUU`O?lbC#3`v|ZrvIgA98gPaET z6hh|M&vCCHJ`%)#$c5ucfL(E&q;ACYh%Av&VvgTK1T}I`1qs-6>s@`&U;q^iiO-p#Ay0dtE7HxKRd5^om*_=PGsCa#_D*iGcQX5 zAWu6B3*Va;Grm_BuGd%N1Fd4b)c3?qp9Ff5XA$JAn0wC}av|%1Wkb?dr{RX&I$qBi zY{5y`gj#M95EeN*lS^p*O(z04+XApr&OE<+n+V;U;s5IsL|z*d=tmPJb4Ff4fD9Nj z{i;#;B4)KW@}9;fGifpj%ngD2JQ)&t9W6-#4+6KK1W>r&1p(w%hqi0o@|}6U1h5Rx zV-3H>*&xh4BV`8jgf9`E1f<1WxVK0s6U~h{7??qRn_HRYGE*Au&%Dm zAVh%tO zWfyo{8Exxzy{uU;2K&h1i8g3>|(d;p8;@~ z7J$41K!e;Wx#ht?I}8lfvOWUz0YE*6lll-K0q79`yq~Ip9bHc3iXw{u&4?fgZ5Akj zd>fo+nQeKaK%Gi~poOp0nu}aP<4unSaQCb`x34kA{=+a zd^T+JK-qY_3p~Bthn3+`Nb-s)j#y4dx*b^}3U=BHlL;$kR?|{`LjO zF^Ua8O9QBM8BE_~7bbAcpqy9Y*SpybHiAEzQMnAF_H`>SMjlqt&lnTuH&_8JV0+`X z+s3rtc+(9X8`{naI3?uKfhuo?=x6c@vNFsS%+TM+%JO*NXkZ3sPuBpm@8HX&svd+L z?2;cS3qd@=hUr}Vlg73JhzbHk0KY($KT)t^5i4-^kQa{>0eo7+4m_PE5dc0)`hmKi^V=wtitgAf>` z4xO+Dnnra?N;Q#HGNc|i4mL`_i7$f~>+ zyE4`=3{FN9LP>mA^WxpLh+IrSv|5*;I+Xg|aRgwwVQP5OfZ)s{8hhiri^o=BNSMxN zV7=bUDg+sklKTl&P*5P&?cAh6V%!s!c@^K-F>& z;VOXGLI42EaLH3_oB-%~u?JrtW7s$+Sr*LY2#494#um@OZ=T*sdD~uOB zA1EjQC$kKV>j>lJaHMh*9oEhZQ{(pwxH}9b0Gn+Fkv&I5YH!0S=CzmcDKO4l8a%~f zfWy&mF^s<)3rKKUy=vkIs6C-xx>kEt@XId>?QN2}7eRc^4^aYTZ7HcadxCU790zt$ z1n}zlmz>yn*OKMZrGatDr`p-G->CW2X|u+hbI!)N*kTc^D@4mDKY3Z|$tQ0x)^CWq zB@N93eh5h&^6oXG&c5K|dz|-q7OChdmH@y!+^nftmrkcyW@CX0%>fpx$T73G4F2TN z4Wlnu5a=k}4GHFXwA3bbhw#OO=epCGk~6Oq>svLM$5+1X^#@k{@u5eaIv^Ss5xb33 z*1?>(J_geFMnzn#cB%>651H|+1OED#lcK;3t+&|i^_a(hqv5D12oq|P!PdW)4bH-_ z@IwuOmqhl2@tZ4*dF45!dln3|^TG-MUV^39a5wJ|V_gL@?__`tpE;jn`0JgSe)oYi zfH7asCt&WbaORay_%W{AAT^)%W7=6(Py>1=JT*9?oE0%M36j{>A>m{*3iS5sZ-yIMsGYJ#T*^!EHefz`lYB8i?-|Db+($G0QmAZK;B zlGVX8#LpEmlAB7;KL_H5ILQ6lLfC4Yl9Rb!>Q^sr8LPt$X!5yUaPkB|&yOVY+w{u5 zUX6;7|2n-tcLI)4X6##;xi11HGk78?#kt|P=~P!ACw%To&7AsD`OgR-$;m=OkNlyU(_UHBIgOBt-jixB#4P7Hk-& z`wZLZ^Cnq`LHLq7k3V6~tB*eXSpdXQd;MI{t6oIF*mfqo>-?D4wyFtSzS(BIxMTQk zZm2ko`fvBb>65Q>!gpGUC_(FPG+IZE>X5D523e}$)5@<1)twEn$NlOQy9|G%21$J; z4qWKIFa`59ahO*EIL@JSOP%7s;EpC-XMLcm)TCDmK88`e2$4!*Bn};Y%U~QJ)!cSK z0M*=k#h|??8o*>SxEREQfx*hf;uAl`76A73+O)`%@kt2>0UO})a4p*J5HXh=V? zZOc5MU-=+t!z7f)+DC*28ASkX-C{PN_G?08OQ^vzk*^_yYombIVCgOz2AWZk^2=#S zUCLUC)rX3e5<*z1b2*0BNK=i+%J{Niz}^{wB8c?P9zH_tqyu0>If4 zW(d^Oh_RX(@5%sxOJ1cJ<~#6cfjB7h7L8#vz-quS$e?C3nE?N6WmLewXZeWCNW9qe z{C*+H38euRY>=dIS*>CEzc?haM5;r_1ir|-DgyHsJz<7`lYta5y^Dw&Jj$6_>iZR# z;L!1N@Xm~iJE{QG{Hi5-^fBgyGom7o)2>Nm{2^le3p4!iIB+5hK#`($Ix*gf5F+9vo3&sGyJ9Fu z^=vo16MOjK-lf4xSV|Y`arDtxbl;~*EFt)zM8#gig+^?@Jm^*SXWry%rZ8~#c*R{P^@;rUQIFNrH&>}wc?*E?Zb8enJixVX)Cpbd$+y%6po z)cl1u>upi%Kw%8bKMDxOVXB>=fb=mxAX* zHtu3y(KyUqb0M9(zzw#m0JNe;hwtRQ0B)JLl1UI}d88r2la9EU>G=hy{k0%aS>&ax zA>b?>&Du@l^J%rsnP1F%h^kAN`9<{nLNDf(7s~uouO_UcRwcb5;!iZCIBw^U=hVoL zc?IIB0X7EJ1jf)Do-xp>BN1`Bj0w+E?VVf{)BwBkOS~?_*J56wL1Ig!jVsQT&bpp4 ze@##r1vS71v>$T;z>npxV$K6>+UzTH);;w60{c10HLwQQz|aK!ZqV};rU`r#-qZ>2 zD>j`$uYond28brKbn%&5R)+%L5Vcz*e`U-sLG7=Of+@5H*hqMDbQW=j5A(dIomc8> z&%$mj<~NxxjEXz72H1%7eSLn~ZVJyBj^#=0TNb@2nOJX}MAiVC1mae|wkgc&kRgYu&FB->gcH*dtwc z@WIbz#*KR*ebfiZvvD?X~ZDee=3Snbwv=&9=6|J#$i7$x5(K za76*#*k@u#s&U_^YiG=upP5**YTTu_>_9)tqVZ*uGz&+4TI92Q&F!+3Y3e)a^nKaL zNwCx1K-1UsxC|Yd89d`%&2LScIdAi^$3Ax0DW~9^Eo~~f#MxWd@06P3_%!WlctB4m zExVP5jW5AK($0gYckP_i_|TgN9(>=dZ(RS=Qc_tm1yH9-J0DX-^)II5$AGw|l#Et7 zu9RVP^YYdvBa^wcmO~-+!IXmL#1#M!lD^wG9cIp@^VvHMq{0%ge$x037K*1QCcNQJ zOaWdXdT*G>m)NVF3zEjdOb^t^l>iFRgzxQ+9U@Ei_xORJ+%{pKi>?3|che#KDBLrb zKnx08*yS9S=-sS7E2gwEkoa&@?W&2=SyUHX(G);O{2q*WKCJdJfWCh>>!iflWl#}} zn)*#9m3m-Drt6-A3jB6jP=aDB0P#K(Iz&!qeg7&*)h`;^y%?0}-Mn7LG>k2ErYvli z%ugG7e<>`gOd>0QTJ=30FYjY|IwL9p1MQ+q0Q+D$t$#OILIbdE?~-*4jqRtHy*9{1|Om0S_zTjZn+Nsh|S% zZs+DOO0K7JV!@+@NsV2DZ?>5W3F!Eq*C^514QJKo*}=>Q+g`CTmI(j~z$=jE)odT{ zDYSj`RDg9N@1k%TpBMTfeCP>3IP}v&IMA&uGaX(_nStUT5u{B8 z*6D_(RlMPSHwy90WRd6GXv|XpDi3-^WDeW8%P4GgYg7e!dYHVGV914Ok)Jd~w3M59 z0~>$!f3U-uk1RAUFi&1pAPYTaY$720=JCu3Fn|fSD_IPgmtQ_&F(b^34y~*$yWyo7JK<({`Q6^V zGlH@C(n~)u?2oIj-qG~(;X5{L*n=s*TvR@d;?a4T$eu1J-^kYV*+!9nCc#9L$N_A* z^yL-g7d54(VT-5|>G?>RlKBKn-10h+`;FEiL)xXYa}cLc$ggajufz1`gflNEy6=H9 zNu?xcG0jim^^h}=^e0M!&>M-`2-{Oed&dqVV=`lwuk(9pG@t;*?BA)E$Fd9CmU+Jy zc>h+X_}mRBKztciHG}~LsOs*EPyB!a#Ft@JLl{tis_wq{#8;96bm5}H72m&8FOOZ7 z!ezO=eQf_V`qlg%`=a|Ni>$5`!6ZcoJU+w^g>deq@6;Ke=Q@(VvTY0X!=bN3zzF_I zstoje3vS7!1diUu5hEWYi086}{;EpwZ)~CSckm-h9Iu}R|D52KG^r!#jdCbJD}DWI zPUr52<3~!$a)lym;X5g;#iyYY$3f!65|LS21wiA8S}|_xlFT<7t&_SEu~|iEnmBQ+ zlhnnW74Xw)iNAMV8U=t6J>>aoMlyHS?*?mpG4u`NU{*>}b5Vpt7&u8C8YQX#83=z> zj~(S`BI76Okh#012LF$M9nh7#tj-N)v(VfU^A6V4y=p~s- zrvOeaVCpTlfxm>w9}r#hTdxRRXPf?fuaU?^G4U>?{3lE1hmp(h67gFe1+bZe(N36d zt`qYMT%R5*@}^z3J6vBC*#j9&@8Y|LTpN%kfFo`>h!nFtzLi0 zqqk3Do!8xwzJ*lwl*l$PB3B(msfK#5Eh3(>HtXe)Wxp~~B`H8GzguOz7?lR5vrWVutbw03_116yM1_>!u1#b#^v&d) zekMK(JlN3^XZ#%T$~*aMj0T|?txT2iui@uj=;%o;{30ErQM{TGUGCgpl6Rdq=@g|VGnem@w7Idu9Pp|^b4EO;VRc{KC%)Q+A3 zaU?K@W(g}es7k1*r?B_(2Jm;78TF@+xa~=vJh3#KNa2x)@;s^%+>xy;z3nUh0uO3e z@M#$HihqebIh!AP@gsUA(b4Z@S%lq1)YGq?^8L%IOgav>!S}bh`8|=QzjEWkQvtLx z4Qu#q*wL(cXYo$^cF&q zL`q*q`A08b2Xa4>yioF}Af_zijuC%%p%I@;b7Y1YHM59V{Q|OlH-f|19!RB@0G-Ds z#aL@fW*M{D>L~alD}aq`L(g(>7Bj*%cHY1>c?n?EUk8?@6RZ!NM#mRh0Ti`PohU** zQ_z<&7C%7NyQ7md(9%Z&YccK~!zf=&$G6dWh(2B_iUK%ESTFKks5%LqxP)ufTGCFQ z%6YFnOef*oGQYuf1&*0N6qBB!D}WLQ(MFTVSX^f3vow5;EViKoDS`xAnd-k!@88~N zy=fJH#OKnGhypl~;7~dlBuBEvIvd5%ci{(80SO?l?QB)mAE6GM2Mbh>Rh+m2D0MJm z5C%OKKf;%=5zYr`RXUVnNjgAd<3XG6jbczlOypQp}?fNJBYhAy_pfu2b(iXpT~hSyTAce zlJu2T0i2BCS)7e$@nnpjFFEu3+danc5