Navbar.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <template>
  2. <div class="navbar">
  3. <!-- <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> -->
  4. <router-link
  5. key="collapse"
  6. style="display: flex; align-items: center; padding-left: 30px"
  7. to="/"
  8. >
  9. <img
  10. src="@/assets/images/base/logo.png"
  11. class="sidebar-logo"
  12. style="width: 129px; height: 37px"
  13. />
  14. </router-link>
  15. <!-- <breadcrumb class="breadcrumb-container" /> -->
  16. <div class="indexlayout-top-menu">
  17. <!-- :class="{'active': getTopMenuActive === route.path}" -->
  18. <!-- el-scrollbar -->
  19. <el-scrollbar
  20. class="horizontal-scrollbar"
  21. style="overflow: hidden; height: 100%"
  22. >
  23. <template v-for="route in permission_routes">
  24. <app-link
  25. v-if="!route.hidden"
  26. :to="route.path"
  27. :key="route.id"
  28. class="indexlayout-top-menu-li"
  29. :class="{ active: getTopMenuActive === route.path }"
  30. >
  31. <span>{{ route.meta.title }}</span>
  32. </app-link>
  33. </template>
  34. </el-scrollbar>
  35. </div>
  36. <div class="right-menu">
  37. <el-popover
  38. placement="bottom"
  39. trigger="hover"
  40. style="display: flex; height: 89px"
  41. >
  42. <div class="popover-container" style="text-align: center">OA审批</div>
  43. <div
  44. style="
  45. display: flex;
  46. align-items: center;
  47. justify-content: center;
  48. height: 89px;
  49. "
  50. class="msginfo"
  51. @click="gotoOa"
  52. slot="reference"
  53. >
  54. <img
  55. src="@/assets/images/base/icon_oa.png"
  56. width="24px"
  57. height="24px"
  58. />
  59. <!-- <div class="active"></div> -->
  60. </div>
  61. </el-popover>
  62. <el-popover
  63. v-if="isShowIns"
  64. placement="bottom"
  65. trigger="hover"
  66. style="display: flex; height: 89px"
  67. >
  68. <div class="popover-container" style="text-align: center">操作手册</div>
  69. <div
  70. style="
  71. display: flex;
  72. align-items: center;
  73. justify-content: center;
  74. height: 89px;
  75. "
  76. class="msginfo"
  77. @click="openIns"
  78. slot="reference"
  79. >
  80. <img
  81. src="@/assets/images/base/instruction-icon.png"
  82. width="24px"
  83. height="24px"
  84. />
  85. <!-- <div class="active"></div> -->
  86. </div>
  87. </el-popover>
  88. <el-popover
  89. placement="bottom"
  90. trigger="hover"
  91. style="display: flex; height: 89px"
  92. >
  93. <div class="popover-container" style="text-align: center">系统日志</div>
  94. <div
  95. style="
  96. display: flex;
  97. align-items: center;
  98. justify-content: center;
  99. height: 89px;
  100. "
  101. class="msginfo"
  102. v-permission="'/journal'"
  103. @click="gotoRecode"
  104. slot="reference"
  105. >
  106. <img
  107. src="@/assets/images/base/base-bell.png"
  108. width="24px"
  109. height="24px"
  110. />
  111. <!-- <div class="active"></div> -->
  112. </div>
  113. </el-popover>
  114. <div class="left-menu">
  115. <el-popover
  116. placement="top-start"
  117. width="300"
  118. trigger="hover"
  119. style="display: flex; height: 89px"
  120. >
  121. <div class="popover-container">
  122. <el-tag
  123. class="navbar_tag"
  124. type="info"
  125. v-for="item in organNameList"
  126. :key="item"
  127. >{{ item }}</el-tag
  128. >
  129. </div>
  130. <span
  131. slot="reference"
  132. class="msginfo"
  133. style="
  134. display: flex;
  135. align-items: center;
  136. justify-content: center;
  137. height: 89px;
  138. "
  139. >
  140. <!-- {{ organName.length > 10 ? organName.substr(0, 10) + "..." : organName }} -->
  141. <!-- <i class="el-icon-s-home" style="font-size: 23px; color: #1A1A1A;"></i> -->
  142. <img
  143. src="@/assets/images/base/base-home.png"
  144. width="24px"
  145. height="24px"
  146. />
  147. </span>
  148. </el-popover>
  149. </div>
  150. <el-dropdown class="avatar-container" trigger="click">
  151. <div class="avatar-wrapper">
  152. <img
  153. v-if="$store.getters.avatar"
  154. :src="$store.getters.avatar"
  155. class="user-avatar"
  156. />
  157. <img
  158. v-else
  159. class="user-avatar"
  160. src="@/assets/images/base/placehorder-icon.png"
  161. />
  162. <!-- <i class="el-icon-caret-bottom" /> -->
  163. <span>{{ username }}</span>
  164. </div>
  165. <el-dropdown-menu slot="dropdown" class="user-dropdown">
  166. <!-- divided -->
  167. <el-dropdown-item v-if="tenantStatus">
  168. <span style="display: block" @click="onTenantChange">{{ tenantName }} <i class="el-icon-sort"></i></span>
  169. </el-dropdown-item>
  170. <el-dropdown-item>
  171. <span style="display: block" @click="resetPassWord">修改密码</span>
  172. </el-dropdown-item>
  173. <el-dropdown-item>
  174. <span style="display: block" @click="accountSetting">账号设置</span>
  175. </el-dropdown-item>
  176. <el-dropdown-item>
  177. <span style="display: block" @click="logout">安全退出</span>
  178. </el-dropdown-item>
  179. </el-dropdown-menu>
  180. </el-dropdown>
  181. </div>
  182. <el-dialog
  183. title="修改密码"
  184. width="500px"
  185. append-to-body
  186. :visible.sync="resetVisible"
  187. >
  188. <el-form
  189. :model="resetForm"
  190. label-position="right"
  191. label-width="100px"
  192. ref="pwdForm"
  193. >
  194. <el-form-item label="手机号" prop="phone">
  195. <div>{{ this.$store.getters.phone }}</div>
  196. </el-form-item>
  197. <el-form-item
  198. label="新密码"
  199. :rules="[
  200. { required: true, message: '密码不能为空', trigger: 'blur' },
  201. {
  202. pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/,
  203. message: '密码为6-20位数字和字母组合',
  204. trigger: 'blur',
  205. },
  206. ]"
  207. prop="password"
  208. >
  209. <el-input
  210. v-model.trim="resetForm.password"
  211. type="password"
  212. style="width: 180px"
  213. autocomplete="off"
  214. ></el-input>
  215. </el-form-item>
  216. <el-form-item
  217. label="再次输入"
  218. :rules="[
  219. { required: true, message: '密码不能为空', trigger: 'blur' },
  220. {
  221. pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/,
  222. message: '密码为6-20位数字和字母组合',
  223. trigger: 'blur',
  224. },
  225. ]"
  226. prop="password2"
  227. >
  228. <el-input
  229. v-model.trim="resetForm.password2"
  230. type="password"
  231. style="width: 180px"
  232. autocomplete="off"
  233. ></el-input>
  234. </el-form-item>
  235. <el-form-item
  236. label="验证码"
  237. :rules="[
  238. { required: true, message: '验证码不能为空', trigger: 'blur' },
  239. ]"
  240. prop="authCode"
  241. style=""
  242. >
  243. <el-input
  244. v-model.trim="resetForm.authCode"
  245. style="width: 180px"
  246. autocomplete="off"
  247. ></el-input>
  248. <el-button :disabled="isDisable" @click="getCode">{{
  249. btnName
  250. }}</el-button>
  251. </el-form-item>
  252. </el-form>
  253. <div slot="footer" class="dialog-footer">
  254. <el-button @click="resetVisible = false">取 消</el-button>
  255. <el-button type="primary" @click="submitResetPassWord">确 定</el-button>
  256. </div>
  257. </el-dialog>
  258. <el-dialog
  259. title="切换机构"
  260. width="500px"
  261. append-to-body
  262. v-if="tenantVisible"
  263. :visible.sync="tenantVisible"
  264. >
  265. <el-form
  266. :model="tenantForm"
  267. label-position="right"
  268. label-width="100px"
  269. ref="tenantForm"
  270. >
  271. <el-form-item
  272. label="选择机构"
  273. :rules="[
  274. { required: true, message: '请选择机构', trigger: 'change' }
  275. ]"
  276. prop="tenantId"
  277. >
  278. <el-select
  279. v-model.trim="tenantForm.tenantId"
  280. filterable
  281. placeholder="请选择机构"
  282. clearable
  283. style="width: 100% !important"
  284. >
  285. <el-option v-for="(item, index) in tenantList" :key="index" :label="item.name" :value="item.id"></el-option>
  286. </el-select>
  287. </el-form-item>
  288. </el-form>
  289. <div slot="footer" class="dialog-footer">
  290. <el-button @click="tenantVisible = false">取 消</el-button>
  291. <el-button type="primary" @click="submitTenant">确 定</el-button>
  292. </div>
  293. </el-dialog>
  294. <portal-target name="AppMain" ref="target">
  295. <instructions ref="instructions" @checkShow="checkShow" />
  296. </portal-target>
  297. </div>
  298. </template>
  299. <script>
  300. import qs from "qs";
  301. import Logo from "./Sidebar/Logo";
  302. import { mapGetters } from "vuex";
  303. // import Breadcrumb from '@/components/Breadcrumb'
  304. // import Hamburger from '@/components/Hamburger'
  305. import { resetPassword } from "@/api/buildTeam";
  306. import AppLink from "./Sidebar/Link";
  307. import { getBelongTopMenuPath } from "@/utils/permission";
  308. import instructions from "./instructions";
  309. import { validOaUrl } from '@/utils/validate'
  310. import { tenantInfoQueryPage } from '@/views/organManager/api'
  311. import Cookies from 'js-cookie'
  312. import axios from 'axios'
  313. export default {
  314. data() {
  315. const tenantId = sessionStorage.getItem('tenantId')
  316. const tenantName = sessionStorage.getItem('tenantName')
  317. return {
  318. username: "",
  319. organName: this.$store.getters.organName,
  320. organNameList: [],
  321. resetVisible: false,
  322. resetForm: {
  323. phone: "",
  324. authCode: "",
  325. password: "",
  326. password2: "",
  327. },
  328. isDisable: false, // 是否允许发送验证码
  329. timerCount: 60,
  330. btnName: "获取验证码",
  331. isShowIns: false,
  332. tenantVisible: false,
  333. tenantName: tenantName || null,
  334. tenantForm: {
  335. tenantId: Number(tenantId) || null,
  336. },
  337. tenantList: []
  338. };
  339. },
  340. components: {
  341. AppLink,
  342. instructions,
  343. // Breadcrumb,
  344. // Hamburger
  345. },
  346. computed: {
  347. ...mapGetters(["sidebar", "avatar", "permission_routes"]),
  348. getTopMenuActive() {
  349. let route = this.$route;
  350. // (route, getBelongTopMenuPath(route))
  351. return getBelongTopMenuPath(route);
  352. },
  353. tenantStatus() { // 判断是否是平台账号 true 是
  354. const baseTenantId = sessionStorage.getItem('baseTenantId')
  355. return baseTenantId < 0 ? true : false
  356. },
  357. },
  358. mounted() {
  359. // 手动加入
  360. this.toggleSideBar();
  361. this.username = this.$store.getters.name;
  362. this.organNameList = this.organName.split(",") || [];
  363. },
  364. methods: {
  365. toggleSideBar() {
  366. this.$store.dispatch("app/toggleSideBar");
  367. },
  368. async logout() {
  369. await this.$store.dispatch("user/logout");
  370. localStorage.removeItem("firstMenuUrl");
  371. // await this.$store.dispatch("permission/removePermission")
  372. this.$router.push(`/login`);
  373. window.location.reload();
  374. },
  375. async onTenantChange() {
  376. try {
  377. const res = await tenantInfoQueryPage({ page: 1, rows: 999 }, )
  378. this.tenantList = res.data?.rows || []
  379. this.tenantVisible = true
  380. } catch(e) {}
  381. },
  382. accountSetting() {
  383. // 账号设置
  384. this.$router.push('/tenantSetting/tenantInfoSetting')
  385. },
  386. submitTenant() {
  387. this.$refs['tenantForm'].validate((res) => {
  388. if(res) {
  389. const tenantForm = this.tenantForm
  390. let tenantName = null
  391. this.tenantList.forEach(item => {
  392. if(item.id == tenantForm.tenantId) {
  393. tenantName = item.name
  394. }
  395. });
  396. const tenantId = sessionStorage.getItem('tenantId')
  397. if(tenantId != tenantForm.tenantId) {
  398. //判断是否是当前路由
  399. // if(url == this.$route.path) {
  400. sessionStorage.setItem('tenantId', tenantForm.tenantId)
  401. sessionStorage.setItem('tenantName', tenantName)
  402. this.$router.push({
  403. path: '/redirect',
  404. query: this.$route.fullPath
  405. })
  406. // } else {
  407. // this.$router.push({
  408. // path: url
  409. // })
  410. // }
  411. }
  412. }
  413. })
  414. },
  415. gotoRecode() {
  416. this.$router.push("/journal/journal");
  417. },
  418. resetPassWord() {
  419. this.resetVisible = true;
  420. },
  421. submitResetPassWord() {
  422. if (this.resetForm.password !== this.resetForm.password2) {
  423. this.$message.error("两次密码必须相同");
  424. return;
  425. }
  426. this.$refs["pwdForm"].validate((res) => {
  427. if (res) {
  428. // 发请求
  429. resetPassword({
  430. authCode: this.resetForm.authCode,
  431. mobile: this.$store.getters.phone,
  432. newPassword: this.resetForm.password,
  433. }).then((res) => {
  434. if (res.code == 200) {
  435. // 修改成功
  436. this.$message.success("修改成功");
  437. this.logout();
  438. }
  439. });
  440. }
  441. });
  442. },
  443. getCode() {
  444. // 获取验证码
  445. if (!this.$store.getters.phone) {
  446. this.$message.error("请输入正确的手机号");
  447. return;
  448. }
  449. if (!this.isDisable) {
  450. this.isDisable = true;
  451. // 发请求成功后开启定时器
  452. // 发送验证码
  453. axios
  454. .post(
  455. "/api-web/code/sendSms",
  456. qs.stringify({ mobile: this.$store.getters.phone })
  457. )
  458. .then((res) => {
  459. if (res.data.code == 200) {
  460. let timer = setInterval((res) => {
  461. if (this.timerCount <= 0) {
  462. clearInterval(timer);
  463. this.isDisable = false;
  464. this.btnName = "获取验证码";
  465. this.timerCount = 60;
  466. } else {
  467. this.timerCount--;
  468. this.btnName = `${this.timerCount}s后重试`;
  469. }
  470. }, 1000);
  471. }
  472. });
  473. }
  474. },
  475. openIns() {
  476. this.$refs.instructions.showInstructions();
  477. },
  478. checkShow(val) {
  479. this.isShowIns = val;
  480. },
  481. gotoOa(){
  482. // console.log(validOaUrl())
  483. // const Token = Cookies.get('cross-Token')
  484. // console.log(Token, validOaUrl().split('//')[1])
  485. // Cookies.set('Admin-Token', Token, { domain: `.${validOaUrl().split('//')[1]}`, path: '/' })
  486. // document.cookie = `Adminoken=${Token};domain=oadev.dayaedu.com;`
  487. window.open(validOaUrl())
  488. }
  489. },
  490. watch: {
  491. resetVisible(val) {
  492. if (!val) {
  493. this.resetForm = {
  494. phone: "",
  495. authCode: "",
  496. password: "",
  497. password2: "",
  498. };
  499. }
  500. },
  501. },
  502. };
  503. </script>
  504. <style lang="scss" scoped>
  505. .navbar_tag {
  506. margin: 0 5px 8px;
  507. }
  508. .indexlayout-top-menu {
  509. padding-left: 57px;
  510. height: 90px;
  511. line-height: 88px;
  512. flex: 1;
  513. display: flex;
  514. overflow: hidden;
  515. /* overflow-x: auto; */
  516. .indexlayout-top-menu-li {
  517. display: inline-block;
  518. padding: 0 5px;
  519. height: 90px;
  520. text-decoration: none;
  521. color: #f2f2f2;
  522. font-size: 16px;
  523. transition: all 0.3s ease;
  524. span {
  525. // display: block;
  526. transition: all 0.3s ease;
  527. padding: 10px 20px;
  528. }
  529. &:hover,
  530. &.active {
  531. span {
  532. background: #fff;
  533. border-radius: 6px;
  534. }
  535. }
  536. &.active span {
  537. font-weight: bold;
  538. }
  539. }
  540. .breadcrumb {
  541. line-height: 90px;
  542. margin-left: 10px;
  543. .el-breadcrumb__item {
  544. display: inline-block;
  545. float: none;
  546. }
  547. }
  548. }
  549. .popover-container {
  550. max-height: 350px;
  551. overflow-y: scroll;
  552. }
  553. .navbar {
  554. display: flex;
  555. flex-direction: row;
  556. justify-content: space-between;
  557. height: 90px;
  558. overflow: hidden;
  559. position: relative;
  560. z-index: 2000;
  561. background: #14928a;
  562. box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.1);
  563. h2 {
  564. font-size: 18px;
  565. line-height: 90px;
  566. margin: 0 0 0 30px;
  567. display: inline-block;
  568. }
  569. .hamburger-container {
  570. line-height: 90px;
  571. height: 100%;
  572. float: left;
  573. cursor: pointer;
  574. transition: background 0.3s;
  575. -webkit-tap-highlight-color: transparent;
  576. &:hover {
  577. background: rgba(0, 0, 0, 0.025);
  578. }
  579. }
  580. .breadcrumb-container {
  581. float: left;
  582. }
  583. .left-menu {
  584. line-height: 90px;
  585. // padding-right: 22px;
  586. font-size: 16px;
  587. color: #fff;
  588. .topIcon {
  589. width: 20px;
  590. height: 25px;
  591. }
  592. }
  593. .right-menu {
  594. min-width: 154px;
  595. float: right;
  596. height: 100%;
  597. line-height: 90px;
  598. display: flex;
  599. flex-direction: row;
  600. justify-content: flex-start;
  601. &:focus {
  602. outline: none;
  603. }
  604. .msginfo.ins {
  605. img {
  606. width: 18px;
  607. height: 23px;
  608. }
  609. }
  610. .msginfo {
  611. display: flex;
  612. flex-direction: row;
  613. justify-content: flex-start;
  614. align-items: center;
  615. padding-right: 25px;
  616. position: relative;
  617. cursor: pointer;
  618. img {
  619. width: 24px;
  620. height: 24px;
  621. }
  622. .active {
  623. position: absolute;
  624. width: 7px;
  625. height: 7px;
  626. background-color: #f97215;
  627. border-radius: 50%;
  628. top: 20px;
  629. right: -4px;
  630. }
  631. }
  632. .right-menu-item {
  633. display: inline-block;
  634. padding: 0 8px;
  635. height: 100%;
  636. font-size: 14px;
  637. color: #5a5e66;
  638. vertical-align: text-bottom;
  639. &.hover-effect {
  640. cursor: pointer;
  641. transition: background 0.3s;
  642. &:hover {
  643. background: rgba(0, 0, 0, 0.025);
  644. }
  645. }
  646. }
  647. .avatar-container {
  648. height: 90px;
  649. margin-right: 42px;
  650. cursor: pointer;
  651. .avatar-wrapper {
  652. position: relative;
  653. display: flex;
  654. flex-direction: row;
  655. justify-content: flex-start;
  656. align-items: center;
  657. span {
  658. margin-left: 8px;
  659. font-size: 14px;
  660. font-weight: 500;
  661. // color: rgba(68, 68, 68, 1);
  662. color: #fff;
  663. }
  664. .user-avatar {
  665. cursor: pointer;
  666. width: 32px;
  667. height: 32px;
  668. border: 2px solid #f0f2f5;
  669. border-radius: 50%;
  670. }
  671. .el-icon-caret-bottom {
  672. cursor: pointer;
  673. position: absolute;
  674. right: -20px;
  675. top: 25px;
  676. font-size: 14px;
  677. }
  678. }
  679. }
  680. }
  681. }
  682. </style>