diff --git a/package.json b/package.json index 1832293..46f88af 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "clipboard": "2.0.8", "core-js": "3.37.1", "decimal.js": "^10.4.3", + "driver.js": "^1.3.5", "echarts": "5.4.0", "element-ui": "2.15.14", "file-saver": "2.0.5", diff --git a/public/favicon.ico b/public/favicon.ico index 05726a9..89f0042 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/api/system/user.js b/src/api/system/user.js index 3ce258b..bab6628 100644 --- a/src/api/system/user.js +++ b/src/api/system/user.js @@ -152,3 +152,12 @@ export function deptTreeSelect() { method: 'get' }) } + +// 已读导览 +export function readGuide(key) { + return request({ + url: '/system/user/guide', + method: 'put', + params: { key } + }) +} diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png index 4856c70..13836f1 100644 Binary files a/src/assets/logo/logo.png and b/src/assets/logo/logo.png differ diff --git a/src/components/Avatar/index.vue b/src/components/Avatar/index.vue index 21c6031..4067106 100644 --- a/src/components/Avatar/index.vue +++ b/src/components/Avatar/index.vue @@ -72,13 +72,16 @@ export default { align-items: center; justify-content: center; border-radius: 50%; + overflow: hidden; + vertical-align: middle; ::v-deep .el-avatar { - display: flex; + display: inline-flex; align-items: center; justify-content: center; background-color: inherit; font-size: 16px; + vertical-align: middle; } } \ No newline at end of file diff --git a/src/components/Business/Area/AreaLink.vue b/src/components/Business/Area/AreaLink.vue new file mode 100644 index 0000000..452554d --- /dev/null +++ b/src/components/Business/Area/AreaLink.vue @@ -0,0 +1,32 @@ + + + \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index 5b6b676..442ed66 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -121,6 +121,12 @@ export const constantRoutes = [ component: () => import('@/views/system/user/view/view.vue'), name: 'UserView', meta: { title: '用户详情' } + }, + { + path: 'area/:id?', + component: () => import('@/views/bst/area/view/view.vue'), + name: 'AreaView', + meta: { title: '运营区详情' } } ] }, diff --git a/src/store/getters.js b/src/store/getters.js index 74490a9..44b63b0 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -10,6 +10,7 @@ const getters = { userId: state => state.user.id, name: state => state.user.name, nickName: state => state.user.nickName, + guides: state => state.user.guides, introduction: state => state.user.introduction, roles: state => state.user.roles, permissions: state => state.user.permissions, diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 270b02b..90f6f4f 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,4 +1,5 @@ import { getInfo, login, logout } from '@/api/login'; +import { readGuide } from '@/api/system/user'; import { getToken, removeToken, setToken } from '@/utils/auth'; // import { Watermark } from '@pansy/watermark'; @@ -15,6 +16,7 @@ const user = { permissions: [], deptId: null, userType: null, + guides: [], }, mutations: { @@ -41,31 +43,12 @@ const user = { }, SET_NICK_NAME: (state, nickName) => { state.nickName = nickName; - - // if (watermark) { - // watermark.destroy(); - // } - // // 创建水印 - // watermark = new Watermark({ - // // 水印文案,支持多行 - // text: nickName, - // // 水印文案样式 - // font: { - // fontSize: 16, - // color: 'rgba(0, 0, 0)', - // fontFamily: 'sans-serif' - // }, - // // 水印之间的间距 - // gap: [300, 300], - // // 旋转角度 - // rotate: -20, - // // 设置水印层级 - // zIndex: 9999, - // opacity: 0.1, - // }) }, SET_USER_TYPE: (state, userType) => { state.userType = userType; + }, + SET_GUIDES: (state, guides) => { + state.guides = guides; } }, @@ -105,6 +88,7 @@ const user = { commit('SET_AVATAR', avatar) commit('SET_DEPT_ID', user.deptId) commit('SET_USER_TYPE', user.userType) + commit('SET_GUIDES', user.guides) resolve(res) }).catch(error => { reject(error) @@ -134,6 +118,14 @@ const user = { removeToken() resolve() }) + }, + // 添加导览 + ADD_GUIDES({commit, state}, key) { + return new Promise((resolve, reject) => { + readGuide(key).then(() => { + commit('SET_GUIDES', [...state.guides, key]) + }); + }); } } } diff --git a/src/utils/guide.js b/src/utils/guide.js new file mode 100644 index 0000000..49d0dbb --- /dev/null +++ b/src/utils/guide.js @@ -0,0 +1,161 @@ +/** + * 用户引导 + */ + +import store from "@/store"; +import { driver } from "driver.js"; +import "driver.js/dist/driver.css"; +export function startGuide(guideKey) { + let guides = store.getters.guides; + if (guides == null || !guides.includes(guideKey)) { + let guide = guideSet[guideKey]; + if (guide) { + guide.start(); + } + } +} + +const guideSet = { + MchIndex: { + start() { + const driverObj = driver({ + showProgress: true, + animate: true, + nextBtnText: '下一步', + prevBtnText: '上一步', + doneBtnText: '完成', + closeBtnText: '关闭', + allowClose: false, + onDestroyed: () => { + // 导览完成后调用接口 + store.dispatch("ADD_GUIDES", "MchIndex"); + }, + steps: [ + { + popover: { + title: '欢迎使用小鹿骑行管理系统', + description: '接下来我们将带您了解系统的主要功能区域', + side: "center", + align: 'center' + } + }, + { + element: '#mch-stat', + popover: { + title: '运营统计', + description: '这里显示您的运营数据统计信息,包括订单数量、金额等关键指标。', + side: "bottom", + align: 'start' + } + }, + { + element: '#device-stat', + popover: { + title: '车辆统计', + description: '这里展示设备相关的统计信息,包括设备总数、在线状态等。', + side: "bottom", + align: 'start' + } + }, + { + element: '#order-daily-stat', + popover: { + title: '每日流水统计', + description: '这里展示每日的订单流水统计图表,帮助您了解业务趋势。', + side: "left", + align: 'start' + } + }, + { + element: '#balance-panel', + popover: { + title: '账户信息', + description: '这里显示您的账户余额、待分润金额等信息,方便您随时查看收益情况。', + side: "right", + align: 'start' + } + }, + { + popover: { + title: '小程序管理', + description: ` +
+

您还可以通过小鹿骑行小程序随时随地管理您的业务,扫码即可体验。

+ +
+ `, + side: "center", + align: 'center' + } + } + ] + }); + + driverObj.drive(); + } + }, + AreaSub: { + start() { + const driverObj = driver({ + showProgress: true, + animate: true, + nextBtnText: '下一步', + prevBtnText: '上一步', + doneBtnText: '完成', + closeBtnText: '关闭', + allowClose: false, + onDestroyed: () => { + store.dispatch("ADD_GUIDES", "AreaSub"); + }, + steps: [ + { + popover: { + title: '子区域管理', + description: '欢迎使用子区域管理功能,这里您可以管理运营区下的子区域和电子围栏。', + side: "center", + align: 'center' + } + }, + { + element: '.sub-area-list', + popover: { + title: '子区域列表', + description: '左侧显示当前运营区下的所有子区域列表,您可以在这里查看和管理子区域。', + side: "right", + align: 'start' + } + }, + { + element: '.map-container', + popover: { + title: '地图操作区', + description: '右侧地图区域是您的主要操作区域,您可以在这里进行以下操作:管理子区域、修改电子围栏、查看区域详情。', + side: "left", + align: 'start' + } + }, + { + element: '#add-sub', + popover: { + title: '新增子区域', + description: '点击按钮,即可开始在地图上绘制区域(停车区、禁行区、禁停区)', + side: "left", + align: 'start' + } + }, + { + element: '#area-edit', + popover: { + title: '电子围栏操作', + description: '点击此处可以编辑电子围栏', + side: "left", + align: 'start' + } + } + ] + }); + + driverObj.drive(); + } + } +} diff --git a/src/views/bst/agreement/index.vue b/src/views/bst/agreement/index.vue index 346225a..c537ab8 100644 --- a/src/views/bst/agreement/index.vue +++ b/src/views/bst/agreement/index.vue @@ -76,7 +76,11 @@ - + + + @@ -192,6 +196,7 @@ import Editor from "@/components/Editor/index.vue"; import AreaRemoteSelect from "@/components/Business/Area/AreaRemoteSelect.vue"; import UserLink from "@/components/Business/User/UserLink.vue"; import {isSysAdmin} from "@/utils/permission"; +import AreaLink from '@/components/Business/Area/AreaLink.vue' // 默认排序字段 const defaultSort = { @@ -203,7 +208,13 @@ export default { name: "Agreement", mixins: [$showColumns], dicts: ['agreement_type', 'agreement_content_type'], - components: {FormCol, Editor, AreaRemoteSelect, UserLink}, + components: {FormCol, Editor, AreaRemoteSelect, UserLink, AreaLink}, + props: { + query: { + type: Object, + default: () => ({}) + }, + }, data() { return { span: 24, @@ -326,6 +337,7 @@ export default { }, }, created() { + Object.assign(this.queryParams, this.query); this.getList(); }, methods: { diff --git a/src/views/bst/area/index.vue b/src/views/bst/area/index.vue index 2a9fbe0..a18e895 100644 --- a/src/views/bst/area/index.vue +++ b/src/views/bst/area/index.vue @@ -104,7 +104,7 @@ {{d.row[column.key]}} + + \ No newline at end of file diff --git a/src/views/bst/areaJoin/index.vue b/src/views/bst/areaJoin/index.vue index 522a857..627a2df 100644 --- a/src/views/bst/areaJoin/index.vue +++ b/src/views/bst/areaJoin/index.vue @@ -12,7 +12,7 @@ + @@ -149,7 +152,7 @@ :id="editId" :init-data="initData" @success="getList" - :types="types" + :types="queryParams.types" /> @@ -160,7 +163,8 @@ import { $showColumns } from '@/utils/mixins'; import AreaJoinEditDialog from './components/AreaJoinEditDialog'; import UserLink from '@/components/Business/User/UserLink.vue'; import AreaRemoteSelect from '@/components/Business/Area/AreaRemoteSelect.vue'; - +import { isEmpty } from '@/utils'; +import AreaLink from '@/components/Business/Area/AreaLink.vue'; // 默认排序字段 const defaultSort = { prop: "createTime", @@ -171,7 +175,13 @@ export default { name: "AreaJoin", mixins: [$showColumns], dicts: ['area_join_type', 'area_join_permission'], - components: { AreaJoinEditDialog, UserLink, AreaRemoteSelect }, + components: { AreaJoinEditDialog, UserLink, AreaRemoteSelect, AreaLink }, + props: { + query: { + type: Object, + default: null + }, + }, data() { return { // 字段列表 @@ -218,14 +228,18 @@ export default { areaId: null, userId: null, remark: null, - createId: null + createId: null, + types: [] }, - types: [], // 类型列表 }; }, created() { this.types = this.$route.query?.types?.split(',') || []; + if (!isEmpty(this.propTypes)) { + this.types = this.propTypes; + } this.queryParams.types = this.types; + Object.assign(this.queryParams, this.query); this.getList(); }, methods: { diff --git a/src/views/bst/areaSub/components/AreaMap.vue b/src/views/bst/areaSub/components/AreaMap.vue index 1cc5412..5b1817f 100644 --- a/src/views/bst/areaSub/components/AreaMap.vue +++ b/src/views/bst/areaSub/components/AreaMap.vue @@ -9,8 +9,8 @@
- 子区域 - 电子围栏 + 子区域 + 电子围栏 全局查看 切换样式 {{ showLabels ? '隐藏' : '显示' }}标签 @@ -300,7 +300,6 @@ export default { position: absolute; top: 15px; right: 15px; - z-index: 100; } .boundary-tools { diff --git a/src/views/bst/areaSub/index.vue b/src/views/bst/areaSub/index.vue index 85e2afa..51daaad 100644 --- a/src/views/bst/areaSub/index.vue +++ b/src/views/bst/areaSub/index.vue @@ -186,6 +186,7 @@ import { getArea, updateArea } from '@/api/bst/area'; import { AreaSubStatus, AreaSubType } from '@/utils/enums'; import AreaMap from '@/views/bst/areaSub/components/AreaMap.vue'; import AreaSubEditDialog from '@/views/bst/areaSub/components/AreaSubEditDialog.vue'; +import {startGuide} from "@/utils/guide" // 默认排序字段 const defaultSort = { @@ -198,6 +199,12 @@ export default { mixins: [$showColumns], dicts: ['area_sub_type', 'area_sub_status'], components: {FormCol, AreaMap, AreaSubEditDialog}, + props: { + areaId: { + type: String, + default: null + } + }, data() { return { AreaSubStatus, @@ -271,9 +278,17 @@ export default { }; }, created() { - this.queryParams.areaId = this.$route.params.areaId; + if (this.areaId) { + this.queryParams.areaId = this.areaId; + } else { + this.queryParams.areaId = this.$route.params.areaId; + } this.getArea(); this.getList(); + + this.$nextTick(()=> { + startGuide("AreaSub"); + }); }, methods: { handleRowClick(row) { diff --git a/src/views/bst/customerService/index.vue b/src/views/bst/customerService/index.vue index 69bc4e5..dea0d07 100644 --- a/src/views/bst/customerService/index.vue +++ b/src/views/bst/customerService/index.vue @@ -88,6 +88,9 @@ + -