├── src ├── i18n │ ├── en │ │ ├── user.js │ │ ├── crud.js │ │ ├── maResource.js │ │ ├── upload.js │ │ ├── skin.js │ │ ├── menus.js │ │ └── sys.js │ ├── zh_CN │ │ ├── crud.js │ │ ├── user.js │ │ ├── upload.js │ │ ├── maResource.js │ │ ├── skin.js │ │ ├── menus.js │ │ └── sys.js │ └── index.js ├── layout │ ├── empty.vue │ ├── components │ │ ├── components │ │ │ ├── search.vue │ │ │ ├── iframe-view.vue │ │ │ ├── sub-menu.vue │ │ │ ├── skin.vue │ │ │ ├── children-menu.vue │ │ │ ├── children-banner.vue │ │ │ └── message-notification.vue │ │ ├── classic │ │ │ ├── index.vue │ │ │ ├── ma-classic-header.vue │ │ │ └── ma-classic-slider.vue │ │ ├── columns │ │ │ ├── ma-columns-header.vue │ │ │ └── index.vue │ │ ├── ma-workerArea.vue │ │ ├── ma-buttonMenu.vue │ │ ├── ma-breadcrumb.vue │ │ ├── mixed │ │ │ └── top-menu.vue │ │ ├── banner │ │ │ └── index.vue │ │ └── ma-menu.vue │ ├── 404.vue │ └── index.vue ├── assets │ ├── logo.png │ ├── avatar.jpg │ ├── not-image.png │ ├── userBanner.jpg │ ├── skins-thumb │ │ ├── city │ │ │ └── thumb.jpg │ │ ├── mine │ │ │ └── thumb.jpg │ │ ├── classics │ │ │ └── thumb.jpg │ │ └── businessGray │ │ │ └── thumb.jpg │ └── image │ │ ├── login.svg │ │ ├── attach.svg │ │ ├── user.svg │ │ └── action.svg ├── views │ ├── dashboard │ │ ├── components │ │ │ ├── work-panel.vue │ │ │ ├── statistics.vue │ │ │ └── components │ │ │ │ ├── st-saiadmin.vue │ │ │ │ ├── st-welcome.vue │ │ │ │ ├── st-announced.vue │ │ │ │ └── st-count.vue │ │ ├── index.vue │ │ └── userCenter │ │ │ └── components │ │ │ ├── userInfomation.vue │ │ │ └── modifyPassword.vue │ ├── system │ │ ├── config │ │ │ └── components │ │ │ │ ├── js │ │ │ │ └── inputType.js │ │ │ │ ├── add-group.vue │ │ │ │ └── manage-config.vue │ │ ├── database │ │ │ └── struct.vue │ │ ├── post │ │ │ ├── view.vue │ │ │ └── edit.vue │ │ ├── logs │ │ │ ├── operLog.vue │ │ │ ├── loginLog.vue │ │ │ └── emailLog.vue │ │ ├── notice │ │ │ ├── index.vue │ │ │ └── edit.vue │ │ ├── dept │ │ │ └── leader.vue │ │ └── dict │ │ │ ├── edit.vue │ │ │ └── edit-data.vue │ └── tool │ │ ├── code │ │ ├── components │ │ │ └── preview.vue │ │ └── js │ │ │ └── vars.js │ │ ├── crontab │ │ ├── view.vue │ │ └── logList.vue │ │ └── install │ │ └── install-box.vue ├── style │ ├── index.css │ ├── skins │ │ ├── city │ │ │ └── background.jpg │ │ ├── mine │ │ │ └── index.less │ │ └── classics │ │ │ └── index.less │ ├── skin.less │ ├── dark.less │ └── animation.less ├── plugin │ └── index.js ├── directives │ ├── auth │ │ ├── auth.js │ │ └── index.js │ ├── role │ │ ├── role.js │ │ └── index.js │ ├── index.js │ └── copy │ │ └── index.js ├── api │ ├── system │ │ ├── monitor.js │ │ ├── attachment.js │ │ ├── emailLog.js │ │ ├── operLog.js │ │ ├── loginLog.js │ │ ├── menu.js │ │ ├── post.js │ │ ├── notice.js │ │ ├── database.js │ │ ├── dept.js │ │ ├── role.js │ │ ├── user.js │ │ ├── config.js │ │ └── dict.js │ ├── login.js │ └── tool │ │ ├── saipackage.js │ │ ├── crontab.js │ │ └── generate.js ├── store │ ├── modules │ │ ├── message.js │ │ ├── config.js │ │ ├── dict.js │ │ ├── iframe.js │ │ ├── keepAlive.js │ │ └── tag.js │ └── index.js ├── App.vue ├── config │ └── skins.js ├── router │ ├── webRouter.js │ ├── homePageRoutes.js │ └── index.js ├── components │ ├── sa-icon │ │ └── index.vue │ ├── sa-chart │ │ └── index.vue │ ├── sa-checkbox │ │ └── index.vue │ ├── sa-select │ │ └── index.vue │ ├── sa-radio │ │ └── index.vue │ ├── sa-switch │ │ └── index.vue │ ├── sa-dict │ │ └── index.vue │ ├── ma-colorPicker │ │ └── index.vue │ ├── sa-table │ │ ├── import.vue │ │ └── defaultOptions.js │ ├── sa-resource │ │ └── button.vue │ ├── index.js │ ├── ma-verifyCode │ │ └── index.vue │ ├── ma-codeEditor │ │ └── index.vue │ ├── sa-treeSlider │ │ └── index.vue │ └── sa-icon-picker │ │ └── index.vue └── main.js ├── public └── favicon.ico ├── .gitignore ├── postcss.config.cjs ├── .env ├── .env.production ├── .env.development ├── jsconfig.json ├── tailwind.config.cjs ├── README.md ├── LICENSE ├── tsconfig.json ├── .vscode └── settings.json ├── package.json └── vite.config.js /src/i18n/en/user.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/i18n/en/crud.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } -------------------------------------------------------------------------------- /src/i18n/zh_CN/crud.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } -------------------------------------------------------------------------------- /src/layout/empty.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/components/components/search.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/i18n/zh_CN/user.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: '菜单管理', 3 | 'system:cache': '系统缓存' 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .idea 7 | stats.html -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/views/dashboard/components/work-panel.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/assets/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/avatar.jpg -------------------------------------------------------------------------------- /src/assets/not-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/not-image.png -------------------------------------------------------------------------------- /src/assets/userBanner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/userBanner.jpg -------------------------------------------------------------------------------- /src/style/index.css: -------------------------------------------------------------------------------- 1 | /* ./src/index.css */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/style/skins/city/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/style/skins/city/background.jpg -------------------------------------------------------------------------------- /src/assets/skins-thumb/city/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/skins-thumb/city/thumb.jpg -------------------------------------------------------------------------------- /src/assets/skins-thumb/mine/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/skins-thumb/mine/thumb.jpg -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VITE_APP_TITLE = SaiAdmin 2 | VITE_APP_PORT = 8888 3 | VITE_APP_OPEN_PROXY = true 4 | VITE_APP_BASE = / 5 | VITE_APP_TOKEN_PREFIX = token -------------------------------------------------------------------------------- /src/assets/skins-thumb/classics/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/skins-thumb/classics/thumb.jpg -------------------------------------------------------------------------------- /src/assets/skins-thumb/businessGray/thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saithink/saiadmin-vue/HEAD/src/assets/skins-thumb/businessGray/thumb.jpg -------------------------------------------------------------------------------- /src/style/skin.less: -------------------------------------------------------------------------------- 1 | @import './skins/mine/index.less'; 2 | @import './skins/city/index.less'; 3 | @import './skins/classics/index.less'; 4 | @import './skins/businessGray/index.less'; -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # .env.production 2 | VITE_APP_ENV = production 3 | 4 | VITE_APP_BASE_URL = 5 | VITE_APP_WS_URL = ws://127.0.0.1:3131 6 | VITE_APP_WS_APPKEY = 2f97f2e18b6b6e6304ce77fdb779c650 7 | VITE_APP_PROXY_PREFIX = -------------------------------------------------------------------------------- /src/i18n/zh_CN/upload.js: -------------------------------------------------------------------------------- 1 | export default { 2 | fileHashFail: '获取文件Hash失败,请重试', 3 | sizeLimit: '文件大小超过了限制', 4 | uploadFailed: '文件上传失败', 5 | buttonText: '本地上传', 6 | clickUpload: '点击上传', 7 | uploadDesc: '将文件拖到此处,或', 8 | } -------------------------------------------------------------------------------- /src/i18n/zh_CN/maResource.js: -------------------------------------------------------------------------------- 1 | export default { 2 | loadingText: '数据加载中...', 3 | searchFileNotice: '文件名搜索', 4 | searchResource: '搜索资源类型', 5 | saveNetworkImage: '保存网络图片', 6 | networkImageNotice: '请粘贴网络图片地址', 7 | ok: '确定' 8 | } -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # .env.development 2 | VITE_APP_ENV = development 3 | 4 | VITE_APP_BASE_URL = http://127.0.0.1:8787 5 | VITE_APP_WS_URL = ws://127.0.0.1:3131 6 | VITE_APP_WS_APPKEY = 8c849eaf1e166c0a71d02fcec7c8df78 7 | VITE_APP_PROXY_PREFIX = /dev -------------------------------------------------------------------------------- /src/plugin/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | install: (Vue) => { 3 | const pluginList = import.meta.glob('./*/main.js') 4 | Object.keys(pluginList).forEach((path) => { 5 | pluginList[path]().then(plugin => Vue.use(plugin.default || plugin)) 6 | }) 7 | } 8 | } -------------------------------------------------------------------------------- /src/directives/auth/auth.js: -------------------------------------------------------------------------------- 1 | import { useUserStore } from '@/store' 2 | 3 | const auth = name => { 4 | const userStore = useUserStore() 5 | return (userStore.codes && userStore.codes.includes(name)) || (userStore.codes && userStore.codes.includes('*')) 6 | } 7 | 8 | export default auth -------------------------------------------------------------------------------- /src/directives/role/role.js: -------------------------------------------------------------------------------- 1 | import { useUserStore } from '@/store' 2 | 3 | const role = name => { 4 | const userStore = useUserStore() 5 | return (userStore.roles && userStore.roles.includes(name)) || (userStore.roles && userStore.roles.includes('superAdmin')) 6 | } 7 | 8 | export default role -------------------------------------------------------------------------------- /src/i18n/en/maResource.js: -------------------------------------------------------------------------------- 1 | export default { 2 | loadingText: 'Loading...', 3 | searchFileNotice: 'Search file by name', 4 | searchResource: 'Search resource type', 5 | saveNetworkImage: 'Save network image', 6 | networkImageNotice: 'Please paste the web picture address', 7 | ok: 'OK', 8 | } -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | import auth from './auth/index' 2 | import role from './role/index' 3 | import copy from './copy/index' 4 | 5 | 6 | export default { 7 | install (Vue) { 8 | Vue.directive('auth', auth) 9 | Vue.directive('role', role) 10 | Vue.directive('copy', copy) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/i18n/en/upload.js: -------------------------------------------------------------------------------- 1 | export default { 2 | fileHashFail: 'Get file hash failed, please try again!', 3 | sizeLimit: 'The file size exceeds the upload limit', 4 | uploadFailed: 'File upload failed', 5 | buttonText: 'Local upload', 6 | clickUpload: 'Click upload', 7 | uploadDesc: 'Drag the file here, or ', 8 | } -------------------------------------------------------------------------------- /src/api/system/monitor.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/utils/request.js' 2 | 3 | /** 4 | * 服务监控接口 5 | */ 6 | export default { 7 | /** 8 | * 获取服务器信息 9 | * @returns 10 | */ 11 | getServerInfo() { 12 | return request({ 13 | url: '/core/system/monitor', 14 | method: 'get' 15 | }) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/style/dark.less: -------------------------------------------------------------------------------- 1 | [arco-theme="dark"] { 2 | .menu-title { color: #efefef } 3 | .logo { span { color: #efefef } } 4 | 5 | .sys-search-container .results li .title, 6 | .sys-search-container .arco-icon 7 | { 8 | color: #efefef; 9 | } 10 | 11 | .sys-search-container .icon { 12 | fill: #efefef; 13 | } 14 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["src/*"], 6 | "@cps/*": ["src/components/*"], 7 | "vue-i18n": ["vue-i18n/dist/vue-i18n.cjs.js"] 8 | }, 9 | "jsx": "preserve" 10 | }, 11 | "exclude": ["node_modules", "dist", "build"] 12 | } 13 | -------------------------------------------------------------------------------- /src/i18n/zh_CN/skin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mine: 'Mine', 3 | classics: '经典', 4 | businessGray: '商务灰', 5 | city: '城市', 6 | 7 | mineDesc: '以纯净的白色为主,Mine默认皮肤', 8 | classicsDesc: '经典的深色侧边栏皮肤', 9 | businessGrayDesc: '灰色的百搭与大气,营造商务与稳重', 10 | cityDesc: '愿城市每一个角度,都有一份温馨', 11 | 12 | activated: '已激活', 13 | use: '使用' 14 | } -------------------------------------------------------------------------------- /src/views/system/config/components/js/inputType.js: -------------------------------------------------------------------------------- 1 | export const inputComponent = [ 2 | { label: '文本框', value: 'input' }, 3 | { label: '文本域', value: 'textarea' }, 4 | { label: '下拉选择框', value: 'select' }, 5 | { label: '单选框', value: 'radio' }, 6 | { label: '图片上传', value: 'uploadImage' }, 7 | { label: '文件上传', value: 'uploadFile' }, 8 | { label: '富文本编辑器', value: 'wangEditor' } 9 | ] 10 | -------------------------------------------------------------------------------- /src/i18n/en/skin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mine: 'Mine', 3 | classics: 'classics', 4 | businessGray: 'Business gray', 5 | city: 'City', 6 | 7 | mineDesc: 'Predominantly pure white, Mine defaults to skin', 8 | classicsDesc: 'Classic dark sidebar skin', 9 | businessGrayDesc: 'Gray versatility and atmosphere, creating business and stability', 10 | cityDesc: 'May there be a warmth in every angle of the city', 11 | 12 | activated: 'Activated', 13 | use: 'Use' 14 | } -------------------------------------------------------------------------------- /src/store/modules/message.js: -------------------------------------------------------------------------------- 1 | 2 | import { defineStore } from 'pinia' 3 | 4 | let defaultType = { 5 | messageList: [], 6 | } 7 | 8 | const useMessageStore = defineStore('message', { 9 | state: () => ({ ...defaultType }), 10 | 11 | getters: { 12 | getState() { 13 | return { ...this.$state } 14 | }, 15 | }, 16 | 17 | actions: { 18 | updateMessage(partial) { 19 | this.$patch(partial); 20 | }, 21 | }, 22 | }) 23 | 24 | export default useMessageStore -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /src/api/system/attachment.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/utils/request.js' 2 | 3 | export default { 4 | /** 5 | * 获取文件分页列表 6 | * @returns 7 | */ 8 | getPageList(params = {}) { 9 | return request({ 10 | url: '/core/attachment/index', 11 | method: 'get', 12 | params 13 | }) 14 | }, 15 | 16 | /** 17 | * 删除数据 18 | * @returns 19 | */ 20 | destroy(data) { 21 | return request({ 22 | url: '/core/attachment/destroy', 23 | method: 'delete', 24 | data 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/system/emailLog.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/utils/request.js' 2 | 3 | /** 4 | * 邮件日志接口 5 | */ 6 | export default { 7 | /** 8 | * 数据列表 9 | * @returns 10 | */ 11 | getPageList(params = {}) { 12 | return request({ 13 | url: '/core/email/index', 14 | method: 'get', 15 | params 16 | }) 17 | }, 18 | 19 | /** 20 | * 删除数据 21 | * @returns 22 | */ 23 | destroy(data) { 24 | return request({ 25 | url: '/core/email/destroy', 26 | method: 'delete', 27 | data 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/views/dashboard/components/statistics.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 17 | -------------------------------------------------------------------------------- /src/api/system/operLog.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/utils/request.js' 2 | 3 | /** 4 | * 操作日志接口 5 | */ 6 | export default { 7 | /** 8 | * 数据列表 9 | * @returns 10 | */ 11 | getPageList(params = {}) { 12 | return request({ 13 | url: '/core/logs/getOperLogPageList', 14 | method: 'get', 15 | params 16 | }) 17 | }, 18 | 19 | /** 20 | * 删除数据 21 | * @returns 22 | */ 23 | destroy(data) { 24 | return request({ 25 | url: '/core/logs/deleteOperLog', 26 | method: 'delete', 27 | data 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/config/skins.js: -------------------------------------------------------------------------------- 1 | import mine from '@/assets/skins-thumb/mine/thumb.jpg' 2 | import classics from '@/assets/skins-thumb/classics/thumb.jpg' 3 | import businessGray from '@/assets/skins-thumb/businessGray/thumb.jpg' 4 | import city from '@/assets/skins-thumb/city/thumb.jpg' 5 | 6 | export default [ 7 | { 8 | name: 'mine', 9 | thumb: mine 10 | }, 11 | { 12 | name: 'classics', 13 | thumb: classics 14 | }, 15 | { 16 | name: 'businessGray', 17 | thumb: businessGray 18 | }, 19 | { 20 | name: 'city', 21 | thumb: city 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /src/api/system/loginLog.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/utils/request.js' 2 | 3 | /** 4 | * 登录日志接口 5 | */ 6 | export default { 7 | /** 8 | * 数据列表 9 | * @returns 10 | */ 11 | getPageList(params = {}) { 12 | return request({ 13 | url: '/core/logs/getLoginLogPageList', 14 | method: 'get', 15 | params 16 | }) 17 | }, 18 | 19 | /** 20 | * 删除数据 21 | * @returns 22 | */ 23 | destroy(data) { 24 | return request({ 25 | url: '/core/logs/deleteLoginLog', 26 | method: 'delete', 27 | data 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/layout/404.vue: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | -------------------------------------------------------------------------------- /src/layout/components/classic/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | -------------------------------------------------------------------------------- /src/router/webRouter.js: -------------------------------------------------------------------------------- 1 | import homePageRoutes from './homePageRoutes' 2 | //系统路由 3 | const routes = [ 4 | { 5 | name: 'layout', 6 | path: '/', 7 | component: () => import('@/layout/index.vue'), 8 | redirect: 'dashboard', 9 | children: homePageRoutes 10 | }, 11 | { 12 | name: 'login', 13 | path: '/login', 14 | component: () => import('@/views/login.vue'), 15 | meta: { title: '登录' } 16 | }, 17 | { 18 | path: '/:pathMatch(.*)*', 19 | hidden: true, 20 | meta: { title: '访问的页面不存在' }, 21 | component: () => import('@/layout/404.vue') 22 | } 23 | ] 24 | 25 | export default routes 26 | -------------------------------------------------------------------------------- /src/store/modules/config.js: -------------------------------------------------------------------------------- 1 | 2 | import { defineStore } from 'pinia' 3 | 4 | let defaultConfig = { 5 | site_name: 'SaiAdmin', 6 | site_keywords: '', 7 | site_desc: '', 8 | site_record_number: '', 9 | site_copyright: '', 10 | site_storage_mode: '', 11 | web_close: '', 12 | } 13 | 14 | const useConfigStore = defineStore('config', { 15 | state: () => ({ ...defaultConfig }), 16 | 17 | getters: { 18 | appCurrentConfig() { 19 | return { ...this.$state } 20 | }, 21 | }, 22 | 23 | actions: { 24 | updateSettings(partial) { 25 | this.$patch(partial); 26 | }, 27 | }, 28 | }) 29 | 30 | export default useConfigStore -------------------------------------------------------------------------------- /src/store/modules/dict.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import commonApi from '@/api/common' 3 | 4 | // 定义字典store,名称是dict 5 | const useDictStore = defineStore('dict', { 6 | // 字典数据是数组,我们定义一个data来进行保存 7 | state: () => ({ data: undefined }), 8 | getters: { 9 | // 获取store状态 10 | getState() { 11 | return { ...this.$state } 12 | } 13 | }, 14 | actions: { 15 | //给字典数据赋值 16 | setInfo(data) { 17 | this.$patch(data) 18 | }, 19 | // 初始化字典数据 20 | async initData() { 21 | const { data } = await commonApi.dictAll() 22 | this.data = data 23 | } 24 | } 25 | }) 26 | 27 | export default useDictStore 28 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors') 2 | module.exports = { 3 | content: [ './src/**/*.{js,jxs,vue}' ], 4 | darkMode: 'class', // or 'media' or 'class' 5 | theme: { 6 | fontFamily: { 7 | sans: ['Graphik', 'sans-serif'], 8 | serif: ['Merriweather', 'serif'], 9 | }, 10 | extend: { 11 | spacing: { 12 | '128': '32rem', 13 | '144': '36rem', 14 | }, 15 | borderRadius: { 16 | '4xl': '2rem', 17 | } 18 | } 19 | }, 20 | variants: { 21 | extend: { 22 | borderColor: ['focus-visible'], 23 | opacity: ['disabled'], 24 | } 25 | }, 26 | plugins: [ 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /src/directives/auth/index.js: -------------------------------------------------------------------------------- 1 | import auth from './auth' 2 | 3 | const checkAuth = (el, binding) => { 4 | const { value } = binding 5 | 6 | if (Array.isArray(value)) { 7 | if (value.length > 0) { 8 | let isHas = false 9 | value.map(item => { 10 | isHas = auth(item) 11 | }) 12 | 13 | if (!isHas && el.parentNode) { 14 | el.parentNode.removeChild(el) 15 | } 16 | } 17 | } else { 18 | throw new Error(`need permission! Like v-auth="['admin','user']"`) 19 | } 20 | } 21 | 22 | export default { 23 | mounted(el, binding) { 24 | checkAuth(el, binding) 25 | }, 26 | updated(el, binding) { 27 | checkAuth(el, binding) 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /src/layout/components/columns/ma-columns-header.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /src/layout/components/classic/ma-classic-header.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /src/components/sa-icon/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | -------------------------------------------------------------------------------- /src/directives/copy/index.js: -------------------------------------------------------------------------------- 1 | import useClipboard from 'vue-clipboard3' 2 | import { Message } from '@arco-design/web-vue' 3 | 4 | const copy = (el, binding) => { 5 | const { value } = binding 6 | el.addEventListener('click', async () => { 7 | if (value && value !== '') { 8 | try { 9 | await useClipboard().toClipboard(value) 10 | Message.success('已成功复制到剪切板') 11 | } catch(e) { 12 | Message.error('复制失败') 13 | } 14 | } else { 15 | throw new Error(`need for copy content! Like v-copy="Hello World"`) 16 | } 17 | }) 18 | } 19 | 20 | export default { 21 | mounted(el, binding) { 22 | copy(el, binding) 23 | }, 24 | updated(el, binding) { 25 | copy(el, binding) 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /src/directives/role/index.js: -------------------------------------------------------------------------------- 1 | import role from './role' 2 | 3 | const checkRole = (el, binding) => { 4 | const { value } = binding 5 | 6 | if (Array.isArray(value)) { 7 | if (value.length > 0) { 8 | let isHas = false 9 | value.map((item) => { 10 | if (!isHas) { 11 | isHas = role(item) 12 | } 13 | }) 14 | 15 | if (!isHas && el.parentNode) { 16 | // el.parentNode.remove() 17 | el.remove() 18 | } 19 | } 20 | } else { 21 | throw new Error(`need role! Like v-role="['seo', 'cfo']"`) 22 | } 23 | } 24 | 25 | export default { 26 | mounted(el, binding) { 27 | checkRole(el, binding) 28 | }, 29 | updated(el, binding) { 30 | checkRole(el, binding) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | import useUserStore from './modules/user' 3 | import useAppStore from './modules/app' 4 | import useTagStore from './modules/tag' 5 | import useKeepAliveStore from './modules/keepAlive' 6 | import useIframeStore from './modules/iframe' 7 | import useConfigStore from './modules/config' 8 | import useMessageStore from './modules/message' 9 | import useDictStore from './modules/dict' 10 | import useTerminalStore from './modules/terminal' 11 | 12 | const pinia = createPinia() 13 | 14 | export { 15 | useUserStore, 16 | useAppStore, 17 | useTagStore, 18 | useKeepAliveStore, 19 | useIframeStore, 20 | useConfigStore, 21 | useMessageStore, 22 | useDictStore, 23 | useTerminalStore 24 | } 25 | export default pinia 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | 7 |

8 | 9 | ## 简介 10 | 11 | `Saidmin Vue` 5.x 12 | 13 | ## 安装使用 14 | 15 | - 获取代码 16 | 17 | ``` 18 | git clone https://github.com/saithink/saiadmin-vue.git 19 | ``` 20 | 21 | 或 22 | 23 | ``` 24 | git clone https://gitee.com/appsai/saiadmin-vue.git 25 | ``` 26 | 27 | 或 28 | 29 | ``` 30 | git clone https://gitcode.com/saigroup/saiadmin-vue.git 31 | ``` 32 | 33 | - 安装依赖 34 | 35 | ``` 36 | cd saiadmin-vue && yarn install 37 | ``` 38 | 39 | - 运行 40 | 41 | ``` 42 | yarn dev 43 | ``` 44 | 45 | - 打包 46 | 47 | ``` 48 | yarn build 49 | ``` 50 | -------------------------------------------------------------------------------- /src/components/sa-chart/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/store/modules/iframe.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | const useIframeStore = defineStore('iframe', { 4 | state: () => ({ 5 | iframes: [], 6 | name: null, 7 | show: true 8 | }), 9 | 10 | getters: { 11 | getState() { 12 | return { ...this.$state } 13 | }, 14 | }, 15 | 16 | actions: { 17 | 18 | addIframe (component) { 19 | if (! this.iframes.includes(component)) { 20 | this.iframes.push(component) 21 | } 22 | }, 23 | 24 | removeIframe (component) { 25 | const idx = this.iframes.indexOf(component) 26 | if (idx !== -1) { 27 | this.iframes.splice(idx, 1) 28 | } 29 | }, 30 | 31 | display () { this.show = true }, 32 | 33 | hidden () { this.show = false }, 34 | 35 | setName (name) { this.name = name }, 36 | 37 | clearIframe() { this.iframes = [] }, 38 | }, 39 | }) 40 | 41 | export default useIframeStore -------------------------------------------------------------------------------- /src/layout/components/components/iframe-view.vue: -------------------------------------------------------------------------------- 1 |