├── public ├── robots.txt ├── favicon.ico ├── images │ ├── logo.png │ ├── loginLeft.png │ └── login-background.jpg └── js │ └── config.js ├── .browserslistrc ├── src ├── components │ ├── ParentView │ │ └── index.vue │ ├── IconSelect │ │ ├── requireIcons.js │ │ └── index.vue │ ├── PreviewImg │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── ThemePicker │ │ └── index.vue │ ├── TableColumn │ │ ├── index.vue │ │ └── columnArr.json │ ├── Screenfull │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ ├── SizeSelect │ │ └── index.vue │ ├── ImagePopup │ │ └── index.vue │ ├── TooltipOver │ │ └── index.vue │ ├── RightToolbar │ │ └── index.vue │ └── Pagination │ │ └── index.vue ├── assets │ ├── logo │ │ └── logo.png │ ├── images │ │ ├── avatar.png │ │ ├── empty.png │ │ ├── profile.jpg │ │ ├── to-right.png │ │ ├── successIcon.png │ │ ├── light.svg │ │ ├── orange.svg │ │ ├── red.svg │ │ ├── yellow.svg │ │ ├── gray.svg │ │ ├── blue.svg │ │ ├── dark.svg │ │ ├── green.svg │ │ └── purple.svg │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ ├── icons │ │ ├── index.js │ │ ├── svg │ │ │ ├── chart.svg │ │ │ ├── size.svg │ │ │ ├── link.svg │ │ │ ├── guide.svg │ │ │ ├── money.svg │ │ │ ├── email.svg │ │ │ ├── drag.svg │ │ │ ├── documentation.svg │ │ │ ├── fullscreen.svg │ │ │ ├── lock.svg │ │ │ ├── user.svg │ │ │ ├── excel.svg │ │ │ ├── example.svg │ │ │ ├── star.svg │ │ │ ├── slider.svg │ │ │ ├── table.svg │ │ │ ├── search.svg │ │ │ ├── education.svg │ │ │ ├── tab.svg │ │ │ ├── hamburger.svg │ │ │ ├── message.svg │ │ │ ├── switch.svg │ │ │ ├── theme.svg │ │ │ ├── druid.svg │ │ │ ├── code.svg │ │ │ ├── peoples.svg │ │ │ ├── input.svg │ │ │ ├── server.svg │ │ │ ├── textarea.svg │ │ │ ├── time.svg │ │ │ ├── edit.svg │ │ │ ├── nested.svg │ │ │ ├── row.svg │ │ │ ├── monitor.svg │ │ │ ├── tree-table.svg │ │ │ ├── eye.svg │ │ │ ├── build.svg │ │ │ ├── clipboard.svg │ │ │ ├── list.svg │ │ │ ├── download.svg │ │ │ ├── icon.svg │ │ │ ├── international.svg │ │ │ ├── question.svg │ │ │ ├── wechat.svg │ │ │ ├── skill.svg │ │ │ ├── people.svg │ │ │ ├── post.svg │ │ │ ├── language.svg │ │ │ ├── checkbox.svg │ │ │ ├── eye-open.svg │ │ │ ├── validCode.svg │ │ │ ├── radio.svg │ │ │ ├── select.svg │ │ │ ├── upload.svg │ │ │ ├── 404.svg │ │ │ ├── zip.svg │ │ │ ├── phone.svg │ │ │ ├── log.svg │ │ │ ├── bug.svg │ │ │ ├── github.svg │ │ │ ├── pdf.svg │ │ │ ├── logininfor.svg │ │ │ ├── rate.svg │ │ │ ├── job.svg │ │ │ ├── exit-fullscreen.svg │ │ │ ├── tree.svg │ │ │ ├── swagger.svg │ │ │ ├── password.svg │ │ │ ├── date-range.svg │ │ │ ├── shopping.svg │ │ │ ├── cascader.svg │ │ │ ├── dashboard.svg │ │ │ ├── button.svg │ │ │ ├── component.svg │ │ │ ├── form.svg │ │ │ ├── tool.svg │ │ │ ├── redis.svg │ │ │ ├── dict.svg │ │ │ └── time-range.svg │ │ └── svgo.yml │ └── styles │ │ ├── element-variables.scss │ │ ├── transition.scss │ │ ├── flex.scss │ │ ├── mixin.scss │ │ └── btn.scss ├── utils │ ├── errorCode.ts │ ├── auth.ts │ ├── export2Excel.ts │ ├── recentlyUsed.ts │ ├── message.ts │ ├── setting.ts │ ├── mixin.ts │ ├── jsencrypt.ts │ ├── zipdownload.ts │ ├── permission.ts │ ├── print.ts │ ├── scroll-to.ts │ ├── validate.ts │ └── sortable.ts ├── types │ ├── common.ts │ ├── app.ts │ ├── permission.ts │ ├── setting.ts │ ├── tagsView.ts │ └── user.ts ├── api │ ├── store │ │ ├── common.ts │ │ ├── menu.ts │ │ └── login.ts │ ├── initial │ │ ├── hospital.ts │ │ └── user.ts │ └── system │ │ ├── org.ts │ │ ├── menu.ts │ │ └── role.ts ├── views │ ├── base │ │ ├── index.vue │ │ ├── redirect.vue │ │ └── error │ │ │ └── 401.vue │ └── systemManagement │ │ └── organizationManagement │ │ └── index.vue ├── store │ ├── index.ts │ ├── common.ts │ ├── app.ts │ └── settings.ts ├── config │ └── baseUrl.ts ├── App.vue ├── settings.ts ├── shims-vue.d.ts ├── layout │ └── components │ │ ├── Sidebar │ │ ├── Item.vue │ │ ├── Link.vue │ │ ├── Logo.vue │ │ └── index.vue │ │ ├── Settings │ │ └── checkBoxList.js │ │ ├── AppMain │ │ └── index.vue │ │ └── TagsView │ │ └── ScrollPane.vue ├── main.ts ├── permission.ts └── router │ └── index.ts ├── babel.config.js ├── .gitignore ├── tslint.json ├── tsconfig.json └── package.json /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/components/ParentView/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/public/images/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/logo/logo.png -------------------------------------------------------------------------------- /public/images/loginLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/public/images/loginLeft.png -------------------------------------------------------------------------------- /src/assets/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/images/avatar.png -------------------------------------------------------------------------------- /src/assets/images/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/images/empty.png -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/images/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/images/profile.jpg -------------------------------------------------------------------------------- /src/assets/images/to-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/images/to-right.png -------------------------------------------------------------------------------- /src/assets/images/successIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/images/successIcon.png -------------------------------------------------------------------------------- /public/images/login-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/public/images/login-background.jpg -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/z-yong/vue3-ts_system/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/utils/errorCode.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 401: '认证失败,无法访问系统资源', 3 | 403: '当前操作没有权限', 4 | 404: '访问资源不存在', 5 | default: '系统未知错误,请反馈给管理员' 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/icons/index.js: -------------------------------------------------------------------------------- 1 | const req = require.context('./svg', false, /\.svg$/) 2 | const requireAll = (requireContext) => requireContext.keys().map(requireContext) 3 | requireAll(req) 4 | -------------------------------------------------------------------------------- /src/types/common.ts: -------------------------------------------------------------------------------- 1 | export interface SupOptType { 2 | sSupId: string; 3 | sSupName: string; 4 | } 5 | 6 | export interface CommonType extends Object { 7 | SupOpt: SupOptType[] 8 | } 9 | -------------------------------------------------------------------------------- /src/types/app.ts: -------------------------------------------------------------------------------- 1 | export interface AppType extends Object { 2 | sidebar: { 3 | opened: boolean; 4 | withoutAnimation: boolean; 5 | }; 6 | device: string; 7 | size: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/store/common.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取供应商列表 4 | export function getSupOpt() { 5 | return request({ 6 | url: '/basic/supplier', 7 | method: 'get' 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /src/api/initial/hospital.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取医院列表 4 | export function getOwnerList() { 5 | return request({ 6 | url: '/system/user/owner', 7 | method: 'get' 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/types/permission.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router' 2 | 3 | export interface PermissionType extends Object { 4 | routes: RouteRecordRaw[]; 5 | addRoutes: RouteRecordRaw[]; 6 | sidebarRouters: RouteRecordRaw[]; 7 | } 8 | -------------------------------------------------------------------------------- /src/api/system/org.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 查询组织架构--不分页 4 | export const getOrgAll = (sUserId) => { 5 | return request({ 6 | url: `/system/user/orgAll?sUserId=${sUserId}`, 7 | method: 'get' 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /src/api/store/menu.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取路由 4 | export const getRouters = (params: { nType: number }) => { 5 | return request({ 6 | url: '/sysmenu/getRoleMenusByUser', 7 | method: 'get', 8 | params 9 | }) 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/types/setting.ts: -------------------------------------------------------------------------------- 1 | export interface SettingType extends Object { 2 | theme: string; 3 | sideTheme: string; 4 | showSettings: boolean; 5 | tagsView: boolean; 6 | fixedHeader: boolean; 7 | sidebarLogo: boolean; 8 | menuDrag: boolean; 9 | menuWidth: number; 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/base/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/IconSelect/requireIcons.js: -------------------------------------------------------------------------------- 1 | 2 | const req = require.context('../../assets/icons/svg', false, /\.svg$/) 3 | const requireAll = requireContext => requireContext.keys() 4 | 5 | const re = /\.\/(.*)\.svg/ 6 | 7 | const icons = requireAll(req).map(i => { 8 | return i.match(re)[1] 9 | }) 10 | 11 | export default icons 12 | -------------------------------------------------------------------------------- /src/assets/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /src/types/tagsView.ts: -------------------------------------------------------------------------------- 1 | import { RouteLocationNormalizedLoaded } from 'vue-router' 2 | 3 | export type TagsType = RouteLocationNormalizedLoaded & { 4 | title?: string; 5 | fullPath?: string; 6 | to?: TagsType 7 | } 8 | 9 | export interface TagsViewType extends Object { 10 | visitedViews: TagsType[]; 11 | cachedViews: string[]; 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /public/js/config.js: -------------------------------------------------------------------------------- 1 | window.globalVar = { 2 | baseUrlDev: '', // 自己更改自己需要部署的地址 3 | indexPath: '/vue3project/', // 服务器地址根路径 4 | isHighInterface: '36.7.136.46:8282', // 高值计费计费页面地址 5 | logoBgUrl: './images/login-background.jpg', // 登录页背景图 6 | cmsName: '****管理系统', // 登录页cms名称 7 | logoUrl: './images/logo.png', // logo图片地址 8 | loginLeftImgUrl: './images/loginLeft.png' // 登录页左边图片地址 9 | } -------------------------------------------------------------------------------- /src/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Admin-Token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) || '' 7 | } 8 | 9 | export function setToken(token: string) { 10 | return Cookies.set(TokenKey, token, { 11 | expires: 7 12 | }) 13 | } 14 | 15 | export function removeToken() { 16 | return Cookies.remove(TokenKey) 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { reactive, readonly } from 'vue' 2 | export abstract class Store { 3 | protected state: T 4 | 5 | constructor() { 6 | const data = this.data() 7 | this.setup(data) 8 | this.state = reactive(data) as T 9 | } 10 | 11 | public getState(): T { 12 | return readonly(this.state) as T 13 | } 14 | 15 | protected abstract data(): T 16 | 17 | protected setup(data: T): void { } 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/export2Excel.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line 2 | export default function (header, data, filename) { 3 | return new Promise((res, rej) => { 4 | import('@/assets/ts/Export2Excel').then((excel) => { 5 | excel.export_json_to_excel({ 6 | header, // 表头 必填 7 | data, // 具体数据 必填 8 | filename, // excel-list 非必填 9 | autoWidth: true, // 自动宽度 非必填 10 | bookType: 'xlsx' // 非必填 11 | }) 12 | res('ok') 13 | }).catch(() => rej()) 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/recentlyUsed.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const RecentlyUsedKey = 'recentlyUsed' 4 | 5 | export function getRecentlyUsed() { 6 | if (Cookies.get(RecentlyUsedKey)) { 7 | return JSON.parse(Cookies.get(RecentlyUsedKey)) 8 | } 9 | return [] 10 | } 11 | 12 | export function setRecentlyUsed(userList) { 13 | return Cookies.set(RecentlyUsedKey, userList, { 14 | expires: 7 15 | }) 16 | } 17 | 18 | export function removeRecentlyUsed() { 19 | return Cookies.remove(RecentlyUsedKey) 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/slider.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/config/baseUrl.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Autor: yong.zhu 3 | * @Date: 2021-11-10 16:45:42 4 | * @LastEditors: yong.zhu 5 | * @LastEditTime: 2021-11-16 08:51:25 6 | * @Description: 接口地址 7 | * @Version: 1.0 8 | */ 9 | const urlS1 = 'http://192.168.1.96:8181/jxhscp' // 开发库 10 | 11 | let baseUrl = '' 12 | if (process.env.NODE_ENV === 'development') { 13 | // 开发环境 14 | baseUrl = urlS1 15 | } else if (process.env.NODE_ENV === 'production') { 16 | // 生产环境 17 | baseUrl = window.globalVar.baseUrlDev // 打包: 开发库 urlS1 | 测试库 urlS4 | 标准上线库 urlS3 18 | } 19 | export default baseUrl 20 | -------------------------------------------------------------------------------- /src/types/user.ts: -------------------------------------------------------------------------------- 1 | export interface OrgType { 2 | sort?: number; 3 | sOrgId: string; 4 | sOrgName: string; 5 | } 6 | 7 | export interface UserType extends Object { 8 | token: string; 9 | avatar: string; 10 | sUserCode: string; 11 | sSelfCode: string; 12 | sUserId: string; 13 | sUserName: string; 14 | roles: any[]; 15 | permissions: any[]; 16 | currentOrg: OrgType; 17 | sOrgList: OrgType[]; 18 | sOwnerId: string; 19 | nTypeId: string; 20 | messageDataNum: number; 21 | } 22 | 23 | export interface LoginType { 24 | sUserCode: string; 25 | sPassword: string; 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/base/redirect.vue: -------------------------------------------------------------------------------- 1 | 9 | 25 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/hamburger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/switch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/druid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/code.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/message.ts: -------------------------------------------------------------------------------- 1 | // 重置message,防止重复点击重复弹出message弹框 2 | import { ElMessage, MessageOptions } from 'element-plus' 3 | 4 | let messageInstance: any = null 5 | const arr: string[] = ['success', 'warning', 'info', 'error', ''] 6 | 7 | arr.forEach((type: any) => { 8 | DoneMessage[type] = (options: MessageOptions) => { 9 | if (typeof options === 'string') { 10 | options = { 11 | message: options 12 | } 13 | } 14 | options.type = type; 15 | return DoneMessage(options); 16 | } 17 | }) 18 | 19 | export default function DoneMessage(options: MessageOptions) { 20 | if (messageInstance) { 21 | messageInstance.close(); 22 | } 23 | messageInstance = ElMessage(options); 24 | return messageInstance 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/icons/svg/input.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/server.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/textarea.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Autor: yong.zhu 3 | * @Date: 2021-11-10 16:45:41 4 | * @LastEditors: yong.zhu 5 | * @LastEditTime: 2021-11-16 08:52:20 6 | * @Description: 配置项 7 | * @Version: 1.0 8 | */ 9 | export default { 10 | title: '管理系统', 11 | 12 | /** 13 | * 侧边栏主题 默认深色主题ThemeDark 14 | */ 15 | sideTheme: 'ThemeDark', 16 | 17 | /** 18 | * 是否系统布局配置 19 | */ 20 | showSettings: false, 21 | 22 | /** 23 | * 是否显示 tagsView 24 | */ 25 | tagsView: true, 26 | 27 | /** 28 | * 是否固定头部 29 | */ 30 | fixedHeader: true, 31 | 32 | /** 33 | * 是否显示logo 34 | */ 35 | sidebarLogo: true, 36 | 37 | /** 38 | * 是否支持菜单栏横向拖拽 39 | */ 40 | menuDrag: false, 41 | 42 | /** 43 | */ 44 | errorLog: 'production', 45 | 46 | // 是否启用本地代理 47 | useProxy: false 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/icons/svg/time.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Autor: yong.zhu 3 | * @Date: 2021-11-10 16:45:41 4 | * @LastEditors: yong.zhu 5 | * @LastEditTime: 2021-11-16 08:47:48 6 | * @Description: 7 | * @Version: 1.0 8 | */ 9 | /* eslint-disable */ 10 | declare module '*.vue' { 11 | import type { DefineComponent } from 'vue' 12 | const component: DefineComponent<{}, {}, any> 13 | export default component 14 | } 15 | 16 | // declare module 'vue' 17 | declare module '*.scss' 18 | declare module '*.js' 19 | declare module 'quill' 20 | declare module 'js-cookie' 21 | interface GlobalType { 22 | baseUrlDev: string; 23 | indexPath: string; 24 | isHighInterface: string; 25 | logoBgUrl: string; 26 | cmsName: string; 27 | logoUrl: string; 28 | loginLeftImgUrl: string; 29 | } 30 | 31 | interface Window { 32 | globalVar: GlobalType; 33 | } 34 | -------------------------------------------------------------------------------- /src/views/systemManagement/organizationManagement/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | -------------------------------------------------------------------------------- /src/assets/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #1890ff; 8 | $--color-success: #13ce66; 9 | $--color-warning: #ffba00; 10 | $--color-danger: #ff4949; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border:1px solid#dfe6ec; 21 | 22 | 23 | // @import "~element-ui/packages/theme-chalk/src/index"; 24 | 25 | // the :export directive is the magic sauce for webpack 26 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 27 | :export { 28 | theme: $--color-primary; 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/icons/svg/row.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/setting.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const settingKey = 'SETTING' 4 | 5 | export function getSetting(key: string = ''): any { 6 | let setting: string = Cookies.get(settingKey) 7 | if (!key) { 8 | return setting 9 | } 10 | let value = null 11 | if (setting) { 12 | setting = JSON.parse(setting) 13 | value = setting[key] 14 | } 15 | return value 16 | } 17 | 18 | export function setSetting(key: string, value: string | number | boolean) { 19 | let setting: any = Cookies.get(settingKey) || '{}' 20 | if (setting) { 21 | setting = JSON.parse(setting) 22 | } else { 23 | setting = {} 24 | } 25 | setting[key] = value 26 | setting = JSON.stringify(setting) 27 | Cookies.set(settingKey, setting, { 28 | expires: 365 29 | }) 30 | } 31 | 32 | export function removeSetting() { 33 | return Cookies.remove(settingKey) 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/icons/svg/monitor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/PreviewImg/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 30 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "linterOptions": { 7 | "exclude": [ 8 | "node_modules/**" 9 | ] 10 | }, 11 | "rules": { 12 | "indent": [true, "spaces", 2], 13 | "interface-name": false, 14 | "no-consecutive-blank-lines": false, 15 | "object-literal-sort-keys": false, 16 | "ordered-imports": false, 17 | "quotemark": [true, "single"], 18 | "semicolon": [false, "always"], 19 | "trailing-comma": [true, { //对尾随逗号的校验 20 | "multiline": { 21 | "objects": "ignore", 22 | "arrays": "never", 23 | "functions": "never", 24 | "typeLiterals": "ignore" 25 | }, 26 | "esSpecCompliant": true //是否允许尾随逗号出现在剩余变量中 27 | }], 28 | "no-console": false, 29 | "no-empty": false, 30 | "ban-types": false, 31 | "skipLibCheck": true 32 | } 33 | } -------------------------------------------------------------------------------- /src/assets/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/build.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-enter-active, 16 | .fade-transform-leave-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter-from { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 9 | 41 | -------------------------------------------------------------------------------- /src/assets/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "noImplicitAny": false, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "sourceMap": true, 15 | "baseUrl": ".", 16 | "suppressImplicitAnyIndexErrors": true, 17 | "types": [ 18 | "webpack-env", 19 | "node" 20 | ], 21 | "paths": { 22 | "@/*": [ 23 | "src/*" 24 | ] 25 | }, 26 | "lib": [ 27 | "esnext", 28 | "dom", 29 | "dom.iterable", 30 | "scripthost" 31 | ] 32 | }, 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "src/**/*.vue", 37 | "tests/**/*.ts", 38 | "tests/**/*.tsx" 39 | ], 40 | "exclude": [ 41 | "node_modules" 42 | ] 43 | } -------------------------------------------------------------------------------- /src/assets/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/styles/flex.scss: -------------------------------------------------------------------------------- 1 | .df { 2 | display: flex !important; 3 | } 4 | .dif { 5 | display: inline-flex !important; 6 | } 7 | .aic { 8 | align-items: center !important; 9 | } 10 | .jsb { 11 | justify-content: space-between !important; 12 | } 13 | .je { 14 | justify-content: flex-end !important; 15 | } 16 | .fdc { 17 | flex-direction: column !important; 18 | } 19 | .jcc { 20 | justify-content: center !important; 21 | } 22 | .fw { 23 | flex-wrap: wrap; 24 | } 25 | .f1 { 26 | flex: 1 !important; 27 | overflow: hidden !important; 28 | } 29 | .h-100 { 30 | height: 100% !important; 31 | } 32 | .w-100 { 33 | width: 100% !important; 34 | } 35 | .m-p-0 { 36 | margin: 0 !important; 37 | padding: 0 !important; 38 | } 39 | .m-r { 40 | margin-right: 20px !important; 41 | } 42 | .tac { 43 | text-align: center !important; 44 | } 45 | .m-t-20 { 46 | margin-top: 20px !important; 47 | } 48 | .m-r-l-20 { 49 | margin-right: 20px !important; 50 | margin-left: 20px !important; 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/common.ts: -------------------------------------------------------------------------------- 1 | import { Store } from './index' 2 | import { getSupOpt } from '@/api/store/common' 3 | import { SupOptType, CommonType } from '@/types/common' 4 | 5 | function getDefaultState(): CommonType { 6 | return { 7 | SupOpt: [] 8 | } 9 | } 10 | const state = getDefaultState() 11 | 12 | class CommonStore extends Store { 13 | public setSupOpt(SupOpt: SupOptType[]): void { 14 | this.state.SupOpt = SupOpt 15 | } 16 | 17 | public resetState(): void { 18 | this.state = getDefaultState() 19 | } 20 | 21 | public getSupOpt() { 22 | return new Promise((resolve) => { 23 | const SupOpt = this.state.SupOpt 24 | if (SupOpt && SupOpt.length) { 25 | resolve(SupOpt) 26 | } else { 27 | getSupOpt().then(({ data }) => { 28 | this.setSupOpt(data) 29 | resolve(data) 30 | }).catch(() => { 31 | this.setSupOpt([]) 32 | resolve([]) 33 | }) 34 | } 35 | }) 36 | } 37 | 38 | protected data(): CommonType { 39 | return state 40 | } 41 | } 42 | 43 | export default new CommonStore() 44 | -------------------------------------------------------------------------------- /src/utils/mixin.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref, nextTick, onMounted, onActivated, onBeforeUnmount } from 'vue' 2 | export default defineComponent({ 3 | setup() { 4 | let domList: HTMLElement[] | null = [] 5 | const multipleTable = ref() 6 | const multipleTableOne = ref() 7 | 8 | const updateTable = (doms: any[] | null = domList) => { 9 | if (doms && doms.length) { 10 | doms.forEach((dom) => dom.doLayout && dom.doLayout()) 11 | } 12 | } 13 | 14 | onMounted(() => { 15 | nextTick(() => { 16 | domList = [] 17 | domList.push(multipleTable.value, multipleTableOne.value) 18 | if (domList.some((item) => item)) { 19 | window.addEventListener('resize', () => updateTable(domList)) 20 | } 21 | }) 22 | }) 23 | 24 | onActivated(updateTable) 25 | 26 | onBeforeUnmount(() => { 27 | if (domList && domList.length) { 28 | domList = [] 29 | window.removeEventListener('resize', () => updateTable(domList)) 30 | } 31 | }) 32 | 33 | return { multipleTable, multipleTableOne } 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/assets/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 42 | -------------------------------------------------------------------------------- /src/layout/components/Settings/checkBoxList.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | imageL: require("@/assets/images/dark.svg"), 3 | alt: "dark", 4 | name: "ThemeDark", 5 | }, 6 | { 7 | imageL: require("@/assets/images/light.svg"), 8 | alt: "light", 9 | name: "ThemeLight", 10 | }, 11 | { 12 | imageL: require("@/assets/images/gray.svg"), 13 | alt: "gray", 14 | name: "ThemeGray", 15 | }, 16 | { 17 | imageL: require("@/assets/images/green.svg"), 18 | alt: "green", 19 | name: "ThemeGreen", 20 | }, 21 | { 22 | imageL: require("@/assets/images/blue.svg"), 23 | alt: "blue", 24 | name: "ThemeBlue", 25 | }, 26 | { 27 | imageL: require("@/assets/images/purple.svg"), 28 | alt: "purple", 29 | name: "ThemePurple", 30 | }, 31 | { 32 | imageL: require("@/assets/images/red.svg"), 33 | alt: "red", 34 | name: "ThemeRed", 35 | }, 36 | { 37 | imageL: require("@/assets/images/yellow.svg"), 38 | alt: "yellow", 39 | name: "ThemeYellow", 40 | }, 41 | { 42 | imageL: require("@/assets/images/orange.svg"), 43 | alt: "orange", 44 | name: "ThemeOrange", 45 | }, 46 | ] -------------------------------------------------------------------------------- /src/assets/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/post.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/store/app.ts: -------------------------------------------------------------------------------- 1 | import { Store } from './index' 2 | import Cookies from 'js-cookie' 3 | import { AppType } from '@/types/app' 4 | 5 | const state: AppType = { 6 | sidebar: { 7 | opened: Cookies.get('sidebarStatus') ? !!Number(Cookies.get('sidebarStatus')) : true, 8 | withoutAnimation: false 9 | }, 10 | device: 'desktop', 11 | size: Cookies.get('size') || 'medium' 12 | } 13 | 14 | class AppStore extends Store { 15 | public toggleSidebar(): void { 16 | const opened = this.state.sidebar.opened 17 | this.state.sidebar = { 18 | opened: !opened, 19 | withoutAnimation: false 20 | } 21 | Cookies.set('sidebarStatus', opened ? '1' : '0') 22 | } 23 | 24 | public closeSideBar(withoutAnimation: boolean): void { 25 | Cookies.set('sidebarStatus', '0') 26 | this.state.sidebar = { 27 | opened: false, 28 | withoutAnimation 29 | } 30 | } 31 | 32 | public toggleDevice(device: string): void { 33 | this.state.device = device 34 | } 35 | 36 | public setSize(size: string): void { 37 | this.state.size = size 38 | Cookies.set('size', size) 39 | } 40 | 41 | protected data(): AppType { 42 | return state 43 | } 44 | } 45 | 46 | export default new AppStore() 47 | -------------------------------------------------------------------------------- /src/assets/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/checkbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/jsencrypt.ts: -------------------------------------------------------------------------------- 1 | import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' 2 | 3 | // 密钥对生成 http://web.chacuo.net/netrsakeypair 4 | 5 | const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + 6 | 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' 7 | 8 | const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + 9 | '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + 10 | 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + 11 | 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + 12 | 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + 13 | 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + 14 | 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + 15 | 'UP8iWi1Qw0Y=' 16 | 17 | // 加密 18 | export function encrypt(txt) { 19 | const encryptor = new JSEncrypt() 20 | encryptor.setPublicKey(publicKey) // 设置公钥 21 | return encryptor.encrypt(txt) // 对数据进行加密 22 | } 23 | 24 | // 解密 25 | export function decrypt(txt) { 26 | const encryptor = new JSEncrypt() 27 | encryptor.setPrivateKey(privateKey) // 设置私钥 28 | return encryptor.decrypt(txt) // 对数据进行解密 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/api/system/menu.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 查询菜单列表 4 | export const listMenu = (params) => { 5 | return request({ 6 | url: '/sysmenu/list', 7 | method: 'get', 8 | params 9 | }) 10 | } 11 | 12 | // 查询菜单详细 13 | export function getMenu(menuId) { 14 | return request({ 15 | url: '/sysmenu/getById/' + menuId, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | // 查询菜单下拉树结构 21 | export function treeselect() { 22 | return request({ 23 | url: '/sysmenu/getRoleMenus', 24 | method: 'get' 25 | }) 26 | } 27 | 28 | // 根据角色ID查询菜单下拉树结构 29 | export function roleMenuTreeselect(roleId) { 30 | return request({ 31 | url: '/sysmenu/getRoleMenus', 32 | method: 'get', 33 | params: { 34 | roleId 35 | } 36 | }) 37 | } 38 | 39 | // 新增菜单 40 | export function addMenu(data) { 41 | return request({ 42 | url: '/sysmenu/create', 43 | method: 'post', 44 | data 45 | }) 46 | } 47 | 48 | // 修改菜单 49 | export function updateMenu(data) { 50 | return request({ 51 | url: '/sysmenu/create', 52 | method: 'post', 53 | data 54 | }) 55 | } 56 | 57 | // 删除菜单 58 | export function delMenu(menuId) { 59 | return request({ 60 | url: '/sysmenu/deleteById/' + menuId, 61 | method: 'get' 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /src/assets/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/validCode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/radio.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/select.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ThemePicker/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | 37 | 53 | -------------------------------------------------------------------------------- /src/assets/icons/svg/upload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3back", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "element-plus": "^2.2.22", 13 | "vue": "^3.2.45", 14 | "vue-class-component": "^7.2.6", 15 | "vue-router": "^4.1.6" 16 | }, 17 | "devDependencies": { 18 | "@types/js-cookie": "^2.2.7", 19 | "@types/nprogress": "^0.2.0", 20 | "@types/quill": "^2.0.9", 21 | "@types/vuedraggable": "^2.24.0", 22 | "@vue/cli-plugin-babel": "~4.5.0", 23 | "@vue/cli-plugin-router": "~4.5.0", 24 | "@vue/cli-plugin-typescript": "~4.5.0", 25 | "@vue/cli-service": "~4.5.0", 26 | "@vue/compiler-sfc": "^3.0.0", 27 | "axios": "^0.21.4", 28 | "file-saver": "^2.0.4", 29 | "fuse.js": "^6.4.3", 30 | "js-cookie": "^3.0.1", 31 | "jsencrypt": "3.0.0-rc.1", 32 | "moment": "^2.29.1", 33 | "nprogress": "0.2.0", 34 | "quill": "1.3.7", 35 | "sass": "^1.26.5", 36 | "sass-loader": "8.0.2", 37 | "screenfull": "5.0.2", 38 | "sortablejs": "1.10.2", 39 | "svg-sprite-loader": "5.1.1", 40 | "typescript": "4.3.5", 41 | "typings-for-css-modules-loader": "^1.7.0", 42 | "vuedraggable": "^2.24.3", 43 | "xlsx": "^0.17.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/assets/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/zipdownload.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import baseUrl from '@/config/baseUrl' 3 | import { getToken } from '@/utils/auth' 4 | 5 | const mimeMap = { 6 | xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 7 | zip: 'application/zip' 8 | } 9 | 10 | export function downLoadZip(str, filename) { 11 | const url = baseUrl + str 12 | axios({ 13 | url, 14 | method: 'get', 15 | responseType: 'blob', 16 | headers: { Authorization: 'Bearer ' + getToken() } 17 | }).then((res) => { 18 | resolveBlob(res, mimeMap.zip) 19 | }) 20 | } 21 | /** 22 | * 解析blob响应内容并下载 23 | * @param {*} res blob响应内容 24 | * @param {String} mimeType MIME类型 25 | */ 26 | export function resolveBlob(res, mimeType) { 27 | const aLink = document.createElement('a') 28 | const blob = new Blob([res.data], { type: mimeType }) 29 | // 从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名; 30 | const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') 31 | const contentDisposition = decodeURI(res.headers['content-disposition']) 32 | const result: any = patt.exec(contentDisposition) 33 | let fileName = result[1] 34 | fileName = fileName.replace(/\"/g, '') 35 | aLink.href = URL.createObjectURL(blob) 36 | aLink.setAttribute('download', fileName) // 设置下载文件名称 37 | document.body.appendChild(aLink) 38 | aLink.click() 39 | document.body.appendChild(aLink) 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/permission.ts: -------------------------------------------------------------------------------- 1 | import userStore from '@/store/user' 2 | 3 | /** 4 | * 字符权限校验 5 | * @param {Array} value 校验值 6 | * @returns {Boolean} 7 | */ 8 | export function checkPermi(value) { 9 | if (value && value instanceof Array && value.length > 0) { 10 | const permissions = userStore.getState().permissions 11 | const permissionDatas = value 12 | const allPermission = '*:*:*'; 13 | 14 | const hasPermission = permissions.some((permission) => { 15 | return allPermission === permission || permissionDatas.includes(permission) 16 | }) 17 | 18 | if (!hasPermission) { 19 | return false 20 | } 21 | return true 22 | } else { 23 | console.error(`need roles! Like checkPermi='['system:user:add','system:user:edit']'`) 24 | return false 25 | } 26 | } 27 | 28 | /** 29 | * 角色权限校验 30 | * @param {Array} value 校验值 31 | * @returns {Boolean} 32 | */ 33 | export function checkRole(value) { 34 | if (value && value instanceof Array && value.length > 0) { 35 | const roles = userStore.getState().roles 36 | const permissionRoles = value 37 | const superAdmin = 'admin'; 38 | 39 | const hasRole = roles.some((role) => { 40 | return superAdmin === role || permissionRoles.includes(role) 41 | }) 42 | 43 | if (!hasRole) { 44 | return false 45 | } 46 | return true 47 | } else { 48 | console.error(`need roles! Like checkRole='['admin','editor']'`) 49 | return false 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/TableColumn/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /src/assets/icons/svg/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/log.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 45 | 46 | 47 | 60 | -------------------------------------------------------------------------------- /src/utils/print.ts: -------------------------------------------------------------------------------- 1 | import { ElMessage } from 'element-plus' 2 | 3 | // tslint:disable-next-line 4 | export default function (templateUrl: string, paramData: any) { 5 | return new Promise((resolve, reject) => { 6 | try { 7 | function printStart() { 8 | const jsonData = { 9 | Table1: paramData 10 | } 11 | // @ts-ignore: Unreachable code error 12 | const report = new Stimulsoft.Report.StiReport(); 13 | report.loadFile(templateUrl, paramData); // 默认可以mrt文件方式初始化 14 | // @ts-ignore: Unreachable code error 15 | const dataSet = new Stimulsoft.System.Data.DataSet('data'); 16 | report.dictionary.databases.clear(); 17 | dataSet.readJson(jsonData); 18 | report.regData('data', 'data', dataSet); 19 | report.render(); 20 | report.reportName = ''; 21 | report.print(); 22 | resolve('ok'); 23 | } 24 | // @ts-ignore: Unreachable code error 25 | if (typeof Stimulsoft === 'undefined') { // 没有这个打印对象 26 | const script = document.createElement('script'); 27 | script.type = 'text/javascript'; 28 | script.src = 'static/stimulsoft/stimulsoft.reports.js'; 29 | document.getElementsByTagName('head')[0].appendChild(script); 30 | script.onload = () => { 31 | printStart(); 32 | }; 33 | } else { 34 | printStart(); 35 | } 36 | } catch (e) { 37 | ElMessage({ 38 | message: '打印出错了', 39 | type: 'warning', 40 | duration: 5 * 1000 41 | }) 42 | reject(e) 43 | } 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/assets/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | 14 | &::-webkit-scrollbar { 15 | width: 6px; 16 | } 17 | 18 | &::-webkit-scrollbar-thumb { 19 | background: #99a9bf; 20 | border-radius: 20px; 21 | } 22 | } 23 | 24 | @mixin relative { 25 | position: relative; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | @mixin pct($pct) { 31 | width: #{$pct}; 32 | position: relative; 33 | margin: 0 auto; 34 | } 35 | 36 | @mixin triangle($width, $height, $color, $direction) { 37 | $width: $width/2; 38 | $color-border-style: $height solid $color; 39 | $transparent-border-style: $width solid transparent; 40 | height: 0; 41 | width: 0; 42 | 43 | @if $direction==up { 44 | border-bottom: $color-border-style; 45 | border-left: $transparent-border-style; 46 | border-right: $transparent-border-style; 47 | } 48 | 49 | @else if $direction==right { 50 | border-left: $color-border-style; 51 | border-top: $transparent-border-style; 52 | border-bottom: $transparent-border-style; 53 | } 54 | 55 | @else if $direction==down { 56 | border-top: $color-border-style; 57 | border-left: $transparent-border-style; 58 | border-right: $transparent-border-style; 59 | } 60 | 61 | @else if $direction==left { 62 | border-right: $color-border-style; 63 | border-top: $transparent-border-style; 64 | border-bottom: $transparent-border-style; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/api/store/login.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { LoginType } from '@/types/user' 3 | 4 | export function login(data: LoginType) { 5 | return request({ 6 | url: '/tokens', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | export function getInfo(token: string) { 13 | return request({ 14 | url: 'system/user/currentUser', 15 | method: 'get', 16 | params: { token } 17 | }) 18 | } 19 | 20 | 21 | export function logout(sUserCode: string) { 22 | return request({ 23 | url: `/tokens/${sUserCode}`, 24 | method: 'delete' 25 | }) 26 | } 27 | 28 | interface PassWordType { 29 | sUserId: string; 30 | oldPassword: string; 31 | newPassword: string; 32 | sPassword: string; 33 | } 34 | 35 | // 验证原始密码是否正确 36 | export function verification(data: PassWordType) { 37 | return request({ 38 | url: `system/user/verification`, 39 | method: 'post', 40 | data 41 | }) 42 | } 43 | // 密码修改 44 | export function modify(data: PassWordType) { 45 | return request({ 46 | url: `system/user/modify`, 47 | method: 'put', 48 | data 49 | }) 50 | } 51 | 52 | // 获取医院信息 53 | export function getHospitalData() { 54 | return request({ 55 | url: `system/user/org`, 56 | method: 'get', 57 | }) 58 | } 59 | 60 | // 获取医院信息 61 | export function getMenuPermission() { 62 | return request({ 63 | url: `sysmenu/getMenuPermission`, 64 | method: 'get', 65 | }) 66 | } 67 | 68 | // 获取未读消息数量 69 | export function getMessageData() { 70 | return request({ 71 | url: `msgInfo/getMsg`, 72 | method: 'get', 73 | params: { isLoading: 1 } 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /src/assets/icons/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/AppMain/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 32 | 33 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 43 | 44 | 59 | -------------------------------------------------------------------------------- /src/store/settings.ts: -------------------------------------------------------------------------------- 1 | import { Store } from './index' 2 | import { SettingType } from '@/types/setting' 3 | import variables from '@/assets/styles/variables.scss' 4 | import variablesElement from '@/assets/styles/element-variables.scss' 5 | import defaultSettings from '@/settings' 6 | import { 7 | setSetting, 8 | getSetting 9 | } from '@/utils/setting' 10 | 11 | const settings = getSetting() 12 | if (!settings) { 13 | setSetting('theme', variablesElement.theme) 14 | setSetting('sideTheme', defaultSettings.sideTheme) 15 | setSetting('showSettings', defaultSettings.showSettings) 16 | setSetting('tagsView', defaultSettings.tagsView) 17 | setSetting('fixedHeader', defaultSettings.fixedHeader) 18 | setSetting('sidebarLogo', defaultSettings.sidebarLogo) 19 | setSetting('menuDrag', defaultSettings.menuDrag) 20 | setSetting('menuWidth', variables.sideBarWidthNumber) 21 | } 22 | 23 | const state: SettingType = { 24 | theme: getSetting('theme'), 25 | sideTheme: getSetting('sideTheme'), 26 | showSettings: getSetting('showSettings'), 27 | tagsView: getSetting('tagsView'), 28 | fixedHeader: getSetting('fixedHeader'), 29 | sidebarLogo: getSetting('sidebarLogo'), 30 | menuDrag: getSetting('menuDrag'), 31 | menuWidth: getSetting('menuWidth') 32 | } 33 | 34 | class SettingsStore extends Store { 35 | public changeSetting(key: string, value: string | number | boolean): void { 36 | if (this.state.hasOwnProperty(key)) { 37 | this.state[key] = value 38 | setSetting(key, value) 39 | } 40 | } 41 | 42 | protected data(): SettingType { 43 | return state 44 | } 45 | } 46 | 47 | export default new SettingsStore() 48 | -------------------------------------------------------------------------------- /src/assets/icons/svg/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/logininfor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/rate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/job.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 项目入口 3 | * @Version: 1.0 4 | * @Autor: yong.zhu 5 | * @Date: 2021-09-27 16:25:41 6 | * @LastEditors: yong.zhu 7 | * @LastEditTime: 2021-10-08 17:21:07 8 | */ 9 | import { createApp } from 'vue' 10 | import App from './App.vue' 11 | import router from './router' 12 | import Cookies from 'js-cookie' 13 | import ElementPlus from 'element-plus' 14 | import 'element-plus/dist/index.css' 15 | import locale from 'element-plus/lib/locale/lang/zh-cn' 16 | 17 | // 公共样式 18 | import '@/assets/styles/index.scss' 19 | import '@/assets/icons' 20 | // 权限控制 21 | import './permission' 22 | // 全局组件引入 23 | import SvgIcon from '@/components/SvgIcon/index.vue' 24 | import Pagination from '@/components/Pagination/index.vue' 25 | import TableColumn from '@/components/TableColumn/index.vue' 26 | import RightToolbar from '@/components/RightToolbar/index.vue' 27 | // 主题颜色风格逻辑引入 28 | import theme from '@/utils/theme' 29 | import { getSetting } from '@/utils/setting' 30 | // 自定义指令引入 31 | import directive from '@/directive' 32 | // 全局mixin引入 33 | // import mixin from '@/utils/mixin.js' 34 | // 主题颜色风格初始化 35 | theme(getSetting('theme')) 36 | // 创建app 37 | const app = createApp(App) 38 | // 全局注册自定义指令 39 | directive(app) 40 | // 全局注册mixin 41 | // app.mixin(mixin) 42 | // 注册ElementPlus 43 | app.use(ElementPlus, { 44 | locale, // 语言设置 45 | size: Cookies.get('size') || 'medium' // 尺寸设置 46 | }) 47 | // 全局组件祖册 48 | app.component( 49 | 'SvgIcon', 50 | // 如果这个组件选项是通过 `export default` 导出的,那么就会优先使用 `.default`,否则回退到使用模块的根 51 | SvgIcon.default || SvgIcon 52 | ) 53 | app.component('Pagination', Pagination) 54 | app.component('RightToolbar', RightToolbar) 55 | app.component('el-table-column', TableColumn) 56 | // 注册路由 57 | app.use(router).mount('#app') 58 | -------------------------------------------------------------------------------- /src/assets/icons/svg/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/swagger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/TagsView/ScrollPane.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 62 | 63 | 77 | -------------------------------------------------------------------------------- /src/assets/styles/btn.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | 3 | @mixin colorBtn($color) { 4 | background: $color; 5 | 6 | &:hover { 7 | color: $color; 8 | 9 | &:before, 10 | &:after { 11 | background: $color; 12 | } 13 | } 14 | } 15 | 16 | .blue-btn { 17 | @include colorBtn($blue) 18 | } 19 | 20 | .light-blue-btn { 21 | @include colorBtn($light-blue) 22 | } 23 | 24 | .red-btn { 25 | @include colorBtn($red) 26 | } 27 | 28 | .pink-btn { 29 | @include colorBtn($pink) 30 | } 31 | 32 | .green-btn { 33 | @include colorBtn($green) 34 | } 35 | 36 | .tiffany-btn { 37 | @include colorBtn($tiffany) 38 | } 39 | 40 | .yellow-btn { 41 | @include colorBtn($yellow) 42 | } 43 | 44 | .pan-btn { 45 | font-size: 14px; 46 | color: #fff; 47 | padding: 14px 36px; 48 | border-radius: 8px; 49 | border: none; 50 | outline: none; 51 | transition: 600ms ease all; 52 | position: relative; 53 | display: inline-block; 54 | 55 | &:hover { 56 | background: #fff; 57 | 58 | &:before, 59 | &:after { 60 | width: 100%; 61 | transition: 600ms ease all; 62 | } 63 | } 64 | 65 | &:before, 66 | &:after { 67 | content: ''; 68 | position: absolute; 69 | top: 0; 70 | right: 0; 71 | height: 2px; 72 | width: 0; 73 | transition: 400ms ease all; 74 | } 75 | 76 | &::after { 77 | right: inherit; 78 | top: inherit; 79 | left: 0; 80 | bottom: 0; 81 | } 82 | } 83 | 84 | .custom-button { 85 | display: inline-block; 86 | line-height: 1; 87 | white-space: nowrap; 88 | cursor: pointer; 89 | background: #fff; 90 | color: #fff; 91 | -webkit-appearance: none; 92 | text-align: center; 93 | box-sizing: border-box; 94 | outline: 0; 95 | margin: 0; 96 | padding: 10px 15px; 97 | font-size: 14px; 98 | border-radius: 4px; 99 | } 100 | -------------------------------------------------------------------------------- /src/components/SizeSelect/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/Logo.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | 30 | 78 | -------------------------------------------------------------------------------- /src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import { RouteRecordRaw } from 'vue-router' 3 | import userStore from '@/store/user' 4 | import permissionStore from '@/store/permission' 5 | import NProgress from 'nprogress' 6 | import 'nprogress/nprogress.css' 7 | import { getToken } from '@/utils/auth' 8 | 9 | NProgress.configure({ 10 | showSpinner: false 11 | }) 12 | 13 | const whiteList = ['/login', '/codeControl'] 14 | 15 | router.beforeEach((to, from, next) => { 16 | NProgress.start() 17 | if (getToken()) { 18 | if (to.path === '/login') { 19 | next({ 20 | path: '/' 21 | }) 22 | NProgress.done() 23 | } else { 24 | if (!userStore.getState().sUserId) { 25 | userStore.getUserInfo().then(() => { 26 | const promiseArr = [ 27 | permissionStore.createRouters(), 28 | userStore.getHospitalData(), 29 | userStore.getMenuPermission() 30 | ] 31 | return Promise.all(promiseArr) 32 | }).then((resList) => { // 动态添加的路由 33 | (resList[0] as RouteRecordRaw[]).forEach((item) => { 34 | router.addRoute(item) 35 | }) 36 | next({ 37 | ...to, 38 | replace: true 39 | }) 40 | }).catch(() => { 41 | userStore.fedLogOut() 42 | next({ path: '/' }) 43 | }) 44 | } else { 45 | next() 46 | } 47 | } 48 | } else { 49 | if (whiteList.indexOf(to.path) !== -1) { 50 | next() 51 | } else { 52 | next(`/login?redirect=${to.fullPath}`) 53 | NProgress.done() 54 | } 55 | } 56 | }) 57 | 58 | 59 | const whiteList2 = ['/index', '/login', '/codeControl', '/404', '/401'] 60 | router.afterEach((to) => { 61 | // if (whiteList2.indexOf(to.path) === -1 && to.path.indexOf('/redirect/') === -1) { 62 | // const toPath = { 63 | // path: to.path, 64 | // name: to.meta.title 65 | // } 66 | // store.dispatch('changeRecentlyUsedList', toPath) 67 | // } 68 | NProgress.done() 69 | }) 70 | -------------------------------------------------------------------------------- /src/utils/scroll-to.ts: -------------------------------------------------------------------------------- 1 | (Math as any).easeInOutQuad = (t, b, c, d) => { 2 | t /= d / 2 3 | if (t < 1) { 4 | return c / 2 * t * t + b 5 | } 6 | t-- 7 | return -c / 2 * (t * (t - 2) - 1) + b 8 | } 9 | 10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts 11 | const requestAnimFrame = (() => { 12 | // tslint:disable-next-line 13 | return (window as any).requestAnimationFrame || (window as any).webkitRequestAnimationFrame || (window as any).mozRequestAnimationFrame || function (callback) { 14 | window.setTimeout(callback, 1000 / 60) 15 | } 16 | })() 17 | 18 | /** 19 | * Because it's so fucking difficult to detect the scrolling element, just move them all 20 | * @param {number} amount 21 | */ 22 | function move(amount) { 23 | document.documentElement.scrollTop = amount 24 | // @ts-ignore 25 | document.body.parentNode.scrollTop = amount 26 | document.body.scrollTop = amount 27 | } 28 | 29 | function position() { 30 | // @ts-ignore 31 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop 32 | } 33 | 34 | /** 35 | * @param {number} to 36 | * @param {number} duration 37 | * @param {Function} callback 38 | */ 39 | export function scrollTo(to, duration, callback = null) { 40 | const start = position() 41 | const change = to - start 42 | const increment = 20 43 | let currentTime = 0 44 | duration = (typeof (duration) === 'undefined') ? 500 : duration 45 | const animateScroll = () => { 46 | // increment the time 47 | currentTime += increment 48 | // find the value with the quadratic in-out easing function 49 | const val = (Math as any).easeInOutQuad(currentTime, start, change, duration) 50 | // move the document.body 51 | move(val) 52 | // do the animation unless its over 53 | if (currentTime < duration) { 54 | requestAnimFrame(animateScroll) 55 | } else { 56 | if (callback && typeof callback === 'function') { 57 | // @ts-ignore 58 | callback() 59 | } 60 | } 61 | } 62 | animateScroll() 63 | } 64 | -------------------------------------------------------------------------------- /src/components/IconSelect/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 49 | 50 | 75 | -------------------------------------------------------------------------------- /src/assets/icons/svg/date-range.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/TableColumn/columnArr.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "商品编码", 4 | "minWidth": "160" 5 | }, 6 | { 7 | "name": "商品名称", 8 | "minWidth": "200" 9 | }, 10 | { 11 | "name": "供应商名称", 12 | "minWidth": "300" 13 | }, 14 | { 15 | "name": "二级供应商", 16 | "minWidth": "300" 17 | }, 18 | { 19 | "name": "规格", 20 | "minWidth": "170" 21 | }, 22 | { 23 | "name": "型号", 24 | "minWidth": "100" 25 | }, 26 | { 27 | "name": "配送规格", 28 | "minWidth": "180" 29 | }, 30 | { 31 | "name": "注册证号", 32 | "minWidth": "280" 33 | }, 34 | { 35 | "name": "生产厂家", 36 | "minWidth": "280" 37 | }, 38 | { 39 | "name": "库房名称", 40 | "minWidth": "180" 41 | }, 42 | { 43 | "name": "二级库房", 44 | "minWidth": "180" 45 | }, 46 | { 47 | "name": "科室名称", 48 | "minWidth": "180" 49 | }, 50 | { 51 | "name": "商品分类", 52 | "minWidth": "170" 53 | }, 54 | { 55 | "name": "批号", 56 | "minWidth": "180" 57 | }, 58 | { 59 | "name": "批次号", 60 | "minWidth": "220" 61 | }, 62 | { 63 | "name": "条码", 64 | "minWidth": "180" 65 | }, 66 | { 67 | "name": "单位", 68 | "minWidth": "70" 69 | }, 70 | { 71 | "name": "效期", 72 | "minWidth": "110" 73 | }, 74 | { 75 | "name": "生产日期", 76 | "minWidth": "110" 77 | }, 78 | { 79 | "name": "单价", 80 | "minWidth": "150" 81 | }, 82 | { 83 | "name": "金额", 84 | "minWidth": "200" 85 | }, 86 | { 87 | "name": "单号", 88 | "minWidth": "180" 89 | }, 90 | { 91 | "name": "创建时间", 92 | "minWidth": "160" 93 | }, 94 | { 95 | "name": "修改时间", 96 | "minWidth": "160" 97 | }, 98 | { 99 | "name": "审核时间", 100 | "minWidth": "160" 101 | }, 102 | { 103 | "name": "审核人", 104 | "minWidth": "170" 105 | }, 106 | { 107 | "name": "创建人", 108 | "minWidth": "170" 109 | }, 110 | { 111 | "name": "修改人", 112 | "minWidth": "170" 113 | }, 114 | { 115 | "name": "状态", 116 | "minWidth": "120" 117 | } 118 | ] -------------------------------------------------------------------------------- /src/components/ImagePopup/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 40 | 41 | -------------------------------------------------------------------------------- /src/views/base/error/401.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 49 | 50 | 89 | -------------------------------------------------------------------------------- /src/assets/icons/svg/shopping.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/cascader.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/TooltipOver/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 74 | 75 | -------------------------------------------------------------------------------- /src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} path 3 | * @returns {Boolean} 4 | */ 5 | export function isExternal(path: string) { 6 | return /^(https?:|mailto:|tel:)/.test(path) 7 | } 8 | 9 | /** 10 | * @param {string} str 11 | * @returns {Boolean} 12 | */ 13 | export function validUsername(str: string) { 14 | const validMap = ['admin', 'editor'] 15 | return validMap.indexOf(str.trim()) >= 0 16 | } 17 | 18 | /** 19 | * @param {string} url 20 | * @returns {Boolean} 21 | */ 22 | export function validURL(url: string) { 23 | const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 24 | return reg.test(url) 25 | } 26 | 27 | /** 28 | * @param {string} str 29 | * @returns {Boolean} 30 | */ 31 | export function validLowerCase(str: string) { 32 | const reg = /^[a-z]+$/ 33 | return reg.test(str) 34 | } 35 | 36 | /** 37 | * @param {string} str 38 | * @returns {Boolean} 39 | */ 40 | export function validUpperCase(str: string) { 41 | const reg = /^[A-Z]+$/ 42 | return reg.test(str) 43 | } 44 | 45 | /** 46 | * @param {string} str 47 | * @returns {Boolean} 48 | */ 49 | export function validAlphabets(str: string) { 50 | const reg = /^[A-Za-z]+$/ 51 | return reg.test(str) 52 | } 53 | 54 | /** 55 | * @param {string} email 56 | * @returns {Boolean} 57 | */ 58 | export function validEmail(email: string) { 59 | const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 60 | return reg.test(email) 61 | } 62 | 63 | /** 64 | * @param {string} str 65 | * @returns {Boolean} 66 | */ 67 | export function isString(str: any) { 68 | if (typeof str === 'string' || str instanceof String) { 69 | return true 70 | } 71 | return false 72 | } 73 | 74 | /** 75 | * @param {Array} arg 76 | * @returns {Boolean} 77 | */ 78 | export function isArray(arg: any) { 79 | if (typeof Array.isArray === 'undefined') { 80 | return Object.prototype.toString.call(arg) === '[object Array]' 81 | } 82 | return Array.isArray(arg) 83 | } 84 | -------------------------------------------------------------------------------- /src/assets/icons/svg/button.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/RightToolbar/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 64 | 65 | 76 | -------------------------------------------------------------------------------- /src/assets/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Pagination/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 84 | 85 | 93 | -------------------------------------------------------------------------------- /src/utils/sortable.ts: -------------------------------------------------------------------------------- 1 | import Sortable from 'sortablejs' 2 | 3 | // 行拖拽 4 | export function rowDrag(domClass, tableData, callback) { 5 | if (!tableData.length) { 6 | return 7 | } 8 | if (!domClass) { 9 | domClass = '' 10 | } else { 11 | domClass = '.' + domClass 12 | } 13 | // 首先获取需要拖拽的dom节点 14 | const wrapperTbody = document.querySelector(`${domClass} .el-table__body-wrapper table > tbody`) 15 | console.log('wrapperTbody', wrapperTbody) 16 | if (!wrapperTbody) { 17 | return 18 | } 19 | Sortable.create(wrapperTbody, { 20 | disabled: false, // 是否开启拖拽 21 | ghostClass: 'sortable-ghost', // 拖拽样式 22 | animation: 150, // 拖拽延时,效果更好看 23 | group: { // 是否开启跨表拖拽 24 | pull: false, 25 | put: false 26 | }, 27 | onEnd: ({ 28 | newIndex, 29 | oldIndex 30 | }) => { 31 | if (oldIndex === newIndex) { 32 | return 33 | } 34 | // 更新数据位置 35 | tableData.splice(newIndex, 0, tableData.splice(oldIndex, 1)[0]) // 数据处理 36 | tableData.map((item, index) => item.sort = index + 1) 37 | // tslint:disable-next-line 38 | typeof callback === 'function' && callback(tableData) 39 | } 40 | }) 41 | } 42 | 43 | // 列拖拽 44 | export function columnDrag(domClass, columnList, callback) { 45 | if (!columnList.length) { 46 | return 47 | } 48 | if (!domClass) { 49 | domClass = '' 50 | } else { 51 | domClass = '.' + domClass 52 | } 53 | const wrapperTr = document.querySelector(`.${domClass} .el-table__header-wrapper tr`) 54 | if (!wrapperTr) { 55 | return 56 | } 57 | Sortable.create(wrapperTr, { 58 | disabled: false, // 是否开启拖拽 59 | ghostClass: 'sortable-ghost', // 拖拽样式 60 | animation: 150, // 拖拽延时,效果更好看 61 | group: { // 是否开启跨表拖拽 62 | pull: false, 63 | put: false 64 | }, 65 | onEnd: ({ 66 | newIndex, 67 | oldIndex 68 | }) => { 69 | if (oldIndex === newIndex) { 70 | return 71 | } 72 | // 更新数据位置 73 | columnList.splice(newIndex, 0, columnList.splice(oldIndex, 1)[0]) 74 | columnList.map((item, index) => item.sort = index + 1) 75 | // 更改key值以重新渲染页面 76 | if (columnList[newIndex].key < columnList.length) { 77 | columnList[newIndex].key += columnList.length 78 | } else { 79 | columnList[newIndex].key -= columnList.length 80 | } 81 | // 执行回调 82 | // tslint:disable-next-line 83 | typeof callback === 'function' && callback(columnList) 84 | } 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 66 | 67 | 77 | -------------------------------------------------------------------------------- /src/api/system/role.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 查询角色列表 4 | export function listRole(query) { 5 | return request({ 6 | url: '/role/list', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | // 查询角色详细 13 | export function getRole(roleId) { 14 | return request({ 15 | url: '/role/getById/' + roleId, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | // 创建角色 21 | export function createRole(data) { 22 | return request({ 23 | url: '/role/create', 24 | method: 'post', 25 | data 26 | }) 27 | } 28 | 29 | // 修改角色 30 | export function updateRole(data) { 31 | return request({ 32 | url: '/role/create', 33 | method: 'post', 34 | data 35 | }) 36 | } 37 | 38 | // 角色数据权限 39 | export function dataScope(data) { 40 | return request({ 41 | url: '/system/role/dataScope', 42 | method: 'put', 43 | data 44 | }) 45 | } 46 | 47 | // 角色状态修改 48 | export function changeRoleStatus(roleId, status) { 49 | return request({ 50 | url: '/role/updateStatus', 51 | method: 'get', 52 | params: { 53 | roleId, 54 | status 55 | } 56 | }) 57 | } 58 | 59 | // 删除角色 60 | export function delRole(roleId) { 61 | return request({ 62 | url: '/role/deleteById/' + roleId, 63 | method: 'get' 64 | }) 65 | } 66 | 67 | // 获取已关联用户信息 68 | export function getHavenUserList(roleId: string) { 69 | return request({ 70 | url: '/system/role/user?sRoleId=' + roleId, 71 | method: 'get' 72 | }) 73 | } 74 | 75 | interface GetOptionalUserType { 76 | pageNum: number; 77 | pageSize: number; 78 | userName?: string 79 | } 80 | export function getOptionalUser(params: GetOptionalUserType) { 81 | return request({ 82 | url: '/system/org/optionalUser2', 83 | method: 'get', 84 | params 85 | }) 86 | } 87 | 88 | interface AddRoleType { 89 | sRoleId: string, 90 | sUserIds: string[] 91 | } 92 | export function addRole(data: AddRoleType) { 93 | return request({ 94 | url: '/system/role/user', 95 | method: 'post', 96 | data 97 | }) 98 | } 99 | 100 | // 删除用户对照 101 | interface UserContrast { 102 | sRoleId: string, 103 | sUserId: string 104 | } 105 | export function delUserContrast(data: UserContrast) { 106 | return request({ 107 | url: '/role/user', 108 | method: 'delete', 109 | data 110 | }) 111 | } 112 | 113 | export function getRoleAll(sUserId) { 114 | return request({ 115 | url: `/system/user/roleAll${sUserId}`, 116 | method: 'get' 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /src/assets/icons/svg/tool.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/light.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/red.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/yellow.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Autor: yong.zhu 3 | * @Date: 2021-10-08 17:55:55 4 | * @LastEditors: yong.zhu 5 | * @LastEditTime: 2021-11-10 16:48:00 6 | * @Description: 路由 7 | * @Version: 1.0 8 | */ 9 | import { createRouter, createWebHashHistory, RouteRecordRaw, RouterOptions } from 'vue-router' 10 | import Layout from '@/layout/index.vue' 11 | 12 | const defaultList = [{ 13 | path: 'index', 14 | component: () => import('@/views/base/index.vue'), 15 | name: '首页', 16 | meta: { 17 | title: '首页', 18 | icon: 'dashboard', 19 | noCache: true, 20 | affix: true 21 | } 22 | } 23 | // 以下注释代码为初始页面, 菜单列表接口为空或者后端接口未调通的情况下暂时写死 24 | // { 25 | // path: 'menuAdministration', 26 | // component: () => import('@/views/systemManagement/menuAdministration/index.vue'), 27 | // name: '菜单', 28 | // meta: { 29 | // title: '菜单', 30 | // icon: 'dashboard', 31 | // noCache: true, 32 | // affix: true 33 | // } 34 | // } 35 | ] 36 | 37 | export const routes: RouteRecordRaw[] = [{ 38 | path: '/redirect', 39 | component: Layout, 40 | meta: { 41 | hidden: true 42 | }, 43 | children: [{ 44 | path: '/redirect/:path(.*)', 45 | component: () => import('@/views/base/redirect.vue') 46 | }] 47 | }, 48 | { 49 | path: '/login', 50 | name: 'login', 51 | component: () => import('@/views/base/login.vue'), 52 | meta: { 53 | hidden: true, 54 | noCache: true 55 | } 56 | }, 57 | { 58 | path: '/404', 59 | name: '404', 60 | component: () => import('@/views/base/error/404.vue'), 61 | meta: { 62 | hidden: true, 63 | noCache: true 64 | } 65 | }, 66 | { 67 | path: '/401', 68 | name: '401', 69 | component: () => import('@/views/base/error/401.vue'), 70 | meta: { 71 | hidden: true, 72 | noCache: true 73 | } 74 | }, 75 | { 76 | path: '/', 77 | component: Layout, 78 | redirect: 'index', 79 | meta: { 80 | hidden: true 81 | }, 82 | children: defaultList 83 | }] 84 | 85 | const routerParams: RouterOptions = { 86 | history: createWebHashHistory(process.env.BASE_URL), 87 | routes, 88 | scrollBehavior() { 89 | return { left: 0, top: 0 } 90 | } 91 | } 92 | 93 | const router = createRouter(routerParams) 94 | 95 | // 去除重复路由报错的问题 96 | const originalPush = router.push 97 | router.push = function push(location) { 98 | return originalPush.call(this, location).catch((err) => err) 99 | } 100 | 101 | export function resetRouter() { 102 | const newRouter = createRouter(routerParams) 103 | router.resolve = newRouter.resolve 104 | } 105 | 106 | export default router 107 | -------------------------------------------------------------------------------- /src/assets/images/gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/green.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/images/purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/icons/svg/redis.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/dict.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icons/svg/time-range.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/initial/user.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { praseStrEmpty } from '@/utils/ruoyi'; 3 | 4 | // 查询用户列表 5 | export function listUser(query) { 6 | return request({ 7 | url: '/system/user/list', 8 | method: 'get', 9 | params: query 10 | }) 11 | } 12 | 13 | // 查询用户详细 14 | export function getUser(params) { 15 | return request({ 16 | url: '/system/user', 17 | method: 'get', 18 | params 19 | }) 20 | } 21 | 22 | // 同步用户 23 | export function syncUser() { 24 | return request({ 25 | url: '/system/user/sync', 26 | method: 'get' 27 | }) 28 | } 29 | 30 | // 新增用户 31 | export function addUser(data) { 32 | return request({ 33 | url: '/system/user', 34 | method: 'post', 35 | data 36 | }) 37 | } 38 | 39 | // 修改用户 40 | export function updateUser(data) { 41 | return request({ 42 | url: '/system/user', 43 | method: 'put', 44 | data 45 | }) 46 | } 47 | 48 | // 删除用户 49 | export function delUser(userId) { 50 | return request({ 51 | url: '/system/user/' + userId, 52 | method: 'delete' 53 | }) 54 | } 55 | 56 | // 导出用户 57 | export function exportUser(query) { 58 | return request({ 59 | url: '/system/user/export', 60 | method: 'get', 61 | params: query 62 | }) 63 | } 64 | 65 | // 用户密码重置 66 | export function resetUserPwd(userId, password) { 67 | const data = { 68 | userId, 69 | password 70 | } 71 | return request({ 72 | url: '/system/user/resetPwd', 73 | method: 'put', 74 | data 75 | }) 76 | } 77 | 78 | // 用户状态修改 79 | export function changeUserStatus(userId, status) { 80 | const data = { 81 | userId, 82 | status 83 | } 84 | return request({ 85 | url: '/system/user/changeStatus', 86 | method: 'put', 87 | data 88 | }) 89 | } 90 | 91 | // 查询用户个人信息 92 | export function getUserProfile() { 93 | return request({ 94 | url: '/system/user/profile', 95 | method: 'get' 96 | }) 97 | } 98 | 99 | // 修改用户个人信息 100 | export function updateUserProfile(data) { 101 | return request({ 102 | url: '/system/user/profile', 103 | method: 'put', 104 | data 105 | }) 106 | } 107 | 108 | // 用户密码重置 109 | export function updateUserPwd(oldPassword, newPassword) { 110 | const data = { 111 | oldPassword, 112 | newPassword 113 | } 114 | return request({ 115 | url: '/system/user/profile/updatePwd', 116 | method: 'put', 117 | params: data 118 | }) 119 | } 120 | 121 | // 用户头像上传 122 | export function uploadAvatar(data) { 123 | return request({ 124 | url: '/system/user/profile/avatar', 125 | method: 'post', 126 | data 127 | }) 128 | } 129 | 130 | // 下载用户导入模板 131 | export function importTemplate() { 132 | return request({ 133 | url: '/system/user/importTemplate', 134 | method: 'get' 135 | }) 136 | } 137 | --------------------------------------------------------------------------------