diff --git a/src/components/customPic/index.vue b/src/components/customPic/index.vue new file mode 100644 index 0000000..6382230 --- /dev/null +++ b/src/components/customPic/index.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/src/components/exportExcel/exportExcel.vue b/src/components/exportExcel/exportExcel.vue new file mode 100644 index 0000000..26ceb18 --- /dev/null +++ b/src/components/exportExcel/exportExcel.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/exportExcel/exportTemplate.vue b/src/components/exportExcel/exportTemplate.vue new file mode 100644 index 0000000..9e099eb --- /dev/null +++ b/src/components/exportExcel/exportTemplate.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/components/exportExcel/importExcel.vue b/src/components/exportExcel/importExcel.vue new file mode 100644 index 0000000..30eef29 --- /dev/null +++ b/src/components/exportExcel/importExcel.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/office/docx.vue b/src/components/office/docx.vue new file mode 100644 index 0000000..35e3e5a --- /dev/null +++ b/src/components/office/docx.vue @@ -0,0 +1,35 @@ + + + + diff --git a/src/components/office/excel.vue b/src/components/office/excel.vue new file mode 100644 index 0000000..ac449da --- /dev/null +++ b/src/components/office/excel.vue @@ -0,0 +1,33 @@ + + + + \ No newline at end of file diff --git a/src/components/office/index.vue b/src/components/office/index.vue new file mode 100644 index 0000000..a6704d6 --- /dev/null +++ b/src/components/office/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/office/pdf.vue b/src/components/office/pdf.vue new file mode 100644 index 0000000..8098f06 --- /dev/null +++ b/src/components/office/pdf.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/src/components/richtext/rich-edit.vue b/src/components/richtext/rich-edit.vue new file mode 100644 index 0000000..cee3b2e --- /dev/null +++ b/src/components/richtext/rich-edit.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/richtext/rich-view.vue b/src/components/richtext/rich-view.vue new file mode 100644 index 0000000..fd6616e --- /dev/null +++ b/src/components/richtext/rich-view.vue @@ -0,0 +1,62 @@ + + + + diff --git a/src/components/selectFile/selectFile.vue b/src/components/selectFile/selectFile.vue new file mode 100644 index 0000000..c85b652 --- /dev/null +++ b/src/components/selectFile/selectFile.vue @@ -0,0 +1,85 @@ + + + + diff --git a/src/components/selectImage/selectImage.vue b/src/components/selectImage/selectImage.vue new file mode 100644 index 0000000..a08393d --- /dev/null +++ b/src/components/selectImage/selectImage.vue @@ -0,0 +1,485 @@ + + + + + diff --git a/src/components/svgIcon/svgIcon.vue b/src/components/svgIcon/svgIcon.vue new file mode 100644 index 0000000..c6f7665 --- /dev/null +++ b/src/components/svgIcon/svgIcon.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/upload/common.vue b/src/components/upload/common.vue new file mode 100644 index 0000000..6234917 --- /dev/null +++ b/src/components/upload/common.vue @@ -0,0 +1,75 @@ + + + + diff --git a/src/components/upload/image.vue b/src/components/upload/image.vue new file mode 100644 index 0000000..e643fa8 --- /dev/null +++ b/src/components/upload/image.vue @@ -0,0 +1,97 @@ + + + + + + diff --git a/src/components/warningBar/warningBar.vue b/src/components/warningBar/warningBar.vue new file mode 100644 index 0000000..1a88c1e --- /dev/null +++ b/src/components/warningBar/warningBar.vue @@ -0,0 +1,33 @@ + + diff --git a/src/core/admin.js b/src/core/admin.js new file mode 100644 index 0000000..4cd23b7 --- /dev/null +++ b/src/core/admin.js @@ -0,0 +1,12 @@ +/* + * web框架组 + * + * */ +// 加载网站配置文件夹 +import { register } from './global' + +export default { + install: (app) => { + register(app) + } +} diff --git a/src/core/config.js b/src/core/config.js new file mode 100644 index 0000000..72f1281 --- /dev/null +++ b/src/core/config.js @@ -0,0 +1,16 @@ +/** + * 网站配置文件 + */ + +const config = { + appName: '利农天下', + appLogo: '', + showViteLogo: true, + logs: [], +} + +export const viteLogo = (env) => { + console.log('哈哈哈') +} + +export default config diff --git a/src/core/global.js b/src/core/global.js new file mode 100644 index 0000000..212f6ca --- /dev/null +++ b/src/core/global.js @@ -0,0 +1,44 @@ +import config from './config' +import { h } from 'vue' + +// 统一导入el-icon图标 +import * as ElIconModules from '@element-plus/icons-vue' +import svgIcon from '@/components/svgIcon/svgIcon.vue' +// 导入转换图标名称的函数 + +const createIconComponent = (name) => ({ + name: 'SvgIcon', + render() { + return h(svgIcon, { + name: name, + }) + }, +}) + +const registerIcons = async (app) => { + const iconModules = import.meta.glob('@/assets/icons/**/*.svg') + for (const path in iconModules) { + const iconName = path.split('/').pop().replace(/\.svg$/, '') + // 如果iconName带空格则不加入到图标库中并且提示名称不合法 + if (iconName.indexOf(' ') !== -1) { + console.error(`icon ${iconName}.svg includes whitespace`) + continue + } + const iconComponent = createIconComponent(iconName) + config.logs.push({ + 'key': iconName, + 'label': iconName, + }) + app.component(iconName, iconComponent) + } +} + +export const register = (app) => { + // 统一注册el-icon图标 + for (const iconName in ElIconModules) { + app.component(iconName, ElIconModules[iconName]) + } + app.component('SvgIcon', svgIcon) + registerIcons(app) + app.config.globalProperties.$GIN_VUE_ADMIN = config +} diff --git a/src/directive/auth.js b/src/directive/auth.js new file mode 100644 index 0000000..50594d4 --- /dev/null +++ b/src/directive/auth.js @@ -0,0 +1,41 @@ +// 权限按钮展示指令 +import { useUserStore } from '@/pinia/modules/user' +export default { + install: (app) => { + const userStore = useUserStore() + app.directive('auth', { + // 当被绑定的元素插入到 DOM 中时…… + mounted: function(el, binding) { + const userInfo = userStore.userInfo + let type = '' + switch (Object.prototype.toString.call(binding.value)) { + case '[object Array]': + type = 'Array' + break + case '[object String]': + type = 'String' + break + case '[object Number]': + type = 'Number' + break + default: + type = '' + break + } + if (type === '') { + el.parentNode.removeChild(el) + return + } + const waitUse = binding.value.toString().split(',') + let flag = waitUse.some(item => Number(item) === userInfo.authorityId) + if (binding.modifiers.not) { + flag = !flag + } + if (!flag) { + el.parentNode.removeChild(el) + } + } + }) + } +} + diff --git a/src/hooks/use-windows-resize.js b/src/hooks/use-windows-resize.js new file mode 100644 index 0000000..4ddf3f0 --- /dev/null +++ b/src/hooks/use-windows-resize.js @@ -0,0 +1,23 @@ +// 监听 window 的 resize 事件,返回当前窗口的宽高 +import { shallowRef } from 'vue' +import { tryOnMounted, useEventListener } from '@vueuse/core' + +const width = shallowRef(0) +const height = shallowRef(0) + +export const useWindowResize = (cb) => { + const onResize = () => { + width.value = window.innerWidth + height.value = window.innerHeight + if (cb && typeof cb === 'function') { + cb(width.value, height.value) + } + } + + tryOnMounted(onResize) + useEventListener('resize', onResize, { passive: true }) + return { + width, + height, + } +} diff --git a/src/pinia/index.js b/src/pinia/index.js new file mode 100644 index 0000000..a6063cf --- /dev/null +++ b/src/pinia/index.js @@ -0,0 +1,7 @@ +import { createPinia } from 'pinia' + +const store = createPinia() + +export { + store +} diff --git a/src/pinia/modules/dictionary.js b/src/pinia/modules/dictionary.js new file mode 100644 index 0000000..cd88f8c --- /dev/null +++ b/src/pinia/modules/dictionary.js @@ -0,0 +1,40 @@ +import { findSysDictionary } from '@/api/sysDictionary' + +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useDictionaryStore = defineStore('dictionary', () => { + const dictionaryMap = ref({}) + + const setDictionaryMap = (dictionaryRes) => { + dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes } + } + + const getDictionary = async(type) => { + if (dictionaryMap.value[type] && dictionaryMap.value[type].length) { + return dictionaryMap.value[type] + } else { + const res = await findSysDictionary({ type }) + if (res.code === 0) { + const dictionaryRes = {} + const dict = [] + res.data.resysDictionary.sysDictionaryDetails && res.data.resysDictionary.sysDictionaryDetails.forEach(item => { + dict.push({ + label: item.label, + value: item.value, + extend: item.extend + }) + }) + dictionaryRes[res.data.resysDictionary.type] = dict + setDictionaryMap(dictionaryRes) + return dictionaryMap.value[type] + } + } + } + + return { + dictionaryMap, + setDictionaryMap, + getDictionary + } +}) diff --git a/src/pinia/modules/router.js b/src/pinia/modules/router.js new file mode 100644 index 0000000..36b9ec0 --- /dev/null +++ b/src/pinia/modules/router.js @@ -0,0 +1,103 @@ +import { asyncRouterHandle } from '@/utils/asyncRouter' +import { emitter } from '@/utils/bus.js' +import { asyncMenu } from '@/api/menu' +import { defineStore } from 'pinia' +import { ref } from 'vue' + +const notLayoutRouterArr = [] +const keepAliveRoutersArr = [] +const nameMap = {} + +const formatRouter = (routes, routeMap, parent) => { + routes && routes.forEach(item => { + item.parent = parent + item.meta.btns = item.btns + item.meta.hidden = item.hidden + if (item.meta.defaultMenu === true) { + if (!parent) { + item = { ...item, path: `/${item.path}` } + notLayoutRouterArr.push(item) + } + } + routeMap[item.name] = item + if (item.children && item.children.length > 0) { + formatRouter(item.children, routeMap, item) + } + }) +} + +const KeepAliveFilter = (routes) => { + routes && routes.forEach(item => { + // 子菜单中有 keep-alive 的,父菜单也必须 keep-alive,否则无效。这里将子菜单中有 keep-alive 的父菜单也加入。 + if ((item.children && item.children.some(ch => ch.meta.keepAlive) || item.meta.keepAlive)) { + item.component && item.component().then(val => { + keepAliveRoutersArr.push(val.default.name) + nameMap[item.name] = val.default.name + }) + } + if (item.children && item.children.length > 0) { + KeepAliveFilter(item.children) + } + }) +} + +export const useRouterStore = defineStore('router', () => { + const keepAliveRouters = ref([]) + const asyncRouterFlag = ref(0) + const setKeepAliveRouters = (history) => { + const keepArrTemp = [] + history.forEach(item => { + if (nameMap[item.name]) { + keepArrTemp.push(nameMap[item.name]) + } + }) + keepAliveRouters.value = Array.from(new Set(keepArrTemp)) + } + emitter.on('setKeepAlive', setKeepAliveRouters) + + const asyncRouters = ref([]) + const routeMap = ({}) + // 从后台获取动态路由 + const SetAsyncRouter = async() => { + asyncRouterFlag.value++ + const baseRouter = [{ + path: '/layout', + name: 'layout', + component: 'view/layout/index.vue', + meta: { + title: '底层layout' + }, + children: [] + }] + const asyncRouterRes = await asyncMenu() + const asyncRouter = asyncRouterRes.data.menus + asyncRouter && asyncRouter.push({ + path: 'reload', + name: 'Reload', + hidden: true, + meta: { + title: '', + closeTab: true, + }, + component: 'view/error/reload.vue' + }) + formatRouter(asyncRouter, routeMap) + baseRouter[0].children = asyncRouter + if (notLayoutRouterArr.length !== 0) { + baseRouter.push(...notLayoutRouterArr) + } + asyncRouterHandle(baseRouter) + KeepAliveFilter(asyncRouter) + asyncRouters.value = baseRouter + return true + } + + return { + asyncRouters, + keepAliveRouters, + asyncRouterFlag, + SetAsyncRouter, + routeMap + } +}) + diff --git a/src/pinia/modules/user.js b/src/pinia/modules/user.js new file mode 100644 index 0000000..7ec2b58 --- /dev/null +++ b/src/pinia/modules/user.js @@ -0,0 +1,162 @@ +import { login, getUserInfo, setSelfInfo } from '@/api/user' +import { jsonInBlacklist } from '@/api/jwt' +import router from '@/router/index' +import { ElLoading, ElMessage } from 'element-plus' +import { defineStore } from 'pinia' +import { ref, computed, watch } from 'vue' +import { useRouterStore } from './router' +import cookie from 'js-cookie' + +export const useUserStore = defineStore('user', () => { + const loadingInstance = ref(null) + + const userInfo = ref({ + uuid: '', + nickName: '', + headerImg: '', + authority: {}, + sideMode: 'dark', + activeColor: 'var(--el-color-primary)', + baseColor: '#fff' + }) + const token = ref(window.localStorage.getItem('token') || cookie.get('x-token') || '') + const setUserInfo = (val) => { + userInfo.value = val + } + + const setToken = (val) => { + token.value = val + } + + const NeedInit = () => { + token.value = '' + window.localStorage.removeItem('token') + router.push({ name: 'Init', replace: true }) + } + + const ResetUserInfo = (value = {}) => { + userInfo.value = { + ...userInfo.value, + ...value + } + } + /* 获取用户信息*/ + const GetUserInfo = async() => { + const res = await getUserInfo() + if (res.code === 0) { + setUserInfo(res.data.userInfo) + } + return res + } + /* 登录*/ + const LoginIn = async(loginInfo) => { + loadingInstance.value = ElLoading.service({ + fullscreen: true, + text: '登录中,请稍候...', + }) + try { + const res = await login(loginInfo) + if (res.code === 0) { + setUserInfo(res.data.user) + setToken(res.data.token) + const routerStore = useRouterStore() + await routerStore.SetAsyncRouter() + const asyncRouters = routerStore.asyncRouters + asyncRouters.forEach(asyncRouter => { + router.addRoute(asyncRouter) + }) + + if (!router.hasRoute(userInfo.value.authority.defaultRouter)) { + ElMessage.error('请联系管理员进行授权') + } else { + await router.replace({ name: userInfo.value.authority.defaultRouter }) + } + + loadingInstance.value.close() + + const isWin = ref(/windows/i.test(navigator.userAgent)) + if (isWin.value) { + window.localStorage.setItem('osType', 'WIN') + } else { + window.localStorage.setItem('osType', 'MAC') + } + return true + } + } catch (e) { + loadingInstance.value.close() + } + loadingInstance.value.close() + } + /* 登出*/ + const LoginOut = async() => { + const res = await jsonInBlacklist() + if (res.code === 0) { + await ClearStorage() + router.push({ name: 'Login', replace: true }) + window.location.reload() + } + } + /* 清理数据 */ + const ClearStorage = async() => { + token.value = '' + sessionStorage.clear() + window.localStorage.removeItem('token') + cookie.remove('x-token') + } + /* 设置侧边栏模式*/ + const changeSideMode = async(data) => { + const res = await setSelfInfo({ sideMode: data }) + if (res.code === 0) { + userInfo.value.sideMode = data + ElMessage({ + type: 'success', + message: '设置成功' + }) + } + } + + const mode = computed(() => userInfo.value.sideMode) + const sideMode = computed(() => { + if (userInfo.value.sideMode === 'dark') { + return '#191a23' + } else if (userInfo.value.sideMode === 'light') { + return '#fff' + } else { + return userInfo.value.sideMode + } + }) + const baseColor = computed(() => { + if (userInfo.value.sideMode === 'dark') { + return '#fff' + } else if (userInfo.value.sideMode === 'light') { + return '#191a23' + } else { + return userInfo.value.baseColor + } + }) + const activeColor = computed(() => { + return 'var(--el-color-primary)' + }) + + watch(() => token.value, () => { + window.localStorage.setItem('token', token.value) + }) + + return { + userInfo, + token, + NeedInit, + ResetUserInfo, + GetUserInfo, + LoginIn, + LoginOut, + changeSideMode, + mode, + sideMode, + setToken, + baseColor, + activeColor, + loadingInstance, + ClearStorage + } +}) diff --git a/src/plugin/email/api/email.js b/src/plugin/email/api/email.js new file mode 100644 index 0000000..590c862 --- /dev/null +++ b/src/plugin/email/api/email.js @@ -0,0 +1,30 @@ +import service from '@/utils/request' +// @Tags System +// @Summary 发送测试邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/emailTest [post] +export const emailTest = (data) => { + return service({ + url: '/email/emailTest', + method: 'post', + data + }) +} + +// @Tags System +// @Summary 发送邮件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param data body email_response.Email true "发送邮件必须的参数" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}" +// @Router /email/sendEmail [post] +export const sendEmail = (data) => { + return service({ + url: '/email/sendEmail', + method: 'post', + data + }) +} + diff --git a/src/plugin/email/view/index.vue b/src/plugin/email/view/index.vue new file mode 100644 index 0000000..11d57e4 --- /dev/null +++ b/src/plugin/email/view/index.vue @@ -0,0 +1,63 @@ + + + + diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..52d3763 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,31 @@ +import { createRouter, createWebHashHistory } from 'vue-router' + +const routes = [{ + path: '/', + redirect: '/login' +}, +{ + path: '/init', + name: 'Init', + component: () => import('@/view/init/index.vue') +}, +{ + path: '/login', + name: 'Login', + component: () => import('@/view/login/index.vue') +}, +{ + path: '/:catchAll(.*)', + meta: { + closeTab: true, + }, + component: () => import('@/view/error/index.vue') +} +] + +const router = createRouter({ + history: createWebHashHistory(), + routes, +}) + +export default router diff --git a/src/style/element/index.scss b/src/style/element/index.scss new file mode 100644 index 0000000..40ef0df --- /dev/null +++ b/src/style/element/index.scss @@ -0,0 +1,24 @@ +@forward 'element-plus/theme-chalk/src/common/var.scss' with ( + $colors: ( + 'white': #ffffff, + 'black': #000000, + 'primary': ( + 'base': #4d70ff, + ), + 'success': ( + 'base': #67c23a, + ), + 'warning': ( + 'base': #e6a23c, + ), + 'danger': ( + 'base': #f56c6c, + ), + 'error': ( + 'base': #f56c6c, + ), + 'info': ( + 'base': #909399, + ), + ) +); diff --git a/src/style/element_visiable.scss b/src/style/element_visiable.scss new file mode 100644 index 0000000..0210664 --- /dev/null +++ b/src/style/element_visiable.scss @@ -0,0 +1,42 @@ +@import '@/style/main.scss'; + +.el-button { + font-weight: 400; + border-radius: 2px; +} + +::-webkit-scrollbar { + @apply hidden; +} + + +.gva-search-box { + @apply p-6 pb-0.5 bg-white rounded mb-3; +} + +.gva-form-box { + @apply p-6 bg-white rounded; +} + +.gva-pagination { + @apply flex justify-end; + .el-pagination__editor { + .el-input__inner { + @apply h-8; + } + } + + .is-active { + @apply rounded text-white; + background: var(--el-color-primary); + color: #ffffff !important; + } +} + + +.el-drawer__header{ + margin-bottom: 0 !important; + padding-top: 16px !important; + padding-bottom: 16px !important; + @apply border-0 border-b border-solid border-gray-200; +} diff --git a/src/style/iconfont.css b/src/style/iconfont.css new file mode 100644 index 0000000..bc091a0 --- /dev/null +++ b/src/style/iconfont.css @@ -0,0 +1,47 @@ +@font-face { + font-family: 'gvaIcon'; + src: url('data:font/ttf;charset=utf-8;base64,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AAAAAAALABMBkAADAAEECQAAACYAAAADAAEECQABABAAPAADAAEECQACAA4AVwADAAEECQADABAAbwADAAEECQAEABAAigADAAEECQAFABYApQADAAEECQAGABAAyQADAAEECQAKAFYA5AADAAEECQALACYBaABDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AABDcmVhdGVkIGJ5IGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAAAAAIAAAAAAAAACgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAgECAQMBBAEFAQYBBwEIAQkRYXJyb3ctZG91YmxlLWxlZnQSYXJyb3ctZG91YmxlLXJpZ2h0EGN1c3RvbWVyLXNlcnZpY2URZnVsbHNjcmVlbi1leHBhbmQRZnVsbHNjcmVlbi1zaHJpbmsGcHJvbXB0B3JlZnJlc2gGc2VhcmNoAAAAAf//AAIAAQAAAAwAAAAWAAAAAgABAAMACgABAAQAAAACAAAAAAAAAAEAAAAA1aQnCAAAAADeWFEjAAAAAN5YUSM=') format('truetype'); + font-weight: 600; + font-style: normal; + font-display: swap; +} +.gvaIcon { + font-family: "gvaIcon" !important; + font-size: 16px; + font-style: normal; + font-weight: 800; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + +.gvaIcon-arrow-double-left:before { + content: "\e665"; + } + +.gvaIcon-arrow-double-right:before { + content: "\e666"; +} + +.gvaIcon-fullscreen-shrink:before { + content: "\e676"; +} +.gvaIcon-customer-service:before { + content: "\e66a"; + } + +.gvaIcon-fullscreen-expand:before { + content: "\e675"; +} + +.gvaIcon-prompt:before { + content: "\e67b"; +} + +.gvaIcon-refresh:before { + content: "\e67c"; +} + +.gvaIcon-search:before { + content: "\e67d"; +} + diff --git a/src/style/main.scss b/src/style/main.scss new file mode 100644 index 0000000..d4865cf --- /dev/null +++ b/src/style/main.scss @@ -0,0 +1,701 @@ +/* Document + ========================================================================== */ + + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +@import '@/style/iconfont.css'; +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + + +/* Sections + ========================================================================== */ + + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + + +/* Grouping content + ========================================================================== */ + + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; + /* 1 */ + height: 0; + /* 1 */ + overflow: visible; + /* 2 */ +} + + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + + +/* Text-level semantics + ========================================================================== */ + + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; + /* 1 */ + text-decoration: underline; + /* 2 */ + text-decoration: underline dotted; + /* 2 */ +} + + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + + +/* Embedded content + ========================================================================== */ + + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + + +/* Forms + ========================================================================== */ + + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ +} + + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { + /* 1 */ + overflow: visible; +} + + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { + /* 1 */ + text-transform: none; +} + + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; + /* 1 */ + color: inherit; + /* 2 */ + display: table; + /* 1 */ + max-width: 100%; + /* 1 */ + padding: 0; + /* 3 */ + white-space: normal; + /* 1 */ +} + + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ +} + + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + + +/* Interactive + ========================================================================== */ + + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + + +/* Misc + ========================================================================== */ + + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} + +HTML, +body, +div, +ul, +ol, +dl, +li, +dt, +dd, +p, +blockquote, +pre, +form, +fieldset, +table, +th, +td { + border: none; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; + font-size: 14px; + margin: 0px; + padding: 0px; +} + +html, +body { + height: 100%; + width: 100%; +} + +address, +caption, +cite, +code, +th, +var { + font-style: normal; + font-weight: normal; +} + +a { + text-decoration: none; +} + +input::-ms-clear { + display: none; +} + +input::-ms-reveal { + display: none; +} + +input { + -webkit-appearance: none; + margin: 0; + outline: none; + padding: 0; +} + +input::-webkit-input-placeholder { + color: #ccc; +} + +input::-ms-input-placeholder { + color: #ccc; +} + +input::-moz-placeholder { + color: #ccc; +} + +input[type=submit], +input[type=button] { + cursor: pointer; +} + +button[disabled], +input[disabled] { + cursor: default; +} + +img { + border: none; +} + +ul, +ol, +li { + list-style-type: none; +} + +// 导航 +#app { + .el-container { + @apply relative h-full w-full; + } + .el-container.mobile.openside { + @apply fixed top-0; + } + .gva-aside { + @apply fixed top-0 left-0 z-[1001] overflow-hidden; + .el-menu { + @apply border-r-0; + } + } + .aside { + .el-menu--collapse { + >.el-menu-item { + display: flex; + justify-content: center; + } + } + .el-sub-menu { + .el-menu { + .is-active { + // 关闭三级菜单二级菜单样式 + ul { + border: none; + } + } + // 关闭三级菜单二级菜单样式 + .is-active.is-opened { + ul { + border: none; + } + } + } + } + } + .hideside { + .aside { + @apply w-[54px] + } + } + + .mobile { + .gva-aside { + @apply w-[54px]; + } + } + + .hideside { + .main-cont.el-main { + @apply ml-[54px]; + } + } + .mobile { + .main-cont.el-main { + @apply ml-0; + } + } +} + +// layout + +.admin-box { + @apply min-h-[calc(100vh-200px)] px-3 py-4 mt-28 mb-4 mx-1; + .el-table { + th { + @apply px-0 py-2; + .cell { + @apply leading-[40px] text-gray-700; + } + } + td { + @apply px-0 py-2; + .cell { + @apply leading-[40px] text-gray-600; + } + } + .is-leaf { + @apply border-b border-t-0 border-l-0 border-solid border-gray-50; + border-right:var(--el-table-border); + background: #F7FBFF !important; + } + } +} + +// table +.el-pagination { + @apply mt-8; + .btn-prev, + .btn-next { + @apply border border-solid border-gray-300 rounded; + } + .el-pager { + li { + @apply border border-solid border-gray-300 rounded text-gray-600 text-sm mx-1; + } + } +} + + +.el-container.layout-cont { + .header-cont { + @apply px-4 h-16 bg-white; + } + + + .main-cont { + @apply h-screen overflow-visible; + &.el-main { + @apply min-h-full ml-[220px] bg-main p-0 overflow-auto; + } + + .breadcrumb { + @apply h-16 flex items-center p-0 ml-12 text-lg; + .el-breadcrumb__item { + .el-breadcrumb__inner { + @apply text-gray-600; + } + } + .el-breadcrumb__item:nth-last-child(1) { + .el-breadcrumb__inner { + @apply text-gray-600; + } + } + } + + .router-history { + @apply bg-white p-0 border-t border-l-0 border-r-0 border-b-0 border-solid border-gray-100; + .el-tabs__header { + @apply m-0; + .el-tabs__item{ + @apply border-solid border-r border-t-0 border-gray-100 border-b-0 border-l-0; + } + .el-tabs__item.is-active { + @apply bg-blue-500 bg-opacity-5; + } + .el-tabs__nav { + @apply border-0; + } + } + } + + .aside { + @apply overflow-auto; + } + .el-menu-vertical { + @apply h-[calc(100vh-60px)]; + &:not(.el-menu--collapse) { + @apply w-[220px]; + } + } + .el-menu--collapse { + @apply w-[54px]; + li { + .el-tooltip, + .el-sub-menu__title { + @apply px-4; + } + } + } + } +} + +.el-dropdown { + @apply overflow-hidden +} + +.gva-table-box { + @apply p-6 bg-white rounded; +} + +.gva-btn-list { + @apply mb-3 flex gap-3 items-center; +} + + +#nprogress .bar { + background: #29d !important; +} +.gva-customer-icon{ + @apply w-4 h-4; +} + +.el-form--inline { + .el-form-item { + & > .el-input, .el-cascader, .el-select, .el-date-editor, .el-autocomplete { + @apply w-52; + } + } +} diff --git a/src/utils/asyncRouter.js b/src/utils/asyncRouter.js new file mode 100644 index 0000000..dfa62bb --- /dev/null +++ b/src/utils/asyncRouter.js @@ -0,0 +1,31 @@ +const viewModules = import.meta.glob('../view/**/*.vue') +const pluginModules = import.meta.glob('../plugin/**/*.vue') + +export const asyncRouterHandle = (asyncRouter) => { + asyncRouter.forEach(item => { + if (item.component && typeof item.component === 'string') { + if (item.component.split('/')[0] === 'view') { + item.component = dynamicImport(viewModules, item.component) + } else if (item.component.split('/')[0] === 'plugin') { + item.component = dynamicImport(pluginModules, item.component) + } + } + if (item.children) { + asyncRouterHandle(item.children) + } + }) +} + +function dynamicImport( + dynamicViewsModules, + component +) { + const keys = Object.keys(dynamicViewsModules) + const matchKeys = keys.filter((key) => { + const k = key.replace('../', '') + return k === component + }) + const matchKey = matchKeys[0] + + return dynamicViewsModules[matchKey] +} diff --git a/src/utils/btnAuth.js b/src/utils/btnAuth.js new file mode 100644 index 0000000..f94fa9b --- /dev/null +++ b/src/utils/btnAuth.js @@ -0,0 +1,6 @@ +import { useRoute } from 'vue-router' +import { reactive } from 'vue' +export const useBtnAuth = () => { + const route = useRoute() + return route.meta.btns || reactive({}) +} diff --git a/src/utils/format.js b/src/utils/format.js index b052ac9..851b77c 100644 --- a/src/utils/format.js +++ b/src/utils/format.js @@ -24,12 +24,6 @@ export const formatOnlyDate = (time) => { return '' } } -export const formatPriceType = (priceType) => { - switch (priceType) { - case 1: return '出厂价' - default: '未定义' - } -} export const filterDict = (value, options) => { const rowLabel = options && options.filter(item => item.value === value) diff --git a/src/view/dashboard/index.vue b/src/view/dashboard/index.vue index a92d8f9..ed6b422 100644 --- a/src/view/dashboard/index.vue +++ b/src/view/dashboard/index.vue @@ -6,14 +6,14 @@
早安,请开始一天的工作吧
{{ weatherInfo }}
- +
{ ip() return weatherInfo } -export const ip = async () => { +export const ip = async() => { // key换成你自己的 https://console.amap.com/dev/index if (amapKey === '') { return false } const res = await axios.get('https://restapi.amap.com/v3/ip?key=' + amapKey) - var cityCode - if (res.data.status !== '0') { - cityCode = res.data.adcode - } else { - cityCode = '100000' + if (res.data.adcode) { + getWeather(res.data.adcode) } - getWeather(cityCode) } -const getWeather = async (code) => { - const url = 'https://restapi.amap.com/v3/weather/weatherInfo?key=' + amapKey + '&extensions=base&city=' + code - const response = await axios.get(url) +const getWeather = async(code) => { + const response = await axios.get('https://restapi.amap.com/v3/weather/weatherInfo?key=' + amapKey + '&extensions=base&city=' + code) if (response.data.status === '1') { const s = response.data.lives[0] weatherInfo.value = s.city + ' 天气:' + s.weather + ' 温度:' + s.temperature + '摄氏度 风向:' + s.winddirection + ' 风力:' + s.windpower + '级 空气湿度:' + s.humidity } } +