初始化

This commit is contained in:
kaiyun 2021-11-05 21:11:35 +08:00
parent 30af9210f1
commit a32bd96595
180 changed files with 15344 additions and 0 deletions

View File

@ -0,0 +1,2 @@
VITE_APP_PROJECT_TITLE = 'SmartAdmin'

View File

@ -0,0 +1,7 @@
VITE_APP_API_URL = 'http://localhost:10086/'
VITE_APP_PROJECT_TITLE = 'SmartAdmin Dev'
VITE_APP_PROFILE = 'dev'
VITE_APP_MODE = 'development'

View File

@ -0,0 +1,7 @@
VITE_APP_API_URL = 'http://127.0.0.1:10086/admin'
VITE_APP_PROJECT_TITLE = 'SmartAdmin Localhost'
VITE_APP_PROFILE = 'local'
VITE_APP_MODE = 'local'

View File

@ -0,0 +1,8 @@
VITE_APP_API_URL = 'http://localhost:10086/api'
VITE_APP_PROJECT_TITLE = 'SmartAdmin PRE'
VITE_APP_PROFILE = 'pre'
VITE_APP_MODE = 'development'

View File

@ -0,0 +1,6 @@
VITE_APP_API_URL = 'http://localhost:10086/api'
VITE_APP_PROFILE = 'prod'
VITE_APP_MODE = 'production'

View File

@ -0,0 +1,7 @@
VITE_APP_API_URL = 'http://sit-xmf-crm.renminyixue.cn/api/admin'
VITE_APP_PROJECT_TITLE = 'SmartAdmin Sit'
VITE_APP_PROFILE = 'sit'
VITE_APP_MODE = 'development'

View File

@ -0,0 +1,17 @@
*.sh
node_modules
lib
*.md
*.woff
*.ttf
.vscode
.idea
dist
public
/docs
.husky
.local
/bin
Dockerfile
src/assets

View File

@ -0,0 +1,76 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'plugin:vue/base'
],
globals: {
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly"
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
// we are only using this rule to check for unused arguments since TS
// catches unused variables but not args.
{ varsIgnorePattern: '.*', args: 'none' }
],
'no-unused-vars': [
'error',
// we are only using this rule to check for unused arguments since TS
// catches unused variables but not args.
{ varsIgnorePattern: '.*', args: 'none' }
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
// Enable vue/script-setup-uses-vars rule
'vue/script-setup-uses-vars': 'error',
}
};

View File

@ -0,0 +1,6 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.idea

View File

@ -0,0 +1,21 @@
module.exports = {
printWidth: 150,// 每行字符长度
tabWidth: 2,// 缩进空格数
useTabs: false,//不用tab缩进
semi: true,//// 在语句末尾打印分号
singleQuote: true,// 使用单引号而不是双引号
vueIndentScriptAndStyle: true,//Vue文件脚本和样式标签缩进
quoteProps: 'as-needed',// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
jsxSingleQuote: false,// 在JSX中使用单引号而不是双引号
trailingComma: 'es5',//多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>"默认none
bracketSpacing: true,// 在对象文字中的括号之间打印空格
jsxBracketSameLine: false,//jsx 标签的反尖括号需要换行
arrowParens: 'always',// 在单独的箭头函数参数周围包括括号 always(x) => x \ avoidx => x
rangeStart: 0,// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeEnd: Infinity,
requirePragma: false,// 指定要使用的解析器,不需要写文件开头的 @prettier
insertPragma: false,// 不需要自动在文件开头插入 @prettier
proseWrap: 'preserve',// 使用默认的折行标准 always\never\preserve
htmlWhitespaceSensitivity: 'css',// 指定HTML文件的全局空格敏感度 css\strict\ignore
endOfLine: 'lf',// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
};

View File

@ -0,0 +1,3 @@
/dist/*
/public/*
public/*

View File

@ -0,0 +1,70 @@
module.exports = {
root: true,
plugins: ['stylelint-order'],
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
rules: {
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global'],
},
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep'],
},
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin',
],
},
],
'no-empty-source': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
// 'declaration-block-trailing-semicolon': 'always',
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested'],
},
],
'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports',
},
{
type: 'at-rule',
name: 'media',
},
'rules',
],
{ severity: 'warning' },
],
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
};

View File

@ -0,0 +1,36 @@
__# smart-admin2
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@ -0,0 +1,24 @@
<!--
* @Description:
* @Author: zhuoda
* @Date: 2021-08-03
* @LastEditTime: 2021-09-01
* @LastEditors: zhuoda
-->
<!DOCTYPE html>
<html lang="en" id="htmlRoot">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="renderer" content="webkit"/>
<meta name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<title>SmartAdmin</title>
<link rel="icon" href="/favicon.ico"/>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
{
"version": "0.0.0",
"author": {
"name": "1024lab",
"email": "1024lab@sina.com",
"url": "https://1024lab.net"
},
"license": "MIT",
"homepage": "https://smartadmin.1024lab.net",
"scripts": {
"dev": "vite",
"sit": "vite build --base=/manage/ --mode sit",
"localhost": "vite --mode localhost",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview"
},
"dependencies": {
"ant-design-vue": "2.2.4",
"axios": "^0.21.1",
"clipboard": "^2.0.8",
"crypto-js": "^4.0.0",
"echarts": "^5.1.2",
"js-cookie": "^2.2.1",
"mitt": "^3.0.0",
"moment": "2.29.1",
"nprogress": "^0.2.0",
"pinia": "^2.0.0-beta.3",
"vue-enum": "~1.0.5",
"vue": "^3.2.1",
"vue-i18n": "9.1.6",
"vue-router": "^4.0.10"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^1.5.1",
"@vitejs/plugin-vue": "^1.4.0",
"@vue/compiler-sfc": "^3.2.1",
"dotenv": "^10.0.0",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.16.0",
"less": "^4.1.1",
"less-loader": "^10.0.1",
"postcss": "^8.3.5",
"prettier": "^2.3.1",
"rimraf": "^3.0.2",
"stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0",
"stylelint-order": "^4.1.0",
"v-viewer": "^1.5.1",
"vite": "^2.4.4",
"vite-plugin-mock": "^2.9.4",
"vite-plugin-style-import": "^1.1.1",
"vue-eslint-parser": "^7.10.0"
},
"engines": {
"node": ">=12"
},
"repository": {
"type": "git",
"url": "https://github.com/1024-lab/smart-admin.git"
}
}

View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {},
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,50 @@
<!--
* @Description: App
* @Author: zhuoda
* @Date: 2021-08-03
* @LastEditTime: 2021-08-28
* @LastEditors: zhuoda
-->
<template>
<a-config-provider :locale="zh_CN">
<!---全局loading常用于表单提交--->
<a-spin :spinning="spinning" size="large">
<RouterView />
</a-spin>
</a-config-provider>
</template>
<script setup lang="ts">
import zh_CN from "ant-design-vue/lib/locale-provider/zh_CN";
import { onMounted, computed } from "vue";
import { loginApi } from "./api/system/login/login";
import { useUserStore } from "./store/modules/system/user";
import { useAppConfigStore } from "/@/store/modules/system/app-config";
import { useSpinStore } from "/@/store/modules/system/spin";
let spinStore = useSpinStore();
const spinning = computed(() => spinStore.loading);
async function getLoginInfo() {
let token = useUserStore().getToken;
if (!token) return;
const res = await loginApi.getLogin();
useUserStore().setUserMenu(res.data);
}
// isMobile
let appConfigStore = useAppConfigStore();
function triggerReSize() {
appConfigStore.setCurrentScreenWidth(document.body.clientWidth);
}
onMounted(() => {
//
getLoginInfo();
//
triggerReSize();
});
window.onresize = () => {
triggerReSize();
};
</script>

View File

@ -0,0 +1,33 @@
/*
* @Description:
* @Author: zhuoda
* @Date: 2021-08-12 16:46:21
* @LastEditTime: 2021-08-12 16:47:04
* @LastEditors: zhuoda
*/
import { postRequest } from '/@/lib/axios';
export const categoryApi = {
// 添加类目 by zhuoda
addCategory: (param) => {
return postRequest('/category/add', param);
},
// GET
// 删除类目 by zhuoda
deleteCategoryById: (categoryId) => {
return getRequest(`/category/del/${categoryId}`);
},
// 查询类目层级树 by zhuoda
queryCategoryTree: (param) => {
return postRequest('/category/tree', param);
},
// 更新类目 by zhuoda
updateCategory: (param) => {
return postRequest('/category/update', param);
},
// 查询类目详情 by zhuoda
getCategory: (categoryId) => {
// POST /admin/clue/user/track/add
return getRequest(`/category/${categoryId}`);
},
};

View File

@ -0,0 +1,17 @@
/*
* @Description:
* @version:
* @Author: zhuoda
* @Date: 2021-08-17 23:32:36
* @LastEditors: zhuoda
* @LastEditTime: 2021-08-18 14:35:52
*/
import { postRequest } from '/@/lib/axios';
export const fileApi = {
// 文件上传 by zhuoda
uploadUrl: '/file/upload',
uploadFile: (param, folder) => {
return postRequest(`/file/upload?folder=${folder}`, param);
},
};

View File

@ -0,0 +1,23 @@
import { postRequest } from '/@/lib/axios';
export const goodsApi = {
// 添加商品 by zhuoda
addGoods: (param) => {
return postRequest('/goods/add', param);
},
// POST /admin/goods/del
// 删除 by zhuoda
deleteGoods: (param) => {
return postRequest('/goods/del', param);
},
// POST /admin/goods/query
// 分页查询 by zhuoda
queryGoodsList: (param) => {
return postRequest('/goods/query', param);
},
// POST /admin/goods/update
// 更新商品 by zhuoda
updateGoods: (param) => {
return postRequest('/goods/update', param);
}
};

View File

@ -0,0 +1,63 @@
/*
* @Author: zhuoda
* @Date: 2021-08-12 17:56:25
* @LastEditTime: 2021-08-16 10:45:05
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/department/department-api.ts
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const departmentApi = {
/**
* @description: 查询部门列表
* @param {*}
* @return {*}
*/
queryAllDepartment: () => {
return getRequest('/department/listAll');
},
/**
* @description: 查询部门树形列表
* @param {*}
* @return {*}
*/
departmentTree: () => {
return getRequest('/department/treeList');
},
/**
* @description: 获取校区列表 by zhuoda
* @param {*}
* @return {*}
*/
querySchoolDepartmentList: () => {
return getRequest('/department/querySchoolList');
},
/**
* @description: 添加部门 by zhuoda
* @param {*}
* @return {*}
*/
addDepartment: (param) => {
return postRequest('/department/add', param);
},
/**
* @description: 更新部门信息 by zhuoda
* @param {*}
* @return {*}
*/
updateDepartment: (param) => {
return postRequest('/department/update', param);
},
/**
* @description: 获取校区列表 by zhuoda
* @param {*}
* @return {*}
*/
deleteDepartment: (deptId) => {
return getRequest(`/department/delete/${deptId}`);
},
};

View File

@ -0,0 +1,96 @@
/*
* @Description: 员工api
* @Author: zhuoda
* @Date: 2021-08-12 18:00:56
* @LastEditTime: 2021-08-25 11:24:51
* @LastEditors: zhuoda
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const employeeApi = {
/**
* @description: 查询所有员工 by zhuoda
* @param {*}
* @return {*}
*/
queryAll: () => {
return getRequest('/employee/queryAll');
},
/**
* @description: 员工管理查询
* @param {*}
* @return {*}
*/
queryEmployee: (params) => {
return postRequest('/employee/query', params);
},
/**
* @description: 添加员工
* @param {EmployeeAddDto} params
* @return {*}
*/
addEmployee: (params) => {
return postRequest('/employee/add', params);
},
/**
* @description: 更新员工信息
* @param {EmployeeUpdateDto} params
* @return {*}
*/
updateEmployee: (params) => {
return postRequest('/employee/update', params);
},
/**
* @description: 删除员工
* @param {number} employeeId
* @return {*}
*/
deleteEmployee: (employeeId) => {
return getRequest(`/employee/delete/${employeeId}`);
},
/**
* @description: 批量删除员工
* @param {number} employeeIdList
* @return {*}
*/
batchDeleteEmployee: (employeeIdList) => {
return getRequest(`/employee/update/batch/delete?employeeIdList=${employeeIdList}`);
},
/**
* @description: 批量调整员工部门
* @param {EmployeeDepartmentUpdateDto} updateDto
* @return {*}
*/
batchUpdateDepartmentEmployee: (updateDto) => {
return postRequest('/employee/update/batch/department', updateDto);
},
/**
* @description: 重置员工密码
* @param {number} employeeId
* @return {*}
*/
resetPassword: (employeeId) => {
return getRequest(`employee/update/pwd/reset/${employeeId}`);
},
/**
* @description: 更新员工禁用状态
* @param {number} employeeId
* @return {*}
*/
updateDisabled: (employeeId) => {
return getRequest(`employee/update/disabled/${employeeId}`);
},
/**
* @description: 查询员工-根据校区id
* @param {number} deptId
* @return {*}
*/
querySchoolEmployee: (deptId) => {
return getRequest(`/employee/query/school/${deptId}`);
},
// 查询员工-根据部门id
queryEmployeeByDeptId: (deptId) => {
return getRequest(`/employee/query/dept/${deptId}`);
},
};

View File

@ -0,0 +1,28 @@
/*
* @Author: zhuoda
* @Date: 2021-08-24 17:21:35
* @LastEditTime: 2021-08-24 17:24:31
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/home/home-api.ts
*/
import { getRequest } from '/@/lib/axios';
export const homeApi = {
/**
* @description: 首页-金额统计业绩收款订单数等 by zhuoda
* @param {*}
* @return {*}
*/
homeAmountStatistics: () => {
return getRequest('/home/amount/statistics');
},
/**
* @description: 首页-待办信息 by zhuoda
* @param {*}
* @return {*}
*/
homeWaitHandle: () => {
return getRequest('home/wait/handle');
},
};

View File

@ -0,0 +1,27 @@
/*
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-08-18 20:04:05
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/login/login.ts
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const loginApi = {
/**
* 登录
* @param param
*/
login: (param) => {
return postRequest('/system/login', param);
},
/**
* 获取登录信息
* @param param
*/
getLogin: () => {
return getRequest('/system/login/get');
},
};

View File

@ -0,0 +1,53 @@
/*
* @Author: zhuoda
* @Date: 2021-08-11 22:15:04
* @LastEditTime: 2021-09-01 20:21:29
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/menu/menu-api.ts
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const menuApi = {
/**
* 添加菜单
*/
addMenu: (param) => {
return postRequest('/menu/add', param);
},
/**
* 更新菜单
*/
updateMenu: (pa) => {
return postRequest('/menu/update', param);
},
/**
* 批量删除菜单
*/
batchDeleteMenu: (menuIdList) => {
return getRequest(`/menu/batchDelete?menuIdList=${menuIdList}`);
},
/**
* 查询所有菜单列表
*/
queryMenu: () => {
return getRequest('/menu/query');
},
/**
* 查询菜单树
*/
queryMenuTree: (onlyMenu) => {
return getRequest(`/menu/tree?onlyMenu=${onlyMenu}`);
},
/**
* 获取所有请求路径
*/
getAllUrl: () => {
return getRequest('/menu/getAllUrl');
},
};

View File

@ -0,0 +1,27 @@
/*
* @Author: zhuoda
* @Date: 2021-08-28 14:16:46
* @LastEditTime: 2021-08-28 14:25:36
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/role-menu/role-menu-api.ts
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const roleMenuApi = {
/**
* @description: 获取角色关联菜单权限
* @param {*}
* @return {*}
*/
getRoleSelectedMenu: (roleId) => {
return getRequest(`role/menu/getRoleSelectedMenu/${roleId}`);
},
/**
* @description: 更新角色权限
* @param {*}
* @return {*}
*/
updateRoleMenu: (data) => {
return postRequest('role/menu/updateRoleMenu', data);
},
};

View File

@ -0,0 +1,109 @@
/*
* @Author: zhuoda
* @Date: 2021-08-16 15:53:46
* @LastEditTime: 2021-08-30 15:18:18
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/api/system/role/role-api.ts
*/
import { getRequest, postRequest } from '/@/lib/axios';
export const roleApi = {
/**
* @description: 获取所有角色
* @param {*}
* @return {*}
*/
queryAll: () => {
return getRequest('role/getAll');
},
/**
* @description:添加角色
* @param {*}
* @return {*}
*/
addRole: (data) => {
return postRequest('role/add', data);
},
/**
* @description:更新角色
* @param {*}
* @return {*}
*/
updateRole: (data) => {
return postRequest('role/update', data);
},
/**
* @description: 删除角色
* @param {number} roleId
* @return {*}
*/
deleteRole: (roleId) => {
return getRequest(`role/delete/${roleId}`);
},
/**
* @description: 批量设置某角色数据范围
* @param {DataScopeBatchSetRoleDto} data
* @return {*}
*/
updateDataScope: (data) => {
return postRequest('/dataScope/batchSet', data);
},
/**
* @description: 获取当前系统所配置的所有数据范围
* @param {*}
* @return {*}
*/
getDataScopeList: () => {
return getRequest('/dataScope/list');
},
/**
* @description: 获取某角色所设置的数据范围
* @param {number} roleId
* @return {*}
*/
getDataScopeByRoleId: (roleId) => {
return getRequest(`/dataScope/listByRole/${roleId}`);
},
/**
* @description: 获取角色成员-员工列表
* @param {*}
* @return {*}
*/
queryRoleEmployee: (params) => {
return postRequest('/role/listEmployee', params);
},
/**
* @description: 从角色成员列表中移除员工
* @param {number} employeeId
* @param {number} roleId
* @return {*}
*/
deleteEmployeeRole: (employeeId, roleId) => {
return getRequest('/role/removeEmployee?employeeId=' + employeeId + '&roleId=' + roleId);
},
/**
* @description: 从角色成员列表中批量移除员工
* @param {RoleEmployeeBatchDto} data
* @return {*}
*/
deleteEmployeeList: (data) => {
return postRequest('/role/removeEmployeeList', data);
},
/**
* @description: 根据角色id获取角色员工列表(无分页)
* @param {*}
* @return {*}
*/
getRoleAllEmployee: (roleId) => {
return getRequest(`role/listAllEmployee/${roleId}`);
},
/**
* @description: 角色成员列表中批量添加员工
* @param {RoleEmployeeBatchDto} data
* @return {*}
*/
addRoleEmployeeList: (data) => {
return postRequest('/role/addEmployeeList', data);
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,70 @@
<template>
<h1>{{ msg }}</h1>
<p>
Recommended IDE setup:
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
+
<a
href="https://marketplace.visualstudio.com/items?itemName=octref.vetur"
target="_blank"
>
Vetur
</a>
or
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
(if using
<code>&lt;script setup&gt;</code>)
</p>
<p>See <code>README.md</code> for more information.</p>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Docs
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
</p>
<button type="button" @click="count++">count is: {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
name: 'HelloWorld',
props: {
msg: {
type: String,
required: true
}
},
setup: () => {
const count = ref(0)
return { count }
}
})
</script>
<style scoped>
a {
color: #42b983;
}
label {
margin: 0 0.5em;
font-weight: bold;
}
code {
background-color: #eee;
padding: 2px 4px;
border-radius: 4px;
color: #304455;
}
</style>

View File

@ -0,0 +1,90 @@
<!--
* @Description:
* @Author: zhuoda
* @Date: 2021-08-12
* @LastEditTime: 2021-08-18
* @LastEditors: zhuoda
-->
<template>
<a-tree-select
v-model:value="selectValue"
:style="`width:${width}`"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="categoryTree"
:placeholder="placeholder"
tree-default-expand-all
@change="handleChange"
/>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { categoryApi } from "@/api/business/category/category-api";
// ========================
interface CategoryTreeSelectProps {
placeholder?: string;
value?: string | number;
categoryType?: number; // CATEGORY_TYPE_ENUM
width: string;
}
const props = withDefaults(defineProps<CategoryTreeSelectProps>(), {
value: undefined,
placeholder: "请选择",
categoryType: undefined,
width: "100%",
});
const emit = defineEmits<{
(e: "update:value", value);
(e: "change", value);
}>();
const categoryTree = ref<[]>([]);
// ========================
async function queryCategoryTree() {
if (!props.categoryType) {
categoryTree.value = [];
return;
}
try {
let param = {
categoryType: props.categoryType,
};
let resp = await categoryApi.queryCategoryTree(
param
);
categoryTree.value = resp.data;
} catch (e) {
console.log(e);
} finally {
console.log(1);
}
}
const selectValue = ref(props.value);
// value
watch(
() => props.value,
(newValue) => {
selectValue.value = newValue;
}
);
//
watch(
() => props.categoryType,
() => {
queryCategoryTree();
}
);
function handleChange(value: any): void {
emit("update:value", value);
emit("change", value);
}
onMounted(queryCategoryTree);
</script>

View File

@ -0,0 +1,70 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-10 16:53:06
* @LastEditTime: 2021-08-16 15:59:21
* @LastEditors: zhuoda
* @Description: 部门树下拉选择
* @FilePath: /smart-admin/src/views/system/employee/department/components/department-tree-select/index.vue
-->
<template>
<a-tree-select
:value="props.value"
:treeData="treeData"
:replaceFields="{ title: 'name', key: 'id', value: 'id' }"
show-search
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择部门"
allow-clear
tree-default-expand-all
:multiple="props.multiple"
@change="treeSelectChange"
/>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import _ from 'lodash';
import { departmentApi } from '/@/api/system/department/department-api';
// ----------------------- emits props ------------------------
interface Props {
//
value?: number | number[];
//
multiple?: boolean;
//
init?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
value: undefined,
multiple: false,
init: true,
});
const emit = defineEmits<{
(e: 'update:value', value: number | number[]);
}>();
let treeData = ref([]);
// ----------------------- watch ------------------------
// ----------------------- ------------------------
onMounted(() => {
if (props.init) {
queryDepartmentTree();
}
});
// ----------------------- ------------------------
//
async function queryDepartmentTree() {
let res = await departmentApi.departmentTree();
treeData.value = res.data;
}
function treeSelectChange(e: number | number[]) {
emit('update:value', e);
}
// ----------------------- ------------------------
defineExpose({
queryDepartmentTree,
});
</script>

View File

@ -0,0 +1,78 @@
<!--
* @Description:
* @Author: zhuoda
* @Date: 2021-08-12 18:23:56
* @LastEditTime: 2021-08-18
* @LastEditors: zhuoda
-->
<template>
<a-select
v-model:value="selectValue"
:style="`width: ${width}`"
:placeholder="props.placeholder"
:showSearch="true"
:allowClear="true"
:size="size"
@change="handleChange"
@deselect="handleChange"
>
<a-select-option v-for="item in employeeList" :key="item.id" :value="item.id">
{{ item.actualName }}{{ item.departmentName }}
</a-select-option>
</a-select>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { employeeApi } from "/@/api/system/employee/employee-api";
// =========== =============
interface EmployeeSelectProps {
placeholder?: string;
value?: number;
width: string;
size?: string;
}
const props = withDefaults(defineProps<EmployeeSelectProps>(), {
value: undefined,
placeholder: "请选择",
width: "100%",
size: "default",
});
const emit = defineEmits<{
(e: "update:value", value: any): void;
(e: "change", value: any): void;
}>();
// =========== =============
//
const employeeList = ref([]);
async function query() {
try {
let resp = await employeeApi.queryAll();
employeeList.value = resp.data;
} catch (e) {
console.log(e);
} finally {
console.log(1);
}
}
onMounted(query);
// value
const selectValue = ref(props.value);
watch(
() => props.value,
(newValue) => {
selectValue.value = newValue;
}
);
function handleChange(value: any): void {
emit("update:value", value);
emit("change", value);
}
</script>

View File

@ -0,0 +1,31 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-08-25 21:20:10
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/menu-location-breadcrumb/index.vue
-->
<template>
<a-breadcrumb separator=">" style="display: inline">
<a-breadcrumb-item v-for="(item, index) in parentMenuList" :key="index">{{ item.title }}</a-breadcrumb-item>
<a-breadcrumb-item>{{ currentRoute.meta.title }}</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script setup lang="ts">
import { computed } from '@vue/reactivity';
import { useRoute } from 'vue-router';
// ----------------------- emits props ---------------------
let currentRoute = useRoute();
// ----------------------- watch ------------------------
const parentMenuList = computed(() => {
return currentRoute.meta.parentMenuList || [];
});
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
// ----------------------- ----------------------------
defineExpose({});
</script>
<style scoped lang="less"></style>

View File

@ -0,0 +1,115 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-17 19:09:40
* @LastEditTime: 2021-08-27
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/recursion-menu/index.vue
-->
<template>
<a-menu
:selectedKeys="selectedKeys"
:openKeys="openKeys"
mode="inline"
theme="dark"
:inline-collapsed="collapsed"
>
<template v-for="item in menuTree" :key="item.name">
<template v-if="!item.meta.hideInMenu">
<template v-if="$lodash.isEmpty(item.children)">
<a-menu-item :key="item.name" @click="turnToPage(item)">
<template #icon>
<component :is="$antIcons[item.meta.icon]" />
</template>
{{ item.meta.title }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.name" @turnToPage="turnToPage" />
</template>
</template>
</template>
</a-menu>
</template>
<script lang="ts">
import { defineComponent, ref, computed } from "vue";
import SubMenu from "./sub-menu.vue";
import { router } from "/@/router/index";
import { MENU_TYPE_ENUM } from "/@/constants/system/menu/menu-enum";
import { RouteRecord, useRoute } from "vue-router";
export default defineComponent({
name: "RecursionMenu",
components: {
SubMenu,
},
props: {
menuList: {
type: Array,
default: () => {
return [];
},
},
collapsed: {
type: Boolean,
default: false,
},
},
setup(prop, context) {
const collapsed = ref<boolean>(false);
let currentRoute = useRoute();
const toggleCollapsed = () => {
collapsed.value = !collapsed.value;
};
//
let routes = router.getRoutes();
const menuTree = computed(() => {
return routes.filter((e) => e.meta.menuType == MENU_TYPE_ENUM.CATALOG.value);
});
const selectedKeys = computed(() => {
return [currentRoute.name];
});
const openKeys = computed(() => {
return (currentRoute.meta.parentMenuList || []).map(
(e: Record<string, string>) => e.name
);
});
//
const turnToPage = (route: RouteRecord | string) => {
console.log(route);
let { name, params, query } = {};
if (typeof route === "string") {
name = route;
} else {
name = route.name;
params = route.params;
query = route.query;
}
if (name.indexOf("isTurnByHref_") > -1) {
window.open(name.split("_")[1]);
return;
}
router.push({
name,
params,
query,
});
};
return {
selectedKeys,
openKeys,
turnToPage,
toggleCollapsed,
menuTree,
};
},
components: {
SubMenu,
},
});
</script>

View File

@ -0,0 +1,56 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-08-17 16:57:59
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/recursion-menu/sub-menu.vue
-->
<template>
<a-sub-menu :key="menuInfo.name">
<template #icon>
<component :is="$antIcons[menuInfo.meta.icon]"/>
</template>
<template #title>{{ menuInfo.meta.title }}</template>
<template v-for="item in menuInfo.children" :key="item.name">
<template v-if="!item.meta.hideInMenu">
<template v-if="!item.children">
<a-menu-item :key="item.name" @click="turnToPage(item)">
<template #icon>
<component :is="$antIcons[item.meta.icon]"/>
</template>
{{ item.meta.title }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.name" @turnToPage="turnToPage"/>
</template>
</template>
</template>
</a-sub-menu>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import {RouteRecord} from "vue-router";
export default defineComponent({
name: "SubMenu",
components: {},
props: {
menuInfo: {
type: Object,
default: () => ({})
}
},
setup(prop,context) {
const turnToPage = (route:RouteRecord | string) => {
context.emit('turnToPage',route)
}
return {
turnToPage
}
}
});
</script>

View File

@ -0,0 +1,92 @@
<!--
* @Description: 校区部门选择
* @Author: zhuoda
* @Date: 2021-08-12 18:23:56
* @LastEditTime: 2021-08-25 11:48:26
* @LastEditors: Please set LastEditors
-->
<template>
<a-select
v-model:value="selectValue"
:style="`width: ${width}`"
:placeholder="props.placeholder"
:showSearch="true"
:allowClear="true"
:size="size"
@change="handleChange"
@deselect="handleChange"
>
<a-select-option v-for="item in list" :key="item.id" :value="item.id">
{{ item.name }}
</a-select-option>
</a-select>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { departmentApi } from "/@/api/system/department/department-api";
// =========== =============
interface ShoolDepartmentSelectProps {
placeholder?: string;
value?: number;
width: string;
size?: string;
showFirst?:boolean;
}
const props = withDefaults(defineProps<ShoolDepartmentSelectProps>(), {
value: undefined,
placeholder: "请选择",
width: "100%",
size: "default",
showFirst:false,
});
const emit = defineEmits<{
(e: "update:value", value);
(e: "change", value);
}>();
// =========== =============
//
const list = ref([]);
async function query() {
try {
let resp = await departmentApi.querySchoolDepartmentList();
list.value = resp.data;
if(props.showFirst){
showFirst()
}
} catch (e) {
console.log(e);
} finally {
console.log(1);
}
}
onMounted(query);
// value
const selectValue = ref(props.value);
watch(
() => props.value,
(newValue) => {
selectValue.value = newValue;
}
);
function handleChange(value): void {
emit("update:value", value);
emit("change", value);
}
//
function showFirst(){
if(list.value && list.value.length > 0){
selectValue.value = list.value[0].id;
handleChange(selectValue.value )
}
}
</script>

View File

@ -0,0 +1,175 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-30 14:38:05
* @LastEditTime: 2021-08-30 15:14:06
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/select-employee-modal/index.vue
-->
<template>
<a-modal v-model:visible="visible" :width="900" title="选择人员" @cancel="closeModal" @ok="selectData">
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-form-item label="关键字" class="smart-query-form-item">
<a-input style="width: 150px" v-model:value="params.keyword" placeholder="商品名称" />
</a-form-item>
<a-form-item label="部门" class="smart-query-form-item">
<DepartmentTreeSelect style="width: 150px" ref="departmentTreeSelect" v-model:value="params.departmentId" />
</a-form-item>
<a-form-item label="状态" class="smart-query-form-item">
<a-select style="width: 150px" v-model:value="params.disabledFlag" placeholder="请选择状态" allowClear>
<a-select-option :key="1"> 禁用 </a-select-option>
<a-select-option :key="0"> 启用 </a-select-option>
</a-select>
</a-form-item>
<a-form-item class="smart-query-form-item smart-margin-left10">
<a-button type="primary" @click="queryEmployee">
<template #icon>
<ReloadOutlined />
</template>
查询
</a-button>
<a-button @click="reset" class="smart-margin-left10">
<template #icon>
<SearchOutlined />
</template>
重置
</a-button>
</a-form-item>
</a-row>
</a-form>
<a-table
:row-selection="{ selectedRowKeys: selectedRowKeyList, onChange: onSelectChange }"
:loading="tableLoading"
size="small"
:columns="columns"
:data-source="tableData"
:pagination="false"
rowKey="id"
:scroll="{ y: 300 }"
>
<template #disabledFlag="{ text }">
<span>{{ text ? '禁用' : '启用' }}</span>
</template>
<template #gender="{ text }">
<span>{{ $smartEnumPlugin.getDescByValue('GENDER_ENUM', text) }}</span>
</template>
</a-table>
<div class="smart-query-table-page">
<a-pagination
showSizeChanger
showQuickJumper
show-less-items
:pageSizeOptions="PAGE_SIZE_OPTIONS"
:defaultPageSize="params.pageSize"
v-model:current="params.pageNum"
v-model:pageSize="params.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="(total) => `共${total}条`"
/>
</div>
</a-modal>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import { employeeApi } from '/@/api/system/employee/employee-api';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common';
import DepartmentTreeSelect from '/@/components/department-tree-select/index.vue';
import { message } from 'ant-design-vue';
// ----------------------- emits props ---------------------
const emits = defineEmits<{
(e: 'selectData', value);
}>();
const visible = ref(false);
const tableLoading = ref<boolean>(false);
const departmentTreeSelect = ref();
//
const columns = [
{
title: '姓名',
dataIndex: 'actualName',
},
{
title: '手机号',
dataIndex: 'phone',
},
{
title: '性别',
dataIndex: 'gender',
slots: { customRender: 'gender' },
},
{
title: '登录账号',
dataIndex: 'loginName',
},
{
title: '状态',
dataIndex: 'disabledFlag',
slots: { customRender: 'disabledFlag' },
},
];
const tableData = ref([]);
let defaultParams = {
departmentId: undefined,
disabledFlag: undefined,
employeeIdList: undefined,
keyword: undefined,
searchCount: undefined,
pageNum: 1,
pageSize: PAGE_SIZE,
sortItemList: undefined,
};
const params = reactive({ ...defaultParams });
const total = ref<number>();
let selectedRowKeyList = ref<number[]>([]);
// ----------------------- watch ------------------------
const hasSelected = computed(() => selectedRowKeyList.value.length > 0);
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
async function showModal(selectEmployeeId) {
selectedRowKeyList.value = selectEmployeeId || [];
visible.value = true;
queryEmployee();
}
function reset() {
Object.assign(params, defaultParams);
queryEmployee();
}
async function queryEmployee() {
tableLoading.value = true;
try {
let res = await employeeApi.queryEmployee(params);
tableData.value = res.data.list;
total.value = res.data.total;
} catch (error) {
console.error(error);
} finally {
tableLoading.value = false;
}
}
function onSelectChange(selectedRowKeys: number[]) {
selectedRowKeyList.value = selectedRowKeys;
}
function closeModal() {
Object.assign(params, defaultParams);
selectedRowKeyList.value = [];
visible.value = false;
}
function selectData() {
if (!hasSelected.value) {
message.warning('请选择角色人员');
return;
}
let selectVoList = tableData.value.filter((e) => selectedRowKeyList.value.includes(e.id));
emits('selectData', selectVoList);
closeModal();
}
// ----------------------- ----------------------------
defineExpose({
showModal,
});
</script>
<style scoped lang="less"></style>

View File

@ -0,0 +1,60 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-25 17:07:41
* @LastEditTime: 2021-08-28 16:46:25
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/side-expand/side-menu/index.vue
-->
<template>
<div class="menu-container">
<!-- logo 一级导航 -->
<TopMenu ref="topMenu" class="topMenu" :menuTree="menuTree" />
<!-- 次级导航 -->
<RecursionMenu v-if="showRecursionMenu" class="recursionMenu" :selectedMenu="selectedMenu" />
</div>
</template>
<script setup lang="ts">
import TopMenu from './top-menu.vue';
import RecursionMenu from './recursion-menu.vue';
import { useUserStore } from '/@/store/modules/system/user';
import { computed } from '@vue/reactivity';
import { ref } from 'vue';
// ----------------------- emits props ---------------------
defineProps<{
value;
}>();
defineEmits<{
(e: 'update:value');
}>();
const topMenu = ref();
// ----------------------- watch ------------------------
const menuTree = computed(() => useUserStore().getMenuTree || []);
const selectedMenu = computed(() => {
if (topMenu.value) {
return topMenu.value.selectedMenu;
}
return {};
});
const showRecursionMenu = computed(() => selectedMenu.value && selectedMenu.value.children && selectedMenu.value.children.some((e) => e.visibleFlag));
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
// ----------------------- ----------------------------
defineExpose({});
</script>
<style scoped lang="less">
.menu-container {
display: flex;
height: 100%;
.topMenu {
width: 114px;
flex-shrink: 0;
}
.recursionMenu {
min-width: 126px;
flex: 1;
}
}
</style>

View File

@ -0,0 +1,86 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-25 17:52:43
* @LastEditTime: 2021-08-27
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/side-expand/side-menu/recursion-menu.vue
-->
<template>
<div class="resursion-container">
<!-- 顶部顶级菜单名称 -->
<div class="top-menu">
<span class="ant-menu">{{ props.selectedMenu?.menuName }}</span>
</div>
<!-- 次级菜单展示 -->
<a-menu :selectedKeys="selectedKeys" :openKeys="openKeys" mode="inline">
<template v-for="item in props.selectedMenu?.children" :key="item.menuId">
<template v-if="item.visibleFlag">
<template v-if="$lodash.isEmpty(item.children)">
<a-menu-item :key="item.menuId.toString()" @click="turnToPage(item)">
<template #icon v-if="item.icon">
<component :is="$antIcons[item.icon]" />
</template>
{{ item.menuName }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.menuId" @turnToPage="turnToPage" />
</template>
</template>
</template>
</a-menu>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useRoute } from "vue-router";
import { router } from "/@/router";
import SubMenu from "./sub-menu.vue";
// ----------------------- emits props ---------------------
let props = defineProps<{
selectedMenu?: any;
}>();
defineEmits<{
(e: "update:value"): void;
}>();
let currentRoute = useRoute();
// ----------------------- watch ------------------------
const selectedKeys = computed(() => {
return [currentRoute.name];
});
const openKeys = computed(() => {
return (currentRoute.meta.parentMenuList || []).map(
(e: Record<string, string>) => e.name
);
});
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
//
function turnToPage(route) {
router.push({ name: route.menuId.toString() });
}
// ----------------------- ----------------------------
defineExpose({});
</script>
<style scoped lang="less">
.resursion-container {
height: 100%;
background: #ffffff;
}
.top-menu {
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
height: @header-user-height;
font-size: 16px;
color: #515a6e;
border-bottom: 1px solid #f3f3f3;
border-right: 1px solid #f3f3f3;
}
</style>

View File

@ -0,0 +1,55 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-08-26
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/side-expand/side-menu/sub-menu.vue
-->
<template>
<a-sub-menu :key="props.menuInfo?.menuId">
<template #icon>
<component :is="$antIcons[props.menuInfo?.icon]" />
</template>
<template #title>{{ props.menuInfo?.menuName }}</template>
<template v-for="item in props.menuInfo?.children" :key="item.menuId">
<template v-if="item.visibleFlag">
<template v-if="!item.children">
<a-menu-item :key="item.menuId" @click="turnToPage(item)">
<template #icon>
<component :is="$antIcons[item.icon]" />
</template>
{{ item.menuName }}
</a-menu-item>
</template>
<template v-else>
<sub-menu :menu-info="item" :key="item.menuId" @turnToPage="turnToPage" />
</template>
</template>
</template>
</a-sub-menu>
</template>
<script setup lang="ts">
// ----------------------- emits props ---------------------
let props = defineProps<{
menuInfo:any;
}>();
let emits = defineEmits<{
(e: "turnToPage", value);
}>();
// ----------------------- watch ------------------------
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
const turnToPage = (route) => {
emits("turnToPage", route);
};
// ----------------------- ----------------------------
defineExpose({});
</script>
<style scoped lang="less">
::v-deep(.ant-menu-item-selected) {
border-right: 3px !important;
}
</style>

View File

@ -0,0 +1,112 @@
<!--
* @Author: zhuoda
* @Date: 2021-08-25 17:09:44
* @LastEditTime: 2021-09-01
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/components/side-expand/side-menu/top-menu.vue
-->
<template>
<div class="top-menu-container">
<!-- 顶部logo区域 -->
<div class="logo">
<h3 style="color: white">SmartAdmin</h3>
</div>
<!-- 一级菜单展示 -->
<a-menu
:selectedKeys="selectedKeys"
mode="inline"
theme="dark"
:inline-collapsed="collapsed"
>
<template v-for="item in props.menuTree" :key="item.menuId">
<template v-if="item.visibleFlag">
<a-menu-item :key="item.menuId.toString()" @click="selectMenu(item)">
<template #icon>
<component :is="$antIcons[item.icon]" />
</template>
{{ item.menuName }}
</a-menu-item>
</template>
</template>
</a-menu>
</div>
</template>
<script setup lang="ts">
import _ from "lodash";
import { computed, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { appDefaultConfig } from "/@/config/app-config";
import { MENU_TYPE_ENUM } from "/@/constants/system/menu/menu-enum";
import { router } from "/@/router";
// ----------------------- emits props ---------------------
let props = defineProps<{
menuTree:any;
}>();
const collapsed = ref<boolean>(false);
const selectedMenu = ref();
let currentRoute = useRoute();
// ----------------------- watch ------------------------
const selectedKeys = computed(() => {
if (selectedMenu.value) {
return [selectedMenu.value.menuId.toString()];
}
return (currentRoute.meta.parentMenuList || []).map(
(e: Record<string, string>) => e.name
);
});
watch(
currentRoute,
() => {
selectedMenu.value = undefined;
let menuList = props.menuTree?.map((e) => e.menuId.toString());
let parentIdList = _.intersection(menuList, <string[]>selectedKeys.value);
if (parentIdList.length > 0) {
let parentId = parentIdList[0];
let parentItem = props.menuTree?.find((e) => e.menuId == Number(parentId));
selectedMenu.value = parentItem;
}
},
{
immediate: true,
}
);
// ----------------------- ---------------------------------
// ----------------------- ------------------------------------
//
function selectMenu(route) {
selectedMenu.value = route;
if (
route.menuType == MENU_TYPE_ENUM.MENU.value &&
(_.isEmpty(route.children) || route.children?.every((e) => !e.visibleFlag))
) {
router.push({ name: route.menuId.toString() });
}
}
function goHome() {
router.push({ name: appDefaultConfig.homePageName });
}
// ----------------------- ----------------------------
defineExpose({
selectedMenu,
});
</script>
<style scoped lang="less">
.top-menu-container {
height: 100%;
background: #001529;
}
.logo {
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
height: 67px;
img {
width: 83px;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,38 @@
.shadow{
box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
}
.side-menu{
min-height: 100vh;
overflow-y: auto;
z-index: 10;
.logo{
height: @header-user-height;
position: relative;
line-height: @header-user-height;
padding-left: 24px;
-webkit-transition: all .3s;
transition: all .3s;
overflow: hidden;
background-color: @layout-trigger-background;
&.light{
background-color: #fff;
h1{
color: @primary-color;
}
}
h1{
color: @menu-dark-highlight-color;
font-size: 20px;
margin: 0 0 0 12px;
display: inline-block;
vertical-align: middle;
}
img{
width: 32px;
vertical-align: middle;
}
}
}
.menu{
padding: 16px 0;
}

View File

@ -0,0 +1,69 @@
<template>
<!--
左侧菜单分为两部分
1顶部logo区域包含 logo和名称
2下方菜单区域
-->
<!-- 顶部logo区域 -->
<div :class="['logo', theme]">
<img src="/@/assets/img/logo.png"/>
<h1>{{ projectName }}</h1>
</div>
<!-- 下方菜单区域 这里使用一个递归菜单解决 -->
<recursion-menu :collapsed="collapsed" />
</template>
<script lang="ts">
import RecursionMenu from "/@/components/recursion-menu/index.vue";
import {computed, defineComponent} from "vue";
import {useAppConfigStore} from "/@/store/modules/system/app-config";
import {useProjectConfigStore} from "/@/store/modules/system/project-config";
export default defineComponent({
name: "SideMenu",
components: {RecursionMenu},
props: {
collapsible: {
type: Boolean,
required: false,
default: false
},
collapsed: {
type: Boolean,
required: false,
default: false
},
menuData: {
type: Array,
required: true
},
theme: {
type: String,
required: false,
default: "dark"
}
},
setup(props, {emit}) {
let appConfigStore = useAppConfigStore();
let projectConfigStore = useProjectConfigStore();
const onSelect = (obj) => {
emit("menuSelect", obj);
};
return {
onSelect,
isMobile: computed(() => appConfigStore.isMobile),
projectName: computed(() => projectConfigStore.projectName)
};
}
});
</script>
<style lang="less" scoped>
@import "index";
</style>

View File

@ -0,0 +1,87 @@
<!--
* @Description: 地区级联选择
* @Author: zhuoda
* @Date: 2021-08-17
* @LastEditTime: 2021-08-18
* @LastEditors: zhuoda
-->
<template>
<a-cascader
:style="`width:${width}`"
v-model:value="areaValue"
:show-search="{ filter }"
:options="areaOptionData"
:placeholder="placeholder"
:size="size"
@change="handleChange"
/>
</template>
<script lang="ts" setup>
import { PROVINCE_CITY_DISTRICT } from "./province-city-district";
import { PROVINCE_CITY } from "./province-city";
import { ref, toRaw, watch } from "vue";
// ============ ============
const TYPE_PROVINCE_CITY_DISTRICT = "province_city_district";
const TYPE_PROVINCE_CITY = "province_city";
import { SmartAreaOption } from "./smart-area-option";
interface SmartAreaCascaderProps {
//
type: string;
value: SmartAreaOption[];
width: string;
size?: string;
placeholder?: string;
}
const props = withDefaults(defineProps<SmartAreaCascaderProps>(), {
size: "default",
value: undefined,
width: "200px",
placeholder: "请选择地区",
});
const emit = defineEmits<{
(e: "update:value", value);
(e: "change", value, selectedOptions);
}>();
// ============ ============
const areaOptionData =
props.type === TYPE_PROVINCE_CITY_DISTRICT ? PROVINCE_CITY_DISTRICT : PROVINCE_CITY;
//
const areaValue = ref<number[]>([]);
// value
watch(
() => props.value,
(newValue) => {
if (newValue) {
let array = [];
for (let index = 0; index < 3; index++) {
if (newValue[index]) {
array.push(newValue[index].value);
}
}
areaValue.value = array;
} else {
areaValue.value = [];
}
}
);
function handleChange(value: number[], selectedOptions: SmartAreaOption[]): void {
emit("update:value", toRaw(selectedOptions));
emit("change", value, toRaw(selectedOptions));
}
const filter = (inputValue: string, path: SmartAreaOption[]) => {
return path.some(
(option) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
);
};
</script>

View File

@ -0,0 +1,73 @@
<template>
<a-select
v-model:value="selectValue"
:style="`width: ${width}px`"
:placeholder="placeholder"
:showSearch="true"
:allowClear="true"
:size="size"
@change="handleChange"
@deselect="handleChange"
>
<a-select-option
v-for="item in $smartEnumPlugin.getValueDescList('FLAG_NUMBER_ENUM')"
:key="item.value"
:value="item.value"
>
{{ item.desc }}
</a-select-option>
</a-select>
</template>
<script setup lang="ts">
import _ from "lodash";
import { ref, watch } from "vue";
interface SmartBooleanSelectProps {
value?: boolean;
width: number;
size?: string;
placeholder?: string;
}
const props = withDefaults(defineProps<SmartBooleanSelectProps>(), {
value: undefined,
width: 100,
placeholder: "请选择",
});
const emit = defineEmits<{
(e: "update:value", value);
(e: "change", value);
}>();
function convertBoolean2number(value: null | boolean | undefined): null | number {
let result: null | number = null;
if (_.isNaN(value) || _.isNull(value) || _.isUndefined(value)) {
result = null;
} else {
result = value ? 1 : 0;
}
return result;
}
const selectValue = ref<any>(convertBoolean2number(props.value));
// value
watch(
() => props.value,
(newValue) => {
selectValue.value = convertBoolean2number(newValue);
}
);
const handleChange = (value) => {
console.log("boolean enum select", value);
let booleanResult = null;
if (!_.isUndefined(value)) {
booleanResult = value === 1 ? true : false;
}
emit("update:value", booleanResult);
emit("change", booleanResult);
};
</script>

View File

@ -0,0 +1,71 @@
<!--
* @Description:
* @Author: zhuoda
* @Date: 2021-08-03
* @LastEditTime: 2021-08-28 15:31:50
* @LastEditors: zhuoda
-->
<template>
<a-select
v-model:value="selectValue"
:style="`width: ${width}px`"
:placeholder="props.placeholder"
:showSearch="true"
:allowClear="true"
:size="size"
@change="handleChange"
@deselect="handleChange"
:disabled="disabled"
>
<a-select-option
v-for="item in $smartEnumPlugin.getValueDescList(props.enumName)"
:key="item.value"
:value="item.value"
>
{{ item.desc }}
</a-select-option>
</a-select>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
// ========================
interface SmartEnumSelectProps {
enumName: string;
value?: string | number | string[] | number[];
width?: number;
size?: string;
placeholder?: string;
disabled?: boolean;
}
const props = withDefaults(defineProps<SmartEnumSelectProps>(), {
enumName: undefined,
value: undefined,
width: 100,
placeholder: "请选择",
});
const emit = defineEmits<{
(e: "update:value", value: any): void;
(e: "change", value: any): void;
}>();
// ========================
const selectValue = ref(props.value);
// value
watch(
() => props.value,
(newValue) => {
selectValue.value = newValue;
}
);
function handleChange(value: any): void {
emit("update:value", value);
emit("change", value);
}
</script>

View File

@ -0,0 +1,13 @@
import {useSpinStore} from "/@/store/modules/system/spin";
export const SmartLoading = {
show: () => {
useSpinStore().show();
},
hide: () => {
useSpinStore().hide();
}
};

View File

@ -0,0 +1,60 @@
<template>
<div class="smart-table-wrapper">
<a-button type="primary" shape="circle" @click="v2showColumnOperator">
<template #icon>
<SettingOutlined/>
</template>
</a-button>
<a-button type="primary" shape="circle">
<template #icon>
<ExportOutlined/>
</template>
</a-button>
</div>
</template>
<script lang="ts">
import {SettingOutlined, ExportOutlined} from "@ant-design/icons-vue";
import {defineComponent, reactive, UnwrapRef} from "vue";
import _ from "lodash";
export default defineComponent({
name: "SmartTableOperator",
components: {
SettingOutlined, ExportOutlined
},
props: {
columnArray: Array,
obj:Object,
text:String
},
methods:{
v2showColumnOperator(){
// this.obj.a = 1;
// this.text = '3';
// this.columnArray.splice(0,1);
console.log(2, this.columnArray);
}
},
setup(props) {
const showColumnOperator = () => {
// arr.splice(0);
// props.columnArray = props.columnArray.splice(0,1);
console.log(props.columnArray);
};
return {
showColumnOperator
};
}
});
</script>

View File

@ -0,0 +1,158 @@
<!--
* @Description:
* @version:
* @Author: zhuoda
* @Date: 2021-08-12 17:34:23
* @LastEditors: zhuoda
* @LastEditTime: 2021-09-01 21:03:33
-->
<template>
<div class="clearfix">
<a-upload
:accept="props.accept"
:customRequest="customRequest"
:file-list="fileList"
list-type="picture-card"
:headers="{ 'x-access-token': useUserStore().getToken }"
:before-upload="beforeUpload"
@preview="handlePreview"
@change="handleChange"
@remove="handleRemove"
>
<div v-if="fileList.length < props.maxSize">
<PlusOutlined />
<div class="ant-upload-text">
{{ buttonText }}
</div>
</div>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewUrl" />
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { message } from "ant-design-vue";
import { fileApi } from "/@/api/business/file/file-api";
import { useUserStore } from "/@/store/modules/system/user";
import { useSpinStore } from "/@/store/modules/system/spin";
import { FILE_FOLDER_TYPE_ENUM } from "/@/constants/business/file";
// =========== token =============
const getToken = computed(() => {
return "";
});
// ========================
interface UploadProps {
value?: string;
buttonText?: string;
showUploadBtn?: boolean;
defaultFileList?: any;
multiple?: boolean;
maxUploadSize?: number; //
accept?: string; //
maxSize?: number;
folder?: number; //
fileList?: Array<FileUploadVo>;
}
const props = withDefaults(defineProps<UploadProps>(), {
value: undefined,
buttonText: "点击上传附件",
showUploadBtn: true,
defaultFileList: [],
multiple: false,
maxUploadSize: 10,
maxSize: 10,
accept: "",
folder: FILE_FOLDER_TYPE_ENUM.COMMON.value,
});
const emit = defineEmits<{
(e: "update:value", value: any): void;
(e: "change", value: any): void;
}>();
//
const files = computed(() => {
let res = [];
if (props.fileList && props.fileList.length > 0) {
props.fileList.forEach((element) => {
element.url = element.fileUrl;
res.push(element);
});
return res;
}
return res;
});
// ========================
const previewVisible = ref<boolean>(false);
const fileList = ref(files.value);
const previewUrl = ref<string>("");
const customRequest = async (options: any) => {
useSpinStore().show();
try {
console.log(options);
const formData = new FormData();
formData.append("file", options.file);
let res = await fileApi.uploadFile(
formData,
props.folder
);
let file = res.data;
file.url = file.fileUrl;
fileList.value.push(file);
emit("change", fileList.value);
} catch (e) {
console.log(e);
} finally {
useSpinStore().hide();
}
};
function handleChange(info: any) {
let fileStatus: string = info.file.status;
let file = info.file;
if (fileStatus == "removed") {
let index = fileList.value.findIndex((e) => e.fileId == file.fileId);
if (index != -1) {
fileList.value.splice(index, 1);
emit("change", fileList.value);
}
}
}
function handleRemove(file) {
console.log(fileList.value);
}
function beforeUpload(file) {
const isLimitSize = file.size / 1024 / 1024 < props.maxSize;
if (!isLimitSize) {
return message.error(`上传的文件必须小于${props.maxSize}Mb`);
}
return isLimitSize;
}
function handleCancel() {
previewVisible.value = false;
}
const handlePreview = async (file: any) => {
previewUrl.value = file.url || file.preview;
previewVisible.value = true;
};
function clear() {
fileList.value = [];
}
defineExpose({
clear,
});
</script>
<style lang="less" scoped>
:deep(.ant-upload-picture-card-wrapper) {
display: flex;
}
</style>

View File

@ -0,0 +1,37 @@
/*
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-08-25 17:01:33
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/config/app-config.ts
*/
/**
* 应用默认配置
*/
export const appDefaultConfig = {
// i18n 语言选择
languageType: 'zh_CN',
// 布局: side 或者 side-expand
layout: 'side-expand',
// 主题
theme: 'dark',
// 手机模式最大宽度 768px如果少于768px则手机自适应
mobileMaxWidth: 768,
// 侧边菜单宽度 默认为256px
sideMenuWidth: 256,
// 标签页
multiPageTagFlag: true,
// 标签页缓存, keep-alive true 开启缓存; false 不开启
multiPageTagKeepAliveFlag: true,
// 固定头部状态栏true:固定false:不固定
headerFixedFlag: true,
// 固定侧边栏true:固定false:不固定
sideBarFixedFlag: true,
// 隐藏设置true:隐藏false:不隐藏
hideSettingFlag: false,
// 首页页面Name
homePageName: 'Home',
};

View File

@ -0,0 +1,26 @@
/*
* @Author: zhuoda
* @Date: 2021-08-03 10:27:11
* @LastEditTime: 2021-09-01
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/config/project-config.ts
*/
import moment from 'moment';
/**
* 项目默认配置
*/
export const projectDefaultConfig = {
// 项目名称
projectName: 'SmartAdmin',
// 版权信息
copyright: 'Copyright ©2015-' + moment().format('YYYY') + '版权所有: 1024创新实验室 ',
// 点击版权的跳转
copyrightUrl: 'http://www.1024lab.net',
// 版本: 1.0.0
version: '2.0.0-beta',
// build时间戳
buildTime: moment().format('YYYY-MM-DD HH:mm:ss'),
};

View File

@ -0,0 +1,22 @@
/*
* @Description:
* @Author: zhuoda
* @Date: 2021-08-11
* @LastEditTime: 2021-09-01 22:31:37
* @LastEditors: zhuoda
*/
// 商品分类
export const GOODS_TYPE_ENUM = {
BOOK: {
value: 1,
desc: '图书',
},
COURSE: {
value: 2,
desc: '课程',
},
};
export default {
GOODS_TYPE_ENUM,
};

View File

@ -0,0 +1,37 @@
/*
* @Description:
* @Author: zhuoda
* @Date: 2021-08-03
* @LastEditTime: 2021-08-27
* @LastEditors: zhuoda
*/
export const PAGE_SIZE = 15;
export const PAGE_SIZE_OPTIONS = ['10', '15', '25', '35', '45', '55', '100', '150', '200', '300', '500'];
export const FLAG_NUMBER_ENUM = {
TRUE: {
value: 1,
desc: '是',
},
FALSE: {
value: 0,
desc: '否',
},
};
export const GenderEnum = {
UNKNOWN: {
value: 0,
desc: '未知',
},
MAN: {
value: 1,
desc: '男',
},
WOMAN: {
value: 2,
desc: '女',
},
};

View File

@ -0,0 +1,26 @@
/*
* @Author: zhuoda
* @Date: 2021-08-16 15:12:42
* @LastEditTime: 2021-08-27
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/constants/system/employee.ts
*/
export const GENDER_ENUM = {
UNKNOWN: {
value: 0,
desc: '未知',
},
MAN: {
value: 1,
desc: '男',
},
WOMAN: {
value: 2,
desc: '女',
},
};
export default {
GENDER_ENUM,
};

View File

@ -0,0 +1,25 @@
/*
* @Author: zhuoda
* @Date: 2021-08-09 08:58:11
* @LastEditTime: 2021-08-18 20:13:28
* @LastEditors: zhuoda
* @Description:
* @FilePath: /smart-admin/src/constants/system/local-storage-key.ts
*/
/**
* key前缀
*/
const KEY_PREFIX = 'crm_';
/**
* localStorageKey集合
*/
export default {
// 用户信息
USER_INFO: `${KEY_PREFIX}user_info`,
// 用户菜单路由
USER_MENU: `${KEY_PREFIX}user_menu`,
// 用户权限点
USER_POINTS: `${KEY_PREFIX}user_points`,
// 用户的tag列表
USER_TAG_NAV: `${KEY_PREFIX}user_tag_nav`,
};

View File

@ -0,0 +1,12 @@
// 语言选择数组
export const i18nList = [
{
text: '简体中文',
value: 'zh_CN',
},
{
text: 'English',
value: 'en',
},
]

View File

@ -0,0 +1,5 @@
import settings from './account/settings'
export default {
...settings
}

View File

@ -0,0 +1,62 @@
export default {
'account.settings.menuMap.basic': 'Basic Settings',
'account.settings.menuMap.security': 'Security Settings',
'account.settings.menuMap.custom': 'Custom Settings',
'account.settings.menuMap.binding': 'Account Binding',
'account.settings.menuMap.notification': 'New Message Notification',
'account.settings.basic.avatar': 'Avatar',
'account.settings.basic.change-avatar': 'Change avatar',
'account.settings.basic.email': 'Email',
'account.settings.basic.email-message': 'Please input your email!',
'account.settings.basic.nickname': 'Nickname',
'account.settings.basic.nickname-message': 'Please input your Nickname!',
'account.settings.basic.profile': 'Personal profile',
'account.settings.basic.profile-message': 'Please input your personal profile!',
'account.settings.basic.profile-placeholder': 'Brief introduction to yourself',
'account.settings.basic.country': 'Country/Region',
'account.settings.basic.country-message': 'Please input your country!',
'account.settings.basic.geographic': 'Province or city',
'account.settings.basic.geographic-message': 'Please input your geographic info!',
'account.settings.basic.address': 'Street Address',
'account.settings.basic.address-message': 'Please input your address!',
'account.settings.basic.phone': 'Phone Number',
'account.settings.basic.phone-message': 'Please input your phone!',
'account.settings.basic.update': 'Update Information',
'account.settings.basic.update.success': 'Update basic information successfully',
'account.settings.security.strong': 'Strong',
'account.settings.security.medium': 'Medium',
'account.settings.security.weak': 'Weak',
'account.settings.security.password': 'Account Password',
'account.settings.security.password-description': 'Current password strength',
'account.settings.security.phone': 'Security Phone',
'account.settings.security.phone-description': 'Bound phone',
'account.settings.security.question': 'Security Question',
'account.settings.security.question-description':
'The security question is not set, and the security policy can effectively protect the account security',
'account.settings.security.email': 'Backup Email',
'account.settings.security.email-description': 'Bound Email',
'account.settings.security.mfa': 'MFA Device',
'account.settings.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
'account.settings.security.modify': 'Modify',
'account.settings.security.set': 'Set',
'account.settings.security.bind': 'Bind',
'account.settings.binding.taobao': 'Binding Taobao',
'account.settings.binding.taobao-description': 'Currently unbound Taobao account',
'account.settings.binding.alipay': 'Binding Alipay',
'account.settings.binding.alipay-description': 'Currently unbound Alipay account',
'account.settings.binding.dingding': 'Binding DingTalk',
'account.settings.binding.dingding-description': 'Currently unbound DingTalk account',
'account.settings.binding.bind': 'Bind',
'account.settings.notification.password': 'Account Password',
'account.settings.notification.password-description':
'Messages from other users will be notified in the form of a station letter',
'account.settings.notification.messages': 'System Messages',
'account.settings.notification.messages-description':
'System messages will be notified in the form of a station letter',
'account.settings.notification.todo': 'To-do Notification',
'account.settings.notification.todo-description':
'The to-do list will be notified in the form of a letter from the station',
'account.settings.settings.open': 'Open',
'account.settings.settings.close': 'Close'
}

View File

@ -0,0 +1,5 @@
import analysis from './dashboard/analysis'
export default {
...analysis
}

View File

@ -0,0 +1,36 @@
export default {
'dashboard.analysis.test': 'Gongzhuan No.{no} shop',
'dashboard.analysis.introduce': 'Introduce',
'dashboard.analysis.total-sales': 'Total Sales',
'dashboard.analysis.day-sales': 'Daily Sales',
'dashboard.analysis.visits': 'Visits',
'dashboard.analysis.visits-trend': 'Visits Trend',
'dashboard.analysis.visits-ranking': 'Visits Ranking',
'dashboard.analysis.day-visits': 'Daily Visits',
'dashboard.analysis.week': 'WoW Change',
'dashboard.analysis.day': 'DoD Change',
'dashboard.analysis.payments': 'Payments',
'dashboard.analysis.conversion-rate': 'Conversion Rate',
'dashboard.analysis.operational-effect': 'Operational Effect',
'dashboard.analysis.sales-trend': 'Stores Sales Trend',
'dashboard.analysis.sales-ranking': 'Sales Ranking',
'dashboard.analysis.all-year': 'All Year',
'dashboard.analysis.all-month': 'All Month',
'dashboard.analysis.all-week': 'All Week',
'dashboard.analysis.all-day': 'All day',
'dashboard.analysis.search-users': 'Search Users',
'dashboard.analysis.per-capita-search': 'Per Capita Search',
'dashboard.analysis.online-top-search': 'Online Top Search',
'dashboard.analysis.the-proportion-of-sales': 'The Proportion Of Sales',
'dashboard.analysis.dropdown-option-one': 'Operation one',
'dashboard.analysis.dropdown-option-two': 'Operation two',
'dashboard.analysis.channel.all': 'ALL',
'dashboard.analysis.channel.online': 'Online',
'dashboard.analysis.channel.stores': 'Stores',
'dashboard.analysis.sales': 'Sales',
'dashboard.analysis.traffic': 'Traffic',
'dashboard.analysis.table.rank': 'Rank',
'dashboard.analysis.table.search-keyword': 'Keyword',
'dashboard.analysis.table.users': 'Users',
'dashboard.analysis.table.weekly-range': 'Weekly Range'
}

View File

@ -0,0 +1,5 @@
import basicForm from './form/basicForm'
export default {
...basicForm
}

View File

@ -0,0 +1,61 @@
export default {
'form.basic-form.basic.title': 'Basic form',
'form.basic-form.basic.description':
'Form pages are used to collect or verify information to users, and basic forms are common in scenarios where there are fewer data items.',
'form.basic-form.title.label': 'Title',
'form.basic-form.title.placeholder': 'Give the target a name',
'form.basic-form.title.required': 'Please enter a title',
'form.basic-form.date.label': 'Start and end date',
'form.basic-form.placeholder.start': 'Start date',
'form.basic-form.placeholder.end': 'End date',
'form.basic-form.date.required': 'Please select the start and end date',
'form.basic-form.goal.label': 'Goal description',
'form.basic-form.goal.placeholder': 'Please enter your work goals',
'form.basic-form.goal.required': 'Please enter a description of the goal',
'form.basic-form.standard.label': 'Metrics',
'form.basic-form.standard.placeholder': 'Please enter a metric',
'form.basic-form.standard.required': 'Please enter a metric',
'form.basic-form.client.label': 'Client',
'form.basic-form.label.tooltip': 'Target service object',
'form.basic-form.client.placeholder':
'Please describe your customer service, internal customers directly @ Name / job number',
'form.basic-form.client.required': 'Please describe the customers you serve',
'form.basic-form.invites.label': 'Inviting critics',
'form.basic-form.invites.placeholder':
'Please direct @ Name / job number, you can invite up to 5 people',
'form.basic-form.weight.label': 'Weight',
'form.basic-form.weight.placeholder': 'Please enter weight',
'form.basic-form.public.label': 'Target disclosure',
'form.basic-form.label.help': 'Customers and invitees are shared by default',
'form.basic-form.radio.public': 'Public',
'form.basic-form.radio.partially-public': 'Partially public',
'form.basic-form.radio.private': 'Private',
'form.basic-form.publicUsers.placeholder': 'Open to',
'form.basic-form.option.A': 'Colleague A',
'form.basic-form.option.B': 'Colleague B',
'form.basic-form.option.C': 'Colleague C',
'form.basic-form.email.required': 'Please enter your email!',
'form.basic-form.email.wrong-format': 'The email address is in the wrong format!',
'form.basic-form.userName.required': 'Please enter your userName!',
'form.basic-form.password.required': 'Please enter your password!',
'form.basic-form.password.twice': 'The passwords entered twice do not match!',
'form.basic-form.strength.msg':
"Please enter at least 6 characters and don't use passwords that are easy to guess.",
'form.basic-form.strength.strong': 'Strength: strong',
'form.basic-form.strength.medium': 'Strength: medium',
'form.basic-form.strength.short': 'Strength: too short',
'form.basic-form.confirm-password.required': 'Please confirm your password!',
'form.basic-form.phone-number.required': 'Please enter your phone number!',
'form.basic-form.phone-number.wrong-format': 'Malformed phone number!',
'form.basic-form.verification-code.required': 'Please enter the verification code!',
'form.basic-form.form.get-captcha': 'Get Captcha',
'form.basic-form.captcha.second': 'sec',
'form.basic-form.form.optional': ' (optional) ',
'form.basic-form.form.submit': 'Submit',
'form.basic-form.form.save': 'Save',
'form.basic-form.email.placeholder': 'Email',
'form.basic-form.password.placeholder': 'Password',
'form.basic-form.confirm-password.placeholder': 'Confirm password',
'form.basic-form.phone-number.placeholder': 'Phone number',
'form.basic-form.verification-code.placeholder': 'Verification code'
}

View File

@ -0,0 +1,6 @@
export default {
submit: 'Submit',
save: 'Save',
'submit.ok': 'Submit successfully',
'save.ok': 'Saved successfully'
}

View File

@ -0,0 +1,35 @@
import antdEnUS from 'ant-design-vue/es/locale-provider/en_US'
import momentEU from 'moment/locale/eu'
import global from './global'
import menu from './menu'
import setting from './setting'
import user from './user'
import dashboard from './dashboard'
import form from './form'
import result from './result'
import account from './account'
const components = {
antLocale: antdEnUS,
momentName: 'eu',
momentLocale: momentEU
}
export default {
message: '-',
'layouts.usermenu.dialog.title': 'Message',
'layouts.usermenu.dialog.content': 'Are you sure you would like to logout?',
'layouts.userLayout.title': 'Ant Design is the most influential web design specification in Xihu district',
...components,
...global,
...menu,
...setting,
...user,
...dashboard,
...form,
...result,
...account
}

View File

@ -0,0 +1,39 @@
export default {
'menu.welcome': 'Welcome',
'menu.home': 'Home',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Analysis',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Workplace',
'menu.form': 'Form',
'menu.form.basic-form': 'Basic Form',
'menu.form.step-form': 'Step Form',
'menu.form.step-form.info': 'Step Form(write transfer information)',
'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
'menu.form.step-form.result': 'Step Form(finished)',
'menu.form.advanced-form': 'Advanced Form',
'menu.list': 'List',
'menu.list.table-list': 'Search Table',
'menu.list.basic-list': 'Basic List',
'menu.list.card-list': 'Card List',
'menu.list.search-list': 'Search List',
'menu.list.search-list.articles': 'Search List(articles)',
'menu.list.search-list.projects': 'Search List(projects)',
'menu.list.search-list.applications': 'Search List(applications)',
'menu.profile': 'Profile',
'menu.profile.basic': 'Basic Profile',
'menu.profile.advanced': 'Advanced Profile',
'menu.result': 'Result',
'menu.result.success': 'Success',
'menu.result.fail': 'Fail',
'menu.exception': 'Exception',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Trigger',
'menu.account': 'Account',
'menu.account.center': 'Account Center',
'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout'
}

View File

@ -0,0 +1,7 @@
import success from './result/success'
import fail from './result/fail'
export default {
...success,
...fail
}

View File

@ -0,0 +1,11 @@
export default {
'result.fail.error.title': 'Submission Failed',
'result.fail.error.description':
'Please check and modify the following information before resubmitting.',
'result.fail.error.hint-title': 'The content you submitted has the following error:',
'result.fail.error.hint-text1': 'Your account has been frozen',
'result.fail.error.hint-btn1': 'Thaw immediately',
'result.fail.error.hint-text2': 'Your account is not yet eligible to apply',
'result.fail.error.hint-btn2': 'Upgrade immediately',
'result.fail.error.btn-text': 'Return to modify'
}

View File

@ -0,0 +1,19 @@
export default {
'result.success.title': 'Submission Success',
'result.success.description':
'The submission results page is used to feed back the results of a series of operational tasks. If it is a simple operation, use the Message global prompt feedback. This text area can show a simple supplementary explanation. If there is a similar requirement for displaying “documents”, the following gray area can present more complicated content.',
'result.success.operate-title': 'Project Name',
'result.success.operate-id': 'Project ID',
'result.success.principal': 'Principal',
'result.success.operate-time': 'Effective time',
'result.success.step1-title': 'Create project',
'result.success.step1-operator': 'Qu Lili',
'result.success.step2-title': 'Departmental preliminary review',
'result.success.step2-operator': 'Zhou Maomao',
'result.success.step2-extra': 'Urge',
'result.success.step3-title': 'Financial review',
'result.success.step4-title': 'Finish',
'result.success.btn-return': 'Back List',
'result.success.btn-project': 'View Project',
'result.success.btn-print': 'Print'
}

View File

@ -0,0 +1,29 @@
export default {
'app.setting.pagestyle': 'Page style setting',
'app.setting.pagestyle.light': 'Light style',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.realdark': 'RealDark style',
'app.setting.themecolor': 'Theme Color',
'app.setting.navigationmode': 'Navigation Mode',
'app.setting.content-width': 'Content Width',
'app.setting.fixedheader': 'Fixed Header',
'app.setting.fixedsidebar': 'Fixed Sidebar',
'app.setting.sidemenu': 'Side Menu Layout',
'app.setting.topmenu': 'Top Menu Layout',
'app.setting.content-width.fixed': 'Fixed',
'app.setting.content-width.fluid': 'Fluid',
'app.setting.othersettings': 'Other Settings',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copy Setting',
'app.setting.loading': 'Loading theme',
'app.setting.copyinfo': 'copy successplease replace defaultSettings in src/config/defaultSettings.js',
'app.setting.production.hint': 'Setting panel shows in development environment only, please manually modify',
'app.setting.themecolor.daybreak': 'Daybreak Blue',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.geekblue': 'Geek Blue',
'app.setting.themecolor.purple': 'Golden Purple'
}

View File

@ -0,0 +1,45 @@
export default {
'user.login.userName': 'userName',
'user.login.password': 'password',
'user.login.username.placeholder': 'Account: admin',
'user.login.password.placeholder': 'password: admin or ant.design',
'user.login.message-invalid-credentials':
'Invalid username or passwordadmin/ant.design',
'user.login.message-invalid-verification-code': 'Invalid verification code',
'user.login.tab-login-credentials': 'Credentials',
'user.login.tab-login-mobile': 'Mobile number',
'user.login.mobile.placeholder': 'Mobile number',
'user.login.mobile.verification-code.placeholder': 'Verification code',
'user.login.remember-me': 'Remember me',
'user.login.forgot-password': 'Forgot your password?',
'user.login.sign-in-with': 'Sign in with',
'user.login.signup': 'Sign up',
'user.login.login': 'Login',
'user.register.register': 'Register',
'user.register.email.placeholder': 'Email',
'user.register.password.placeholder': 'Password ',
'user.register.password.popover-message': 'Please enter at least 6 characters. Please do not use passwords that are easy to guess. ',
'user.register.confirm-password.placeholder': 'Confirm password',
'user.register.get-verification-code': 'Get code',
'user.register.sign-in': 'Already have an account?',
'user.register-result.msg': 'Accountregistered at {email}',
'user.register-result.activation-email':
'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
'user.register-result.back-home': 'Back to home',
'user.register-result.view-mailbox': 'View mailbox',
'user.email.required': 'Please enter your email!',
'user.email.wrong-format': 'The email address is in the wrong format!',
'user.userName.required': 'Please enter account name or email address',
'user.password.required': 'Please enter your password!',
'user.password.twice.msg': 'The passwords entered twice do not match!',
'user.password.strength.msg':
'The password is not strong enough',
'user.password.strength.strong': 'Strength: strong',
'user.password.strength.medium': 'Strength: medium',
'user.password.strength.low': 'Strength: low',
'user.password.strength.short': 'Strength: too short',
'user.confirm-password.required': 'Please confirm your password!',
'user.phone-number.required': 'Please enter your phone number!',
'user.phone-number.wrong-format': 'Please enter a valid phone number',
'user.verification-code.required': 'Please enter the verification code!'
}

View File

@ -0,0 +1,5 @@
import settings from './account/settings'
export default {
...settings
}

View File

@ -0,0 +1,57 @@
export default {
'account.settings.menuMap.basic': '基本设置',
'account.settings.menuMap.security': '安全设置',
'account.settings.menuMap.custom': '个性化',
'account.settings.menuMap.binding': '账号绑定',
'account.settings.menuMap.notification': '新消息通知',
'account.settings.basic.avatar': '头像',
'account.settings.basic.change-avatar': '更换头像',
'account.settings.basic.email': '邮箱',
'account.settings.basic.email-message': '请输入您的邮箱!',
'account.settings.basic.nickname': '昵称',
'account.settings.basic.nickname-message': '请输入您的昵称!',
'account.settings.basic.profile': '个人简介',
'account.settings.basic.profile-message': '请输入个人简介!',
'account.settings.basic.profile-placeholder': '个人简介',
'account.settings.basic.country': '国家/地区',
'account.settings.basic.country-message': '请输入您的国家或地区!',
'account.settings.basic.geographic': '所在省市',
'account.settings.basic.geographic-message': '请输入您的所在省市!',
'account.settings.basic.address': '街道地址',
'account.settings.basic.address-message': '请输入您的街道地址!',
'account.settings.basic.phone': '联系电话',
'account.settings.basic.phone-message': '请输入您的联系电话!',
'account.settings.basic.update': '更新基本信息',
'account.settings.basic.update.success': '更新基本信息成功',
'account.settings.security.strong': '强',
'account.settings.security.medium': '中',
'account.settings.security.weak': '弱',
'account.settings.security.password': '账户密码',
'account.settings.security.password-description': '当前密码强度:',
'account.settings.security.phone': '密保手机',
'account.settings.security.phone-description': '已绑定手机:',
'account.settings.security.question': '密保问题',
'account.settings.security.question-description': '未设置密保问题,密保问题可有效保护账户安全',
'account.settings.security.email': '备用邮箱',
'account.settings.security.email-description': '已绑定邮箱:',
'account.settings.security.mfa': 'MFA 设备',
'account.settings.security.mfa-description': '未绑定 MFA 设备,绑定后,可以进行二次确认',
'account.settings.security.modify': '修改',
'account.settings.security.set': '设置',
'account.settings.security.bind': '绑定',
'account.settings.binding.taobao': '绑定淘宝',
'account.settings.binding.taobao-description': '当前未绑定淘宝账号',
'account.settings.binding.alipay': '绑定支付宝',
'account.settings.binding.alipay-description': '当前未绑定支付宝账号',
'account.settings.binding.dingding': '绑定钉钉',
'account.settings.binding.dingding-description': '当前未绑定钉钉账号',
'account.settings.binding.bind': '绑定',
'account.settings.notification.password': '账户密码',
'account.settings.notification.password-description': '其他用户的消息将以站内信的形式通知',
'account.settings.notification.messages': '系统消息',
'account.settings.notification.messages-description': '系统消息将以站内信的形式通知',
'account.settings.notification.todo': '待办任务',
'account.settings.notification.todo-description': '待办任务将以站内信的形式通知',
'account.settings.settings.open': '开',
'account.settings.settings.close': '关'
}

View File

@ -0,0 +1,5 @@
import analysis from './dashboard/analysis'
export default {
...analysis
}

View File

@ -0,0 +1,36 @@
export default {
'dashboard.analysis.test': '工专路 {no} 号店',
'dashboard.analysis.introduce': '指标说明',
'dashboard.analysis.total-sales': '总销售额',
'dashboard.analysis.day-sales': '日均销售额¥',
'dashboard.analysis.visits': '访问量',
'dashboard.analysis.visits-trend': '访问量趋势',
'dashboard.analysis.visits-ranking': '门店访问量排名',
'dashboard.analysis.day-visits': '日访问量',
'dashboard.analysis.week': '周同比',
'dashboard.analysis.day': '日同比',
'dashboard.analysis.payments': '支付笔数',
'dashboard.analysis.conversion-rate': '转化率',
'dashboard.analysis.operational-effect': '运营活动效果',
'dashboard.analysis.sales-trend': '销售趋势',
'dashboard.analysis.sales-ranking': '门店销售额排名',
'dashboard.analysis.all-year': '全年',
'dashboard.analysis.all-month': '本月',
'dashboard.analysis.all-week': '本周',
'dashboard.analysis.all-day': '今日',
'dashboard.analysis.search-users': '搜索用户数',
'dashboard.analysis.per-capita-search': '人均搜索次数',
'dashboard.analysis.online-top-search': '线上热门搜索',
'dashboard.analysis.the-proportion-of-sales': '销售额类别占比',
'dashboard.analysis.dropdown-option-one': '操作一',
'dashboard.analysis.dropdown-option-two': '操作二',
'dashboard.analysis.channel.all': '全部渠道',
'dashboard.analysis.channel.online': '线上',
'dashboard.analysis.channel.stores': '门店',
'dashboard.analysis.sales': '销售额',
'dashboard.analysis.traffic': '客流量',
'dashboard.analysis.table.rank': '排名',
'dashboard.analysis.table.search-keyword': '搜索关键词',
'dashboard.analysis.table.users': '用户数',
'dashboard.analysis.table.weekly-range': '周涨幅'
}

View File

@ -0,0 +1,5 @@
import basicForm from './form/basicForm'
export default {
...basicForm
}

Some files were not shown because too many files have changed in this diff Show More