From 9b8baee48d0bdd95ea157be10e7c24711c511dc1 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期五, 19 九月 2025 09:26:03 +0800
Subject: [PATCH] 最新版本541200007

---
 admin/src/components/common/Menu.vue                 |    2 
 admin/src/components/common/CommonHeader.vue         |  133 +++++++++++++++++---------------
 admin/src/components/system/menu/OperaMenuWindow.vue |   12 ++-
 admin/src/api/system/menu.js                         |    2 
 admin/src/views/system/menu.vue                      |   52 ++++++++++--
 admin/src/main.js                                    |   11 ++
 admin/src/store/index.js                             |    6 +
 7 files changed, 140 insertions(+), 78 deletions(-)

diff --git a/admin/src/api/system/menu.js b/admin/src/api/system/menu.js
index 4f7d1c0..38002a0 100644
--- a/admin/src/api/system/menu.js
+++ b/admin/src/api/system/menu.js
@@ -2,7 +2,7 @@
 
 // 鏌ヨ
 export function fetchTree (data) {
-  return request.post('/visitsAdmin/cloudService/system/menu/treeList', data)
+  return request.post('/visitsAdmin/cloudService/system/menu/treeList', data ||{})
 }
 
 // 鏂板缓
diff --git a/admin/src/components/common/CommonHeader.vue b/admin/src/components/common/CommonHeader.vue
index 2ef2259..2e95744 100644
--- a/admin/src/components/common/CommonHeader.vue
+++ b/admin/src/components/common/CommonHeader.vue
@@ -8,11 +8,12 @@
             <div>鏈嶅姟涓績</div>
             <div class="linellae"></div>
           </div>
-          <div class="item" @click="getHeaderNav('0')">瀹夐槻涓績</div>
+          <div  class="item" v-for="(item,index) in topMenuList.list" :key="item.id"  @click="getHeaderNav(item)" :index="index">{{item.label}}</div>
+<!--          <div class="item" @click="getHeaderNav('0')">瀹夐槻涓績</div>
           <div class="item" @click="getHeaderNav('1')">娑堟帶涓績</div>
           <div class="item" @click="getHeaderNav('2')">鑳界涓績</div>
           <div class="item" @click="getHeaderNav('7')">鍚庡嫟涓績</div>
-          <div class="item" @click="getHeaderNav('3')">椹鹃┒鑸�</div>
+          <div class="item" @click="getHeaderNav('3')">椹鹃┒鑸�</div>-->
           <!-- <div class="item" @click="handleTest">娴嬭瘯</div> -->
         </div>
         <!-- <div class="title-en">Diagnosis of Intelligent Manufacturing Integrated Service Platfrom</div> -->
@@ -70,7 +71,7 @@
       default: true
     }
   },
-  data() {
+  data () {
     return {
       title: process.env.VUE_APP_TITLE,
       headerNavData: {},
@@ -96,7 +97,7 @@
           ],
           newPwd: [
             { required: true, message: '璇疯緭鍏ュ瘑鐮�', trigger: 'blur' },
-            { validator: this.validatePassword, trigger: 'blur' },
+            { validator: this.validatePassword, trigger: 'blur' }
           ],
           confirmPwd: [
             { required: true, message: '璇峰啀娆¤緭鍏ユ柊瀵嗙爜' }
@@ -106,15 +107,15 @@
     }
   },
   computed: {
-    ...mapState(['menuData', 'userInfo']),
+    ...mapState(['menuData', 'userInfo', 'topMenuList'])
     // title () {
     //   return this.$route.meta.title
     // }
   },
-  created() {
+  created () {
 
   },
-  mounted() {
+  mounted () {
     // needChangePwd 0 : 榛樿瀵嗙爜闇�瑕佷慨鏀癸紝1 涓嶉渶瑕�
     if (this.userInfo && (!this.userInfo.needChangePwd || this.userInfo.needChangePwd == '0')) {
       this.visible.changePwd = true
@@ -122,7 +123,7 @@
   },
   filters: {
     // 灞曠ず鍚嶇О
-    displayName(userInfo) {
+    displayName (userInfo) {
       if (userInfo == null) {
         return ''
       }
@@ -135,13 +136,13 @@
   methods: {
     ...mapMutations(['setUserInfo', 'switchCollapseMenu', 'clearUserInfo']),
     // 淇敼瀵嗙爜
-    changePwd() {
+    changePwd () {
       this.visible.changePwd = true
       this.$nextTick(() => {
         this.$refs.changePwdDataForm.resetFields()
       })
     },
-    validatePassword(rule, value, callback) {
+    validatePassword (rule, value, callback) {
       if (!value) {
         callback(new Error('璇疯緭鍏ュ瘑鐮�'))
       } else {
@@ -161,74 +162,82 @@
         }
       }
     },
-    getHeaderNav(type) {
+    getHeaderNav (item) {
+      if (item.url && item.url === 'goHKAF' && item.params != null) {
+        this.getHKAFHeaderNav(item.params)
+      } else if (item.url && item.url.indexOf('http') === 0) {
+        window.open(item.url, '_blank')
+      }
+    },
+    getHKAFHeaderNav (type) {
       getAppHeaderNav(type).then(res => {
-        window.open(res, "_blank")
+        window.open(res, '_blank')
       })
     },
-    handleTest() {
+    handleTest () {
       const myWindow = window.open('https://10.50.250.253/portal/ui/index?componentId=dfe&componentMenuId=process_apply')
       setTimeout(() => {
         const params = {
           componentId: 'dfe',
           componentMenuId: 'process_apply',
           callback: {
-            method: 'dealTlncMsg', argument: {
-              "msgId": "98c256b9-aaff-11ef-8347-fa163ee2c57c",
-              "moduleId": "dfeFlowTodoModuleId",
-              "msgTitle": "璇峰強鏃跺鐞�13856591439鍙戣捣鐨勮鍋囩敵璇�",
-              "msgStatus": "0",
-              "msgCreateTime": "浠婂ぉ 15:33",
-              "msgCreateTimeIso": "2024-11-25T15:33:42.000+08:00",
-              "serverTime": 1732531014591,
-              "menuCode": "process_apply",
-              "msgStatusStr": "寰呭鐞�",
-              "comId": "dfe",
-              "userId": "13856591439",
-              "extendNoShow": "{\"processInstanceId\":\"98a57fe0-aaff-11ef-8347-fa163ee2c57c\",\"processId\":\"process_dabcfdd39f1b6f46d36a9f4ff6ce1080\",\"param\":{\"sourceType\":\"todo\",\"modelCode\":\"tb_leave_dfe_for_dfe_runtime\",\"processNodeId\":\"UserTask_dde7d83377343a2d5fa1f60c23b023ef\",\"taskId\":\"98c256b9-aaff-11ef-8347-fa163ee2c57c\"},\"process.param.appId\":\"32ca8770-6f85-11ec-b5a3-991864da52a6\",\"appId\":\"32ca8770-6f85-11ec-b5a3-991864da52a6\",\"name\":\"璇峰亣鐢宠\",\"taskId\":\"98c256b9-aaff-11ef-8347-fa163ee2c57c\",\"taskNodeId\":\"UserTask_dde7d83377343a2d5fa1f60c23b023ef\",\"url\":\"/dfe-form/process/tlnc/apply\"}",
-              "targetComId": "dfe",
-              "moduleName": "娴佺▼寰呭姙",
-              "tid": "99cad778-aaff-11ef-9dbf-ff08ba71965c",
-              "msgEndTime": null,
-              "msgEndTimeIso": "",
-              "picUrl": "",
-              "extendJson": "{\"key1\":\"璇峰強鏃跺鐞嗛檲楦块鍙戣捣鐨勮鍋囩敵璇穃"}",
-              "extendParam": null,
-              "extendCascade": null,
-              "createUser": "13856591439",
-              "webCascadeUrl": null,
-              "h5CascadeUrl": null,
-              "cascadeTodoOpenType": null,
-              "cascadeSourceConfigId": null,
-              "h5Url": "/h5/pages/form-page/form-page?returnPath=-1&appId=32ca8770-6f85-11ec-b5a3-991864da52a6&taskId=98c256b9-aaff-11ef-8347-fa163ee2c57c&processNodeId=UserTask_dde7d83377343a2d5fa1f60c23b023ef&modelCode=tb_leave_dfe_for_dfe_runtime&type=flowHandle&component=form-apply&_sn=true",
-              "segmentId": "dfe-form",
-              "lastUsers": "闄堥缚椋�",
-              "currentUsers": "闄堥缚椋�",
-              "todoTypeCode": "dfe@@tlnc_placeholder_tlnc@@dfeFlowTodoModuleId",
-              "currentUserIds": "13856591439",
-              "lastUserIds": "13856591439",
-              "msgDesc": null,
-              "widgetUrl": null,
-              "detailType": null,
-              "widgetWidth": null,
-              "widgetHeight": null,
-              "userIdList": null,
-              "statusName": null,
-              "arriveTime": null,
-              "stayTime": null,
-              "todoType": null,
-              "cascadePort": null,
-              "openMode": null
+            method: 'dealTlncMsg',
+            argument: {
+              msgId: '98c256b9-aaff-11ef-8347-fa163ee2c57c',
+              moduleId: 'dfeFlowTodoModuleId',
+              msgTitle: '璇峰強鏃跺鐞�13856591439鍙戣捣鐨勮鍋囩敵璇�',
+              msgStatus: '0',
+              msgCreateTime: '浠婂ぉ 15:33',
+              msgCreateTimeIso: '2024-11-25T15:33:42.000+08:00',
+              serverTime: 1732531014591,
+              menuCode: 'process_apply',
+              msgStatusStr: '寰呭鐞�',
+              comId: 'dfe',
+              userId: '13856591439',
+              extendNoShow: '{"processInstanceId":"98a57fe0-aaff-11ef-8347-fa163ee2c57c","processId":"process_dabcfdd39f1b6f46d36a9f4ff6ce1080","param":{"sourceType":"todo","modelCode":"tb_leave_dfe_for_dfe_runtime","processNodeId":"UserTask_dde7d83377343a2d5fa1f60c23b023ef","taskId":"98c256b9-aaff-11ef-8347-fa163ee2c57c"},"process.param.appId":"32ca8770-6f85-11ec-b5a3-991864da52a6","appId":"32ca8770-6f85-11ec-b5a3-991864da52a6","name":"璇峰亣鐢宠","taskId":"98c256b9-aaff-11ef-8347-fa163ee2c57c","taskNodeId":"UserTask_dde7d83377343a2d5fa1f60c23b023ef","url":"/dfe-form/process/tlnc/apply"}',
+              targetComId: 'dfe',
+              moduleName: '娴佺▼寰呭姙',
+              tid: '99cad778-aaff-11ef-9dbf-ff08ba71965c',
+              msgEndTime: null,
+              msgEndTimeIso: '',
+              picUrl: '',
+              extendJson: '{"key1":"璇峰強鏃跺鐞嗛檲楦块鍙戣捣鐨勮鍋囩敵璇�"}',
+              extendParam: null,
+              extendCascade: null,
+              createUser: '13856591439',
+              webCascadeUrl: null,
+              h5CascadeUrl: null,
+              cascadeTodoOpenType: null,
+              cascadeSourceConfigId: null,
+              h5Url: '/h5/pages/form-page/form-page?returnPath=-1&appId=32ca8770-6f85-11ec-b5a3-991864da52a6&taskId=98c256b9-aaff-11ef-8347-fa163ee2c57c&processNodeId=UserTask_dde7d83377343a2d5fa1f60c23b023ef&modelCode=tb_leave_dfe_for_dfe_runtime&type=flowHandle&component=form-apply&_sn=true',
+              segmentId: 'dfe-form',
+              lastUsers: '闄堥缚椋�',
+              currentUsers: '闄堥缚椋�',
+              todoTypeCode: 'dfe@@tlnc_placeholder_tlnc@@dfeFlowTodoModuleId',
+              currentUserIds: '13856591439',
+              lastUserIds: '13856591439',
+              msgDesc: null,
+              widgetUrl: null,
+              detailType: null,
+              widgetWidth: null,
+              widgetHeight: null,
+              userIdList: null,
+              statusName: null,
+              arriveTime: null,
+              stayTime: null,
+              todoType: null,
+              cascadePort: null,
+              openMode: null
             }
           },
           msgType: 'tlnc'
         }
-        let argus = JSON.stringify(params)
+        const argus = JSON.stringify(params)
         myWindow.postMessage('{"method":"goToApp","argument":' + argus + '}', '*')
       }, 8000)
     },
     // 纭畾淇敼瀵嗙爜
-    confirmChangePwd() {
+    confirmChangePwd () {
       if (this.isWorking.changePwd) {
         return
       }
@@ -261,7 +270,7 @@
       })
     },
     // 閫�鍑虹櫥褰�
-    logout() {
+    logout () {
       logout()
         .then(() => {
           this.clearUserInfo()
diff --git a/admin/src/components/common/Menu.vue b/admin/src/components/common/Menu.vue
index 02d55e2..2d42e3c 100644
--- a/admin/src/components/common/Menu.vue
+++ b/admin/src/components/common/Menu.vue
@@ -108,7 +108,7 @@
         const height = window.innerHeight
         // console.log('main_app========================锛�'+height)
         const height13 = this.getEleHeghtByClassName('common-header',0)
-        const height5 = document.getElementsByTagName('thead') && document.getElementsByTagName('thead')[0] ? document.getElementsByTagName('thead')[0].clientHeight : 0
+        const height5 = document.getElementsByTagName( 'thead') && document.getElementsByTagName('thead')[0] ? document.getElementsByTagName('thead')[0].clientHeight : 0
         if (document.getElementsByClassName('main_app') && document.getElementsByClassName('main_app')[0]) {
           // console.log('main_app========================')
           // alert(height)
diff --git a/admin/src/components/system/menu/OperaMenuWindow.vue b/admin/src/components/system/menu/OperaMenuWindow.vue
index a367642..73505e7 100644
--- a/admin/src/components/system/menu/OperaMenuWindow.vue
+++ b/admin/src/components/system/menu/OperaMenuWindow.vue
@@ -8,7 +8,7 @@
   >
     <p class="tip" v-if="form.parent != null && form.id == null">涓� <em>{{parentName}}</em> 鏂板缓瀛愯彍鍗�</p>
     <el-form :model="form" ref="form" :rules="rules">
-      <el-form-item label="涓婄骇鑿滃崟" prop="parentId">
+      <el-form-item label="涓婄骇鑿滃崟" prop="parentId"  v-if="form.type !== 1">
         <MenuSelect v-if="visible" v-model="form.parentId" placeholder="璇烽�夋嫨涓婄骇鑿滃崟" :exclude-id="excludeMenuId" clearable :inline="false"/>
       </el-form-item>
       <el-form-item label="鑿滃崟鍚嶇О" prop="name" required>
@@ -20,7 +20,7 @@
       <el-form-item label="鎼哄甫鍙傛暟" prop="params">
         <el-input v-model="form.params" placeholder="璇疯緭鍏ユ惡甯﹀弬鏁�" v-trim maxlength="200"/>
       </el-form-item>
-      <el-form-item label="鍥炬爣" prop="icon" class="form-item-icon">
+      <el-form-item label="鍥炬爣" prop="icon" class="form-item-icon" v-if="form.type !== 1">
         <el-radio-group v-model="form.icon">
           <el-radio :label="icon" v-for="icon in icons" :key="icon">
             <i :class="{[icon]: true}"></i>
@@ -55,9 +55,10 @@
         id: null,
         parentId: null,
         name: '',
-        params:'',
+        params: '',
         path: '',
         icon: '',
+        type: '',
         remark: ''
       },
       // 楠岃瘉瑙勫垯
@@ -74,9 +75,10 @@
      * @target: 缂栬緫鐨勮彍鍗曞璞�
      * @parent: 鏂板缓鏃剁殑涓婄骇鑿滃崟
      */
-    open (title, target, parent) {
+    open (title, target, parent,type) {
       this.title = title
       this.visible = true
+      this.form.type = type || 0
       // 鏂板缓锛宮enu涓虹┖鏃惰〃绀烘柊寤鸿彍鍗�
       if (target == null) {
         this.excludeMenuId = null
@@ -85,6 +87,7 @@
           this.form.id = null
           this.form.parentId = parent == null ? null : parent.id
           this.parentName = parent == null ? null : parent.name
+          // this.form.type = parent == null ? 0 : parent.type
         })
         return
       }
@@ -94,6 +97,7 @@
         for (const key in this.form) {
           this.form[key] = target[key]
         }
+        this.form.type = this.form.type || 0
       })
     }
   },
diff --git a/admin/src/main.js b/admin/src/main.js
index 0cac6e7..0ad3f03 100644
--- a/admin/src/main.js
+++ b/admin/src/main.js
@@ -67,13 +67,22 @@
       this.$store.commit('resetMenus')
       // 鑾峰彇鑿滃崟
       const storeMenus = this.$store.state.menuData.list
+      const storeTopMenus = this.$store.state.topMenuList.list
       if (storeMenus.length > 0 && this.homePage == null) {
         this.setHomePage(storeMenus[0])
       }
       await fetchMenuTree()
-        .then(menus => {
+        .then(allmenus => {
           // 娣诲姞鑿滃崟
+          var menus = allmenus.filter(item => {
+            return item.type !== 1
+          })
+          var topList = allmenus.filter(item => {
+            return item.type === 1
+          })
+          console.log(topList)
           storeMenus.push.apply(storeMenus, menus)
+          storeTopMenus.push.apply(storeTopMenus, topList)
           // 娣诲姞璺敱
           this.__addRouters(storeMenus)
           // 404
diff --git a/admin/src/store/index.js b/admin/src/store/index.js
index 4af82fa..6825b73 100644
--- a/admin/src/store/index.js
+++ b/admin/src/store/index.js
@@ -16,6 +16,12 @@
     // 鏄惁鏀惰捣
     collapse: false
   },
+  topMenuList: {
+    // 鑿滃崟鍒楄〃
+    list: [],
+    // 鏄惁鏀惰捣
+    collapse: false
+  },
   // tags鏁扮粍
   tags: [],
   // tagsview鏍囩鏄剧ず闅愯棌
diff --git a/admin/src/views/system/menu.vue b/admin/src/views/system/menu.vue
index 3eb9fe1..44e1bac 100644
--- a/admin/src/views/system/menu.vue
+++ b/admin/src/views/system/menu.vue
@@ -1,9 +1,17 @@
 <template>
   <TableLayout class="menu-layout" :permissions="['system:menu:query']">
+    <el-form ref="searchForm" slot="search-form"   label-width="100px" inline>
+      <div class="platgroup_tabs">
+        <div class="tab" :class="{ active: activeGroup === item.id }" @click="groupClick(item)"
+             v-for="(item, i) in groupList" :key="i">
+          {{ item.name }}
+        </div>
+      </div>
+    </el-form>
     <!-- 琛ㄦ牸鍜屽垎椤� -->
     <template v-slot:table-wrap>
       <ul class="toolbar" v-permissions="['system:menu:create', 'system:menu:delete', 'system:menu:sort']">
-        <li><el-button type="primary" @click="$refs.operaMenuWindow.open('鏂板缓涓�绾ц彍鍗�')" icon="el-icon-plus" v-permissions="['system:menu:create']">鏂板缓</el-button></li>
+        <li><el-button type="primary" @click="$refs.operaMenuWindow.open(activeGroup==0?'鏂板缓涓�绾ц彍鍗�':'鏂板缓椤堕儴瀵艰埅鑿滃崟',null,null,activeGroup)" icon="el-icon-plus" v-permissions="['system:menu:create']">鏂板缓</el-button></li>
         <li><el-button @click="deleteByIdInBatch" icon="el-icon-delete" v-permissions="['system:menu:delete']">鍒犻櫎</el-button></li>
         <li><el-button @click="sort('top')" :loading="isWorking.sort" icon="el-icon-sort-up" v-permissions="['system:menu:sort']">涓婄Щ</el-button></li>
         <li><el-button @click="sort('bottom')" :loading="isWorking.sort" icon="el-icon-sort-down" v-permissions="['system:menu:sort']">涓嬬Щ</el-button></li>
@@ -20,14 +28,14 @@
         @selection-change="handleSelectionChange"
       >
         <el-table-column type="selection" width="55" fixed="left"></el-table-column>
-        <el-table-column prop="name" label="鑿滃崟鍚嶇О"  fixed="left" min-width="160px"></el-table-column>
-        <el-table-column prop="icon" label="鍥炬爣" min-width="80px" class-name="table-column-icon">
+        <el-table-column prop="name" label="鑿滃崟鍚嶇О"  fixed="left" min-width="160px" show-tooltip-when-overflow></el-table-column>
+        <el-table-column prop="icon" v-if="activeGroup !== 1" label="鍥炬爣" min-width="80px" class-name="table-column-icon">
           <template slot-scope="{row}">
             <i v-if="row.icon != null && row.icon !== ''" :class="{[row.icon]: true}"></i>
             <template v-else>鏈缃�</template>
           </template>
         </el-table-column>
-        <el-table-column prop="path" label="璁块棶璺緞" min-width="140px"></el-table-column>
+        <el-table-column prop="path" label="璁块棶璺緞" show-tooltip-when-overflow min-width="220px"></el-table-column>
         <el-table-column prop="params" label="鍙傛暟" min-width="120px"></el-table-column>
         <el-table-column prop="remark" label="澶囨敞" min-width="120px"></el-table-column>
         <el-table-column prop="createUser" label="鍒涘缓浜�" min-width="100px">
@@ -38,7 +46,7 @@
           <template slot-scope="{row}">{{row.updateUserInfo == null ? '' : row.updateUserInfo.username}}</template>
         </el-table-column>
         <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" min-width="140px"></el-table-column>
-        <el-table-column prop="disabled" label="鏄惁鍚敤" min-width="80px">
+        <el-table-column prop="disabled" label="鏄惁鍚敤" min-width="80px" fixed="right">
           <template slot-scope="{row}">
             <el-switch v-model="row.disabled" :active-value="false" :inactive-value="true" @change="switchDisabled(row)"/>
           </template>
@@ -50,8 +58,8 @@
           fixed="right"
         >
           <template slot-scope="{row}">
-            <el-button type="text" icon="el-icon-edit" @click="$refs.operaMenuWindow.open('缂栬緫鑿滃崟', row)" v-permissions="['system:menu:update']">缂栬緫</el-button>
-            <el-button type="text" icon="el-icon-plus" @click="$refs.operaMenuWindow.open('鏂板缓瀛愯彍鍗�', null, row)" v-permissions="['system:menu:create']">鏂板缓瀛愯彍鍗�</el-button>
+            <el-button type="text" icon="el-icon-edit" @click="$refs.operaMenuWindow.open('缂栬緫鑿滃崟', row,null,activeGroup)" v-permissions="['system:menu:update']">缂栬緫</el-button>
+            <el-button v-if="activeGroup !== 1" type="text" icon="el-icon-plus" @click="$refs.operaMenuWindow.open('鏂板缓瀛愯彍鍗�', null, row,activeGroup)" v-permissions="['system:menu:create']">鏂板缓瀛愯彍鍗�</el-button>
             <el-button v-if="!row.fixed" type="text" icon="el-icon-delete" @click="deleteById(row)" v-permissions="['system:menu:delete']">鍒犻櫎</el-button>
           </template>
         </el-table-column>
@@ -74,16 +82,22 @@
   data () {
     return {
       // 鏄惁姝e湪澶勭悊涓�
+      activeGroup: 0,
+      groupList:[{id:0,name: "鏈嶅姟涓績鑿滃崟"},{id:1,name: "椤堕儴瀵艰埅閰嶇疆"}],
       isWorking: {
         sort: false
       }
     }
   },
   methods: {
+    groupClick(item){
+      this.activeGroup = item.id
+      this.handlePageChange();
+    },
     // 鏌ヨ鏁版嵁
     handlePageChange () {
       this.isWorking.search = true
-      fetchTree()
+      fetchTree({type: this.activeGroup})
         .then(records => {
           this.tableData.list = records
         })
@@ -206,9 +220,29 @@
 
 <style lang="scss" scoped>
 @import "@/assets/style/variables.scss";
+.platgroup_tabs {
+  flex: 1;
+  display: flex;
+  border-bottom: 1px solid #dfe2e8;
+
+  .tab {
+    color: #666666;
+    margin-right: 40px;
+    cursor: pointer;
+    padding-bottom: 18px;
+    border-bottom: 2px solid #fff;
+  }
+
+  .active {
+    font-weight: 500;
+    font-size: 15px;
+    color: #222222;
+    border-bottom: 2px solid $primary-color;
+  }
+}
 .menu-layout {
   /deep/ .table-content {
-    margin-top: 0;
+    margin-top: 0px;
   }
 }
 // 鍥炬爣鍒�

--
Gitblit v1.9.3