mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-02-04 13:42:27 +08:00
fix Bug or add docs
This commit is contained in:
@@ -13,14 +13,18 @@ VITE_BASE_URL = /
|
|||||||
# 是否删除console
|
# 是否删除console
|
||||||
VITE_DROP_CONSOLE = true
|
VITE_DROP_CONSOLE = true
|
||||||
|
|
||||||
|
# 跨域代理,可以配置多个,请注意不要换行
|
||||||
|
#VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]]
|
||||||
|
# VITE_PROXY=[["/api","https://naive-ui-admin"]]
|
||||||
|
|
||||||
# API 接口地址
|
# API 接口地址
|
||||||
VITE_APP_API_URL = /
|
VITE_GLOB_API_URL =
|
||||||
|
|
||||||
# 图片上传地址
|
# 图片上传地址
|
||||||
VITE_GLOB_UPLOAD_URL= /
|
VITE_GLOB_UPLOAD_URL=
|
||||||
|
|
||||||
# 图片前缀地址
|
# 图片前缀地址
|
||||||
VITE_GLOB_IMG_URL= /
|
VITE_GLOB_IMG_URL=
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_GLOB_API_URL_PREFIX = /api
|
VITE_GLOB_API_URL_PREFIX = /api
|
||||||
|
|||||||
@@ -11,13 +11,21 @@ VITE_BASE_URL = /
|
|||||||
VITE_DROP_CONSOLE = true
|
VITE_DROP_CONSOLE = true
|
||||||
|
|
||||||
# API
|
# API
|
||||||
VITE_APP_API_URL = /
|
VITE_GLOB_API_URL =
|
||||||
|
|
||||||
# 图片上传地址
|
# 图片上传地址
|
||||||
VITE_GLOB_UPLOAD_URL= /
|
VITE_GLOB_UPLOAD_URL=
|
||||||
|
|
||||||
# 图片前缀地址
|
# 图片前缀地址
|
||||||
VITE_GLOB_IMG_URL= /
|
VITE_GLOB_IMG_URL=
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_GLOB_API_URL_PREFIX = /api
|
VITE_GLOB_API_URL_PREFIX = /api
|
||||||
|
|
||||||
|
# 是否启用gzip压缩或brotli压缩
|
||||||
|
# 可选: gzip | brotli | none
|
||||||
|
# 如果你需要多种形式,你可以用','来分隔
|
||||||
|
VITE_BUILD_COMPRESS = 'none'
|
||||||
|
|
||||||
|
# 使用压缩时是否删除原始文件,默认为false
|
||||||
|
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
|
||||||
|
|||||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
|||||||
|
# 1.5 (2021-07-30)
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- 修复表格列配置,拖拽时最后的操作列重复增加
|
||||||
|
- 多标签页交互优化
|
||||||
|
|
||||||
|
- ### ✨ Features
|
||||||
|
- `项目文档`已上线
|
||||||
|
- `Application`组件加载机制优化,解决路由守卫,Axios中可使用,Dialog,Message 等之类组件
|
||||||
|
- `BasicTable` 组件新增`高度自适应`,`单元格编辑`,`整行编辑` 特性
|
||||||
|
- `nprogress` 移除,用 `Loading Bar`代替
|
||||||
|
- 打包支持`gzip`,`brotli` 压缩
|
||||||
|
- 新增代理`VITE_PROXY`配置
|
||||||
|
- 路由菜单,支持多级菜单
|
||||||
|
- 依赖升级
|
||||||
|
- 本次更新,有破坏性更新,涉及文件重命名,增删调整
|
||||||
|
|
||||||
|
|
||||||
# 1.4 (2021-07-21)
|
# 1.4 (2021-07-21)
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
- vite降至2.3.6
|
- vite降至2.3.6
|
||||||
|
|||||||
28
README.md
28
README.md
@@ -1,6 +1,6 @@
|
|||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
Naive Ui Admin 是一个免费开源的中后台模版,使用了最新的`vue3`,`vite2`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
[Naive Ui Admin](https://github.com/jekip/naive-ui-admin) 是一个基于 [Vue3.0](https://github.com/vuejs/vue-next)、[Vite](https://github.com/vitejs/vite)、 [Naive UI](https://www.naiveui.com/)、[TypeScript](https://www.typescriptlang.org/) 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目,该项目使用最新的前端技术栈,相信不管是从新技术使用还是其他方面,都能帮助到你。
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
- **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发
|
- **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发
|
||||||
@@ -10,35 +10,15 @@ Naive Ui Admin 是一个免费开源的中后台模版,使用了最新的`vue3
|
|||||||
- **权限** 内置完善的动态路由权限生成方案
|
- **权限** 内置完善的动态路由权限生成方案
|
||||||
- **组件** 二次封装了多个常用的组件
|
- **组件** 二次封装了多个常用的组件
|
||||||
|
|
||||||
### 页面功能
|
|
||||||
#### 系统看板
|
|
||||||
- [x] 主控台
|
|
||||||
- [ ] 监控页
|
|
||||||
- [x] 工作台
|
|
||||||
- [x] 表单页面
|
|
||||||
- [x] 列表页面
|
|
||||||
- [x] 异常页面
|
|
||||||
- [x] 结果页面
|
|
||||||
- [x] 设置页面
|
|
||||||
- [x] 系统设置
|
|
||||||
- [x] 菜单权限
|
|
||||||
- [x] 角色权限
|
|
||||||
|
|
||||||
### 页面组件
|
|
||||||
#### ProTable
|
|
||||||
- [x] 基础表格
|
|
||||||
- [x] 上传图片
|
|
||||||
- [x] 滑块验证码
|
|
||||||
- 持续开发中...
|
|
||||||
|
|
||||||
## 在线预览
|
## 在线预览
|
||||||
- [naive-ui-admin](https://jekip.github.io)
|
- [naive-ui-admin](https://jekip.github.io)
|
||||||
|
|
||||||
账号:admin,密码:123456
|
账号:admin,密码:123456(随意)
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
[文档地址](https://github.com/jekip/naive-ui-admin) - 待完善
|
[文档地址](https://jekip.github.io/docs/)
|
||||||
|
|
||||||
## 准备
|
## 准备
|
||||||
|
|
||||||
@@ -85,7 +65,7 @@ yarn build
|
|||||||
[CHANGELOG](./CHANGELOG.md)
|
[CHANGELOG](./CHANGELOG.md)
|
||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
[@Vben](https://github.com/anncwb/vue-vben-admin) 借鉴 vue-vben-admin 实现的骨架,同时也使用作者开发的 vite 插件,非常感谢作者。
|
[@Vben](https://github.com/anncwb/vue-vben-admin) 借鉴 vue-vben-admin 实现的骨架,同时也使用作者开发的 vite 插件,再次感谢作者。
|
||||||
|
|
||||||
|
|
||||||
## 如何贡献
|
## 如何贡献
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
|
|||||||
if (envName === 'VITE_PROXY') {
|
if (envName === 'VITE_PROXY') {
|
||||||
try {
|
try {
|
||||||
realName = JSON.parse(realName);
|
realName = JSON.parse(realName);
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret[envName] = realName;
|
ret[envName] = realName;
|
||||||
process.env[envName] = realName;
|
process.env[envName] = realName;
|
||||||
@@ -51,12 +50,11 @@ export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.pr
|
|||||||
try {
|
try {
|
||||||
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
|
const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
|
||||||
envConfig = { ...envConfig, ...env };
|
envConfig = { ...envConfig, ...env };
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(envConfig).forEach((key) => {
|
Object.keys(envConfig).forEach((key) => {
|
||||||
const reg = new RegExp(`^(${ match })`);
|
const reg = new RegExp(`^(${match})`);
|
||||||
if (!reg.test(key)) {
|
if (!reg.test(key)) {
|
||||||
Reflect.deleteProperty(envConfig, key);
|
Reflect.deleteProperty(envConfig, key);
|
||||||
}
|
}
|
||||||
|
|||||||
35
build/vite/plugin/compress.ts
Normal file
35
build/vite/plugin/compress.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
|
||||||
|
* https://github.com/anncwb/vite-plugin-compression
|
||||||
|
*/
|
||||||
|
import type { Plugin } from 'vite';
|
||||||
|
|
||||||
|
import compressPlugin from 'vite-plugin-compression';
|
||||||
|
|
||||||
|
export function configCompressPlugin(
|
||||||
|
compress: 'gzip' | 'brotli' | 'none',
|
||||||
|
deleteOriginFile = false
|
||||||
|
): Plugin | Plugin[] {
|
||||||
|
const compressList = compress.split(',');
|
||||||
|
|
||||||
|
const plugins: Plugin[] = [];
|
||||||
|
|
||||||
|
if (compressList.includes('gzip')) {
|
||||||
|
plugins.push(
|
||||||
|
compressPlugin({
|
||||||
|
ext: '.gz',
|
||||||
|
deleteOriginFile,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (compressList.includes('brotli')) {
|
||||||
|
plugins.push(
|
||||||
|
compressPlugin({
|
||||||
|
ext: '.br',
|
||||||
|
algorithm: 'brotliCompress',
|
||||||
|
deleteOriginFile,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
@@ -5,9 +5,10 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
|
|||||||
|
|
||||||
import { configHtmlPlugin } from './html';
|
import { configHtmlPlugin } from './html';
|
||||||
import { configMockPlugin } from './mock';
|
import { configMockPlugin } from './mock';
|
||||||
|
import { configCompressPlugin } from './compress';
|
||||||
|
|
||||||
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock) {
|
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock) {
|
||||||
const { VITE_USE_MOCK } = viteEnv;
|
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
|
||||||
|
|
||||||
const vitePlugins: (Plugin | Plugin[])[] = [
|
const vitePlugins: (Plugin | Plugin[])[] = [
|
||||||
// have to
|
// have to
|
||||||
@@ -22,5 +23,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean, prodMock)
|
|||||||
// vite-plugin-mock
|
// vite-plugin-mock
|
||||||
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock));
|
VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild, prodMock));
|
||||||
|
|
||||||
|
if (isBuild) {
|
||||||
|
// rollup-plugin-gzip
|
||||||
|
vitePlugins.push(
|
||||||
|
configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return vitePlugins;
|
return vitePlugins;
|
||||||
}
|
}
|
||||||
|
|||||||
34
build/vite/proxy.ts
Normal file
34
build/vite/proxy.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Used to parse the .env.development proxy configuration
|
||||||
|
*/
|
||||||
|
import type { ProxyOptions } from 'vite';
|
||||||
|
|
||||||
|
type ProxyItem = [string, string];
|
||||||
|
|
||||||
|
type ProxyList = ProxyItem[];
|
||||||
|
|
||||||
|
type ProxyTargetList = Record<string, ProxyOptions & { rewrite: (path: string) => string }>;
|
||||||
|
|
||||||
|
const httpsRE = /^https:\/\//;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate proxy
|
||||||
|
* @param list
|
||||||
|
*/
|
||||||
|
export function createProxy(list: ProxyList = []) {
|
||||||
|
const ret: ProxyTargetList = {};
|
||||||
|
for (const [prefix, target] of list) {
|
||||||
|
const isHttps = httpsRE.test(target);
|
||||||
|
|
||||||
|
// https://github.com/http-party/node-http-proxy#options
|
||||||
|
ret[prefix] = {
|
||||||
|
target: target,
|
||||||
|
changeOrigin: true,
|
||||||
|
ws: true,
|
||||||
|
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
|
||||||
|
// https is require secure=false
|
||||||
|
...(isHttps ? { secure: false } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
|
<style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="appProvider" style="display: none"></div>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="first-loading-wrp">
|
<div class="first-loading-wrp">
|
||||||
<div class="loading-wrp">
|
<div class="loading-wrp">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const tableList = (pageSize) => {
|
|||||||
date: `@date('yyyy-MM-dd')`,
|
date: `@date('yyyy-MM-dd')`,
|
||||||
time: `@time('HH:mm')`,
|
time: `@time('HH:mm')`,
|
||||||
'no|100000-10000000': 100000,
|
'no|100000-10000000': 100000,
|
||||||
'status|1': ['normal', 'enable', 'disable'],
|
'status|1': [true, false],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "naive-ui-admin",
|
"name": "naive-ui-admin",
|
||||||
"version": "1.4",
|
"version": "1.5",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Ahjung",
|
"name": "Ahjung",
|
||||||
"email": "735878602@qq.com",
|
"email": "735878602@qq.com",
|
||||||
@@ -8,8 +8,12 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"bootstrap": "yarn install",
|
||||||
|
"serve": "npm run dev",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build && esno ./build/script/postBuild.ts",
|
"build": "vite build && esno ./build/script/postBuild.ts",
|
||||||
|
"build:no-cache": "yarn clean:cache && npm run build",
|
||||||
|
"report": "cross-env REPORT=true npm run build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build typecheck": "vuedx-typecheck . && vite build",
|
"build typecheck": "vuedx-typecheck . && vite build",
|
||||||
"deploy": "gh-pages -d dist",
|
"deploy": "gh-pages -d dist",
|
||||||
@@ -34,8 +38,7 @@
|
|||||||
"makeit-captcha": "^1.2.5",
|
"makeit-captcha": "^1.2.5",
|
||||||
"mitt": "^2.1.0",
|
"mitt": "^2.1.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"naive-ui": "^2.15.5",
|
"naive-ui": "^2.15.11",
|
||||||
"nprogress": "^1.0.0-1",
|
|
||||||
"pinia": "^2.0.0-beta.3",
|
"pinia": "^2.0.0-beta.3",
|
||||||
"qs": "^6.10.1",
|
"qs": "^6.10.1",
|
||||||
"vfonts": "^0.1.0",
|
"vfonts": "^0.1.0",
|
||||||
@@ -76,14 +79,16 @@
|
|||||||
"postcss": "^8.3.5",
|
"postcss": "^8.3.5",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"pretty-quick": "^3.1.0",
|
"pretty-quick": "^3.1.0",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
"stylelint": "^13.13.1",
|
"stylelint": "^13.13.1",
|
||||||
"stylelint-config-prettier": "^8.0.2",
|
"stylelint-config-prettier": "^8.0.2",
|
||||||
"stylelint-config-standard": "^22.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"stylelint-order": "^4.1.0",
|
"stylelint-order": "^4.1.0",
|
||||||
"stylelint-scss": "^3.19.0",
|
"stylelint-scss": "^3.19.0",
|
||||||
"tailwindcss": "^2.2.4",
|
"tailwindcss": "^2.2.7",
|
||||||
"typescript": "^4.3.2",
|
"typescript": "^4.3.5",
|
||||||
"vite": "2.3.6",
|
"vite": "2.3.6",
|
||||||
|
"vite-plugin-compression": "^0.3.1",
|
||||||
"vite-plugin-html": "^2.0.7",
|
"vite-plugin-html": "^2.0.7",
|
||||||
"vite-plugin-mock": "^2.9.3",
|
"vite-plugin-mock": "^2.9.3",
|
||||||
"vite-plugin-style-import": "^1.0.1",
|
"vite-plugin-style-import": "^1.0.1",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</AppProvider>
|
</AppProvider>
|
||||||
</NConfigProvider>
|
</NConfigProvider>
|
||||||
|
|
||||||
<transition v-if="isLock && $route.name != 'login'" name="slide-up">
|
<transition v-if="isLock && $route.name !== 'login'" name="slide-up">
|
||||||
<LockScreen />
|
<LockScreen />
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
@@ -34,9 +34,7 @@
|
|||||||
const designStore = useDesignSettingStore();
|
const designStore = useDesignSettingStore();
|
||||||
const isLock = computed(() => useLockscreen.isLock);
|
const isLock = computed(() => useLockscreen.isLock);
|
||||||
const lockTime = computed(() => useLockscreen.lockTime);
|
const lockTime = computed(() => useLockscreen.lockTime);
|
||||||
/**
|
|
||||||
* @type import('naive-ui').GlobalThemeOverrides
|
|
||||||
*/
|
|
||||||
const getThemeOverrides = computed(() => {
|
const getThemeOverrides = computed(() => {
|
||||||
return {
|
return {
|
||||||
common: {
|
common: {
|
||||||
@@ -89,9 +87,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
@import 'styles/global.less';
|
|
||||||
@import 'styles/common.less';
|
@import 'styles/common.less';
|
||||||
@import 'styles/override.less';
|
|
||||||
|
|
||||||
.slide-up-enter-active,
|
.slide-up-enter-active,
|
||||||
.slide-up-leave-active {
|
.slide-up-leave-active {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function adminMenus() {
|
|||||||
* 获取tree菜单列表
|
* 获取tree菜单列表
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export function getMenuList(params) {
|
export function getMenuList(params?) {
|
||||||
return http.request({
|
return http.request({
|
||||||
url: '/menu/list',
|
url: '/menu/list',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export function login(params) {
|
|||||||
params,
|
params,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
isTransformRequestResult: false,
|
isTransformResponse: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ export function changePassword(params, uid) {
|
|||||||
params,
|
params,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
isTransformRequestResult: false,
|
isTransformResponse: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-dialog-provider>
|
<n-loading-bar-provider>
|
||||||
<DialogContent />
|
<LoadingContent />
|
||||||
<n-notification-provider>
|
<n-dialog-provider>
|
||||||
<n-message-provider>
|
<DialogContent />
|
||||||
<MessageContent />
|
<n-notification-provider>
|
||||||
<slot slot="default"></slot>
|
<n-message-provider>
|
||||||
</n-message-provider>
|
<MessageContent />
|
||||||
</n-notification-provider>
|
<slot slot="default"></slot>
|
||||||
</n-dialog-provider>
|
</n-message-provider>
|
||||||
|
</n-notification-provider>
|
||||||
|
</n-dialog-provider>
|
||||||
|
</n-loading-bar-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
import {
|
||||||
|
NDialogProvider,
|
||||||
|
NNotificationProvider,
|
||||||
|
NMessageProvider,
|
||||||
|
NLoadingBarProvider,
|
||||||
|
} from 'naive-ui';
|
||||||
|
import { LoadingContent } from '@/components/LoadingContent';
|
||||||
import { MessageContent } from '@/components/MessageContent';
|
import { MessageContent } from '@/components/MessageContent';
|
||||||
import { DialogContent } from '@/components/DialogContent';
|
import { DialogContent } from '@/components/DialogContent';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Application',
|
name: 'Application',
|
||||||
components: { MessageContent, DialogContent },
|
components: {
|
||||||
|
NDialogProvider,
|
||||||
|
NNotificationProvider,
|
||||||
|
NMessageProvider,
|
||||||
|
NLoadingBarProvider,
|
||||||
|
LoadingContent,
|
||||||
|
MessageContent,
|
||||||
|
DialogContent,
|
||||||
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|||||||
3
src/components/LoadingContent/index.ts
Normal file
3
src/components/LoadingContent/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import LoadingContent from './index.vue';
|
||||||
|
|
||||||
|
export { LoadingContent };
|
||||||
12
src/components/LoadingContent/index.vue
Normal file
12
src/components/LoadingContent/index.vue
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<template></template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { useLoadingBar } from 'naive-ui';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoadingContent',
|
||||||
|
setup() {
|
||||||
|
//挂载在 window 方便与在js中使用
|
||||||
|
window['$loading'] = useLoadingBar();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
BasicTable 重封装组件说明
|
|
||||||
====
|
|
||||||
|
|
||||||
封装说明
|
|
||||||
----
|
|
||||||
|
|
||||||
> 基础的使用方式与 API 与 [官方版(data-table)](https://www.naiveui.com/zh-CN/os-theme/components/data-table#tree) 本一致,在其基础上,封装了加载数据的方法。
|
|
||||||
>
|
|
||||||
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 BasicTable 组件传递绑定 `:api="Promise"` 对象即可
|
|
||||||
>
|
|
||||||
> 例子1
|
|
||||||
----
|
|
||||||
(基础使用)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<BasicTable
|
|
||||||
title="表格列表"
|
|
||||||
:columns="columns"
|
|
||||||
:api="loadDataTable"
|
|
||||||
:row-key="row => row.id"
|
|
||||||
@update:checked-row-keys="onCheckedRow"
|
|
||||||
>
|
|
||||||
<template #toolbar>
|
|
||||||
<n-button type="primary">添加会员</n-button>
|
|
||||||
</template>
|
|
||||||
</BasicTable>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue'
|
|
||||||
import { BasicTable } from '@/components/Table'
|
|
||||||
import { getTableList } from '@/api/table/list'
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'id',
|
|
||||||
key: 'id'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
key: 'name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '地址',
|
|
||||||
key: 'address',
|
|
||||||
auth: ['amdin'], // 同时根据权限控制是否显示
|
|
||||||
ifShow: (row) => {
|
|
||||||
return true; // 根据业务控制是否显示
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '日期',
|
|
||||||
key: 'date'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
export default defineComponent({
|
|
||||||
components: { BasicTable },
|
|
||||||
setup() {
|
|
||||||
const loadDataTable = async (params) => {
|
|
||||||
const data = await getTableList(params);
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
columns,
|
|
||||||
loadDataTable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
API
|
|
||||||
----
|
|
||||||
BasicTable 在 NaiveUi 的 data-table 上进行了一层封装,支持了一些预设,并且封装了一些行为。这里只列出与 data-table 不同的 api。
|
|
||||||
|
|
||||||
> request:Promise 参考上面例子写法
|
|
||||||
> ref:可绑定ref 调用组件内部方法(data-table本身的方法和参数)
|
|
||||||
|
|
||||||
Methods
|
|
||||||
----
|
|
||||||
> reload:actionRef.value.reload()
|
|
||||||
|
|
||||||
> 其余方法,请打印查看
|
|
||||||
|
|
||||||
Slots
|
|
||||||
----
|
|
||||||
> 名称:tableTitle | 表格顶部左侧区域
|
|
||||||
> 名称:toolbar | 表格顶部右侧区域
|
|
||||||
|
|
||||||
|
|
||||||
更新时间
|
|
||||||
----
|
|
||||||
|
|
||||||
该文档最后更新于: 2021-07-12 PM 10:13
|
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="s-table">
|
<div class="s-table">
|
||||||
<n-data-table
|
<n-data-table
|
||||||
|
ref="tableElRef"
|
||||||
v-bind="getBindValues"
|
v-bind="getBindValues"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
@update:page="updatePage"
|
@update:page="updatePage"
|
||||||
@@ -73,7 +74,17 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { NDataTable } from 'naive-ui';
|
import { NDataTable } from 'naive-ui';
|
||||||
import { ref, defineComponent, reactive, unref, toRaw, computed, toRefs } from 'vue';
|
import {
|
||||||
|
ref,
|
||||||
|
defineComponent,
|
||||||
|
reactive,
|
||||||
|
unref,
|
||||||
|
toRaw,
|
||||||
|
computed,
|
||||||
|
toRefs,
|
||||||
|
onMounted,
|
||||||
|
nextTick,
|
||||||
|
} from 'vue';
|
||||||
import { ReloadOutlined, ColumnHeightOutlined, QuestionCircleOutlined } from '@vicons/antd';
|
import { ReloadOutlined, ColumnHeightOutlined, QuestionCircleOutlined } from '@vicons/antd';
|
||||||
import { createTableContext } from './hooks/useTableContext';
|
import { createTableContext } from './hooks/useTableContext';
|
||||||
|
|
||||||
@@ -88,6 +99,10 @@
|
|||||||
|
|
||||||
import { BasicTableProps } from './types/table';
|
import { BasicTableProps } from './types/table';
|
||||||
|
|
||||||
|
import { getViewportOffset } from '@/utils/domUtils';
|
||||||
|
import { useWindowSizeFn } from '@/hooks/event/useWindowSizeFn';
|
||||||
|
import { isBoolean } from '@/utils/is';
|
||||||
|
|
||||||
const densityOptions = [
|
const densityOptions = [
|
||||||
{
|
{
|
||||||
type: 'menu',
|
type: 'menu',
|
||||||
@@ -117,9 +132,20 @@
|
|||||||
...NDataTable.props, // 这里继承原 UI 组件的 props
|
...NDataTable.props, // 这里继承原 UI 组件的 props
|
||||||
...basicProps,
|
...basicProps,
|
||||||
},
|
},
|
||||||
emits: ['fetch-success', 'fetch-error', 'update:checked-row-keys'],
|
emits: [
|
||||||
|
'fetch-success',
|
||||||
|
'fetch-error',
|
||||||
|
'update:checked-row-keys',
|
||||||
|
'edit-end',
|
||||||
|
'edit-cancel',
|
||||||
|
'edit-row-end',
|
||||||
|
'edit-change',
|
||||||
|
],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
const deviceHeight = ref(150);
|
||||||
|
const tableElRef = ref<ComponentRef>(null);
|
||||||
const wrapRef = ref<Nullable<HTMLDivElement>>(null);
|
const wrapRef = ref<Nullable<HTMLDivElement>>(null);
|
||||||
|
let paginationEl: HTMLElement | null;
|
||||||
|
|
||||||
const tableData = ref<Recordable[]>([]);
|
const tableData = ref<Recordable[]>([]);
|
||||||
const innerPropsRef = ref<Partial<BasicTableProps>>();
|
const innerPropsRef = ref<Partial<BasicTableProps>>();
|
||||||
@@ -173,20 +199,14 @@
|
|||||||
emit('update:checked-row-keys', rowKeys);
|
emit('update:checked-row-keys', rowKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
//重置 Columns
|
|
||||||
const resetColumns = () => {
|
|
||||||
columns.map((item) => {
|
|
||||||
item.isShow = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//获取表格大小
|
//获取表格大小
|
||||||
const getTableSize = computed(() => state.tableSize);
|
const getTableSize = computed(() => state.tableSize);
|
||||||
|
|
||||||
//组装表格信息
|
//组装表格信息
|
||||||
const getBindValues = computed(() => {
|
const getBindValues = computed(() => {
|
||||||
const tableData = unref(getDataSourceRef);
|
const tableData = unref(getDataSourceRef);
|
||||||
let propsData = {
|
const maxHeight = tableData.length ? `${unref(deviceHeight)}px` : 'auto';
|
||||||
|
return {
|
||||||
...unref(getProps),
|
...unref(getProps),
|
||||||
loading: unref(getLoading),
|
loading: unref(getLoading),
|
||||||
columns: toRaw(unref(getPageColumns)),
|
columns: toRaw(unref(getPageColumns)),
|
||||||
@@ -194,8 +214,8 @@
|
|||||||
data: tableData,
|
data: tableData,
|
||||||
size: unref(getTableSize),
|
size: unref(getTableSize),
|
||||||
remote: true,
|
remote: true,
|
||||||
|
'max-height': maxHeight,
|
||||||
};
|
};
|
||||||
return propsData;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//获取分页信息
|
//获取分页信息
|
||||||
@@ -205,7 +225,7 @@
|
|||||||
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
|
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableAction: TableActionType = {
|
const tableAction = {
|
||||||
reload,
|
reload,
|
||||||
setColumns,
|
setColumns,
|
||||||
setLoading,
|
setLoading,
|
||||||
@@ -216,14 +236,54 @@
|
|||||||
setCacheColumnsField,
|
setCacheColumnsField,
|
||||||
emit,
|
emit,
|
||||||
getSize: () => {
|
getSize: () => {
|
||||||
return unref(getBindValues).size as SizeType;
|
return unref(getBindValues).size;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCanResize = computed(() => {
|
||||||
|
const { canResize } = unref(getProps);
|
||||||
|
return canResize;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function computeTableHeight() {
|
||||||
|
const table = unref(tableElRef);
|
||||||
|
if (!table) return;
|
||||||
|
if (!unref(getCanResize)) return;
|
||||||
|
const tableEl: any = table?.$el;
|
||||||
|
const headEl = tableEl.querySelector('.n-data-table-thead ');
|
||||||
|
const { bottomIncludeBody } = getViewportOffset(headEl);
|
||||||
|
const headerH = 64;
|
||||||
|
let paginationH = 2;
|
||||||
|
let marginH = 24;
|
||||||
|
if (!isBoolean(pagination)) {
|
||||||
|
paginationEl = tableEl.querySelector('.n-data-table__pagination') as HTMLElement;
|
||||||
|
if (paginationEl) {
|
||||||
|
const offsetHeight = paginationEl.offsetHeight;
|
||||||
|
paginationH += offsetHeight || 0;
|
||||||
|
} else {
|
||||||
|
paginationH += 28;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let height =
|
||||||
|
bottomIncludeBody - (headerH + paginationH + marginH + (props.resizeHeightOffset || 0));
|
||||||
|
const maxHeight = props.maxHeight;
|
||||||
|
height = maxHeight && maxHeight < height ? maxHeight : height;
|
||||||
|
deviceHeight.value = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
useWindowSizeFn(computeTableHeight, 280);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
computeTableHeight();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
createTableContext({ ...tableAction, wrapRef, getBindValues });
|
createTableContext({ ...tableAction, wrapRef, getBindValues });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
|
tableElRef,
|
||||||
getBindValues,
|
getBindValues,
|
||||||
densityOptions,
|
densityOptions,
|
||||||
reload,
|
reload,
|
||||||
@@ -232,7 +292,6 @@
|
|||||||
updatePageSize,
|
updatePageSize,
|
||||||
updateCheckedRowKeys,
|
updateCheckedRowKeys,
|
||||||
pagination,
|
pagination,
|
||||||
resetColumns,
|
|
||||||
tableAction,
|
tableAction,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
41
src/components/Table/src/componentMap.ts
Normal file
41
src/components/Table/src/componentMap.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type { Component } from 'vue';
|
||||||
|
import {
|
||||||
|
NInput,
|
||||||
|
NSelect,
|
||||||
|
NCheckbox,
|
||||||
|
NInputNumber,
|
||||||
|
NSwitch,
|
||||||
|
NDatePicker,
|
||||||
|
NTimePicker,
|
||||||
|
} from 'naive-ui';
|
||||||
|
import type { ComponentType } from './types/componentType';
|
||||||
|
|
||||||
|
export enum EventEnum {
|
||||||
|
NInput = 'on-input',
|
||||||
|
NInputNumber = 'on-input',
|
||||||
|
NSelect = 'on-update:value',
|
||||||
|
NSwitch = 'on-update:value',
|
||||||
|
NCheckbox = 'on-update:value',
|
||||||
|
NDatePicker = 'on-update:value',
|
||||||
|
NTimePicker = 'on-update:value',
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentMap = new Map<ComponentType, Component>();
|
||||||
|
|
||||||
|
componentMap.set('NInput', NInput);
|
||||||
|
componentMap.set('NInputNumber', NInputNumber);
|
||||||
|
componentMap.set('NSelect', NSelect);
|
||||||
|
componentMap.set('NSwitch', NSwitch);
|
||||||
|
componentMap.set('NCheckbox', NCheckbox);
|
||||||
|
componentMap.set('NDatePicker', NDatePicker);
|
||||||
|
componentMap.set('NTimePicker', NTimePicker);
|
||||||
|
|
||||||
|
export function add(compName: ComponentType, component: Component) {
|
||||||
|
componentMap.set(compName, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function del(compName: ComponentType) {
|
||||||
|
componentMap.delete(compName);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { componentMap };
|
||||||
@@ -41,6 +41,7 @@
|
|||||||
actions: {
|
actions: {
|
||||||
type: Array as PropType<ActionItem[]>,
|
type: Array as PropType<ActionItem[]>,
|
||||||
default: null,
|
default: null,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
dropDownActions: {
|
dropDownActions: {
|
||||||
type: Array as PropType<ActionItem[]>,
|
type: Array as PropType<ActionItem[]>,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type { FunctionalComponent, defineComponent } from 'vue';
|
||||||
|
import type { ComponentType } from '../../types/componentType';
|
||||||
|
import { componentMap } from '@/components/Table/src/componentMap';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { NPopover } from 'naive-ui';
|
||||||
|
|
||||||
|
export interface ComponentProps {
|
||||||
|
component: ComponentType;
|
||||||
|
rule: boolean;
|
||||||
|
popoverVisible: boolean;
|
||||||
|
ruleMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CellComponent: FunctionalComponent = (
|
||||||
|
{ component = 'NInput', rule = true, ruleMessage, popoverVisible }: ComponentProps,
|
||||||
|
{ attrs }
|
||||||
|
) => {
|
||||||
|
const Comp = componentMap.get(component) as typeof defineComponent;
|
||||||
|
|
||||||
|
const DefaultComp = h(Comp, attrs);
|
||||||
|
if (!rule) {
|
||||||
|
return DefaultComp;
|
||||||
|
}
|
||||||
|
return h(
|
||||||
|
NPopover,
|
||||||
|
{ 'display-directive': 'show', show: !!popoverVisible, manual: 'manual' },
|
||||||
|
{
|
||||||
|
trigger: () => DefaultComp,
|
||||||
|
default: () =>
|
||||||
|
h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
width: '90px',
|
||||||
|
display: 'inline-block',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => ruleMessage,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
402
src/components/Table/src/components/editable/EditableCell.vue
Normal file
402
src/components/Table/src/components/editable/EditableCell.vue
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editable-cell">
|
||||||
|
<div v-show="!isEdit" class="editable-cell-content" @click="handleEdit">
|
||||||
|
{{ getValues }}
|
||||||
|
<n-icon class="edit-icon" v-if="!column.editRow">
|
||||||
|
<FormOutlined />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
<div class="flex editable-cell-content" v-show="isEdit" v-click-outside="onClickOutside">
|
||||||
|
<CellComponent
|
||||||
|
v-bind="getComponentProps"
|
||||||
|
:component="getComponent"
|
||||||
|
:style="getWrapperStyle"
|
||||||
|
:popoverVisible="getRuleVisible"
|
||||||
|
:ruleMessage="ruleMessage"
|
||||||
|
:rule="getRule"
|
||||||
|
:class="getWrapperClass"
|
||||||
|
ref="elRef"
|
||||||
|
@options-change="handleOptionsChange"
|
||||||
|
@pressEnter="handleEnter"
|
||||||
|
/>
|
||||||
|
<div class="editable-cell-action" v-if="!getRowEditable">
|
||||||
|
<n-icon class="cursor-pointer mx-2">
|
||||||
|
<CheckOutlined @click="handleSubmit" />
|
||||||
|
</n-icon>
|
||||||
|
<n-icon class="cursor-pointer mx-2">
|
||||||
|
<CloseOutlined @click="handleCancel" />
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import type { CSSProperties, PropType } from 'vue';
|
||||||
|
import type { BasicColumn } from '../../types/table';
|
||||||
|
import type { EditRecordRow } from './index';
|
||||||
|
|
||||||
|
import { defineComponent, ref, unref, nextTick, computed, watchEffect, toRaw } from 'vue';
|
||||||
|
import { FormOutlined, CloseOutlined, CheckOutlined } from '@vicons/antd';
|
||||||
|
import { CellComponent } from './CellComponent';
|
||||||
|
|
||||||
|
import { useTableContext } from '../../hooks/useTableContext';
|
||||||
|
|
||||||
|
import clickOutside from '@/directives/clickOutside';
|
||||||
|
|
||||||
|
import { propTypes } from '@/utils/propTypes';
|
||||||
|
import { isString, isBoolean, isFunction, isNumber, isArray } from '@/utils/is';
|
||||||
|
import { createPlaceholderMessage } from './helper';
|
||||||
|
import { set, omit } from 'lodash-es';
|
||||||
|
import { EventEnum } from '@/components/Table/src/componentMap';
|
||||||
|
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'EditableCell',
|
||||||
|
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent },
|
||||||
|
directives: {
|
||||||
|
clickOutside,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [String, Number, Boolean, Object] as PropType<string | number | boolean | Recordable>,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
record: {
|
||||||
|
type: Object as PropType<EditRecordRow>,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
type: Object as PropType<BasicColumn>,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
index: propTypes.number,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const table = useTableContext();
|
||||||
|
const isEdit = ref(false);
|
||||||
|
const elRef = ref();
|
||||||
|
const ruleVisible = ref(false);
|
||||||
|
const ruleMessage = ref('');
|
||||||
|
const optionsRef = ref<LabelValueOptions>([]);
|
||||||
|
const currentValueRef = ref<any>(props.value);
|
||||||
|
const defaultValueRef = ref<any>(props.value);
|
||||||
|
|
||||||
|
// const { prefixCls } = useDesign('editable-cell');
|
||||||
|
|
||||||
|
const getComponent = computed(() => props.column?.editComponent || 'NInput');
|
||||||
|
const getRule = computed(() => props.column?.editRule);
|
||||||
|
|
||||||
|
const getRuleVisible = computed(() => {
|
||||||
|
return unref(ruleMessage) && unref(ruleVisible);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getIsCheckComp = computed(() => {
|
||||||
|
const component = unref(getComponent);
|
||||||
|
return ['NCheckbox', 'NSwitch'].includes(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getComponentProps = computed(() => {
|
||||||
|
const compProps = props.column?.editComponentProps ?? {};
|
||||||
|
const editComponent = props.column?.editComponent ?? null;
|
||||||
|
const component = unref(getComponent);
|
||||||
|
const apiSelectProps: Recordable = {};
|
||||||
|
|
||||||
|
const isCheckValue = unref(getIsCheckComp);
|
||||||
|
|
||||||
|
const valueField = isCheckValue ? 'checked' : 'value';
|
||||||
|
const val = unref(currentValueRef);
|
||||||
|
|
||||||
|
let value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
|
||||||
|
|
||||||
|
if (component === 'NDatePicker') {
|
||||||
|
value = dayjs(value).valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEvent: any = editComponent ? EventEnum[editComponent] : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
placeholder: createPlaceholderMessage(unref(getComponent)),
|
||||||
|
...apiSelectProps,
|
||||||
|
...omit(compProps, 'onChange'),
|
||||||
|
[onEvent]: handleChange,
|
||||||
|
[valueField]: value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getValues = computed(() => {
|
||||||
|
const { editComponentProps, editValueMap } = props.column;
|
||||||
|
|
||||||
|
const value = unref(currentValueRef);
|
||||||
|
|
||||||
|
if (editValueMap && isFunction(editValueMap)) {
|
||||||
|
return editValueMap(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const component = unref(getComponent);
|
||||||
|
if (!component.includes('NSelect')) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: LabelValueOptions = editComponentProps?.options ?? (unref(optionsRef) || []);
|
||||||
|
const option = options.find((item) => `${item.value}` === `${value}`);
|
||||||
|
|
||||||
|
return option?.label ?? value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getWrapperStyle = computed((): CSSProperties => {
|
||||||
|
// if (unref(getIsCheckComp) || unref(getRowEditable)) {
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
return {
|
||||||
|
width: 'calc(100% - 48px)',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getWrapperClass = computed(() => {
|
||||||
|
const { align = 'center' } = props.column;
|
||||||
|
return `edit-cell-align-${align}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getRowEditable = computed(() => {
|
||||||
|
const { editable } = props.record || {};
|
||||||
|
return !!editable;
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
defaultValueRef.value = props.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
const { editable } = props.column;
|
||||||
|
if (isBoolean(editable) || isBoolean(unref(getRowEditable))) {
|
||||||
|
isEdit.value = !!editable || unref(getRowEditable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleEdit() {
|
||||||
|
if (unref(getRowEditable) || unref(props.column?.editRow)) return;
|
||||||
|
ruleMessage.value = '';
|
||||||
|
isEdit.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
const el = unref(elRef);
|
||||||
|
el?.focus?.();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleChange(e: any) {
|
||||||
|
const component = unref(getComponent);
|
||||||
|
if (!e) {
|
||||||
|
currentValueRef.value = e;
|
||||||
|
} else if (e?.target && Reflect.has(e.target, 'value')) {
|
||||||
|
currentValueRef.value = (e as ChangeEvent).target.value;
|
||||||
|
} else if (component === 'NCheckbox') {
|
||||||
|
currentValueRef.value = (e as ChangeEvent).target.checked;
|
||||||
|
} else if (isString(e) || isBoolean(e) || isNumber(e)) {
|
||||||
|
currentValueRef.value = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO 这里组件参数格式,和dayjs格式不一致
|
||||||
|
if (component === 'NDatePicker') {
|
||||||
|
let format = (props.column.editComponentProps?.format)
|
||||||
|
.replace(/yyyy/g, 'YYYY')
|
||||||
|
.replace(/dd/g, 'DD');
|
||||||
|
currentValueRef.value = dayjs(currentValueRef.value).format(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = props.column?.editComponentProps?.onChange;
|
||||||
|
if (onChange && isFunction(onChange)) onChange(...arguments);
|
||||||
|
|
||||||
|
table.emit?.('edit-change', {
|
||||||
|
column: props.column,
|
||||||
|
value: unref(currentValueRef),
|
||||||
|
record: toRaw(props.record),
|
||||||
|
});
|
||||||
|
await handleSubmiRule();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmiRule() {
|
||||||
|
const { column, record } = props;
|
||||||
|
const { editRule } = column;
|
||||||
|
const currentValue = unref(currentValueRef);
|
||||||
|
|
||||||
|
if (editRule) {
|
||||||
|
if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) {
|
||||||
|
ruleVisible.value = true;
|
||||||
|
const component = unref(getComponent);
|
||||||
|
ruleMessage.value = createPlaceholderMessage(component);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isFunction(editRule)) {
|
||||||
|
const res = await editRule(currentValue, record as Recordable);
|
||||||
|
if (!!res) {
|
||||||
|
ruleMessage.value = res;
|
||||||
|
ruleVisible.value = true;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ruleMessage.value = '';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ruleMessage.value = '';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSubmit(needEmit = true, valid = true) {
|
||||||
|
if (valid) {
|
||||||
|
const isPass = await handleSubmiRule();
|
||||||
|
if (!isPass) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { column, index, record } = props;
|
||||||
|
if (!record) return false;
|
||||||
|
const { key } = column;
|
||||||
|
const value = unref(currentValueRef);
|
||||||
|
if (!key) return;
|
||||||
|
|
||||||
|
const dataKey = key as string;
|
||||||
|
|
||||||
|
set(record, dataKey, value);
|
||||||
|
//const record = await table.updateTableData(index, dataKey, value);
|
||||||
|
needEmit && table.emit?.('edit-end', { record, index, key, value });
|
||||||
|
isEdit.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleEnter() {
|
||||||
|
if (props.column?.editRow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await handleSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel() {
|
||||||
|
isEdit.value = false;
|
||||||
|
currentValueRef.value = defaultValueRef.value;
|
||||||
|
const { column, index, record } = props;
|
||||||
|
const { key } = column;
|
||||||
|
ruleVisible.value = true;
|
||||||
|
ruleMessage.value = '';
|
||||||
|
table.emit?.('edit-cancel', {
|
||||||
|
record,
|
||||||
|
index,
|
||||||
|
key: key,
|
||||||
|
value: unref(currentValueRef),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClickOutside() {
|
||||||
|
if (props.column?.editable || unref(getRowEditable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const component = unref(getComponent);
|
||||||
|
|
||||||
|
if (component.includes('NInput')) {
|
||||||
|
handleCancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only ApiSelect
|
||||||
|
function handleOptionsChange(options: LabelValueOptions) {
|
||||||
|
optionsRef.value = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
|
||||||
|
if (props.record) {
|
||||||
|
/* eslint-disable */
|
||||||
|
isArray(props.record[cbs])
|
||||||
|
? props.record[cbs]?.push(handle)
|
||||||
|
: (props.record[cbs] = [handle]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.record) {
|
||||||
|
initCbs('submitCbs', handleSubmit);
|
||||||
|
initCbs('validCbs', handleSubmiRule);
|
||||||
|
initCbs('cancelCbs', handleCancel);
|
||||||
|
|
||||||
|
if (props.column.key) {
|
||||||
|
if (!props.record.editValueRefs) props.record.editValueRefs = {};
|
||||||
|
props.record.editValueRefs[props.column.key] = currentValueRef;
|
||||||
|
}
|
||||||
|
/* eslint-disable */
|
||||||
|
props.record.onCancelEdit = () => {
|
||||||
|
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn());
|
||||||
|
};
|
||||||
|
/* eslint-disable */
|
||||||
|
props.record.onSubmitEdit = async() => {
|
||||||
|
if (isArray(props.record?.submitCbs)) {
|
||||||
|
const validFns = (props.record?.validCbs || []).map((fn) => fn());
|
||||||
|
|
||||||
|
const res = await Promise.all(validFns);
|
||||||
|
|
||||||
|
const pass = res.every((item) => !!item);
|
||||||
|
|
||||||
|
if (!pass) return;
|
||||||
|
const submitFns = props.record?.submitCbs || [];
|
||||||
|
submitFns.forEach((fn) => fn(false, false));
|
||||||
|
table.emit?.('edit-row-end');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isEdit,
|
||||||
|
handleEdit,
|
||||||
|
currentValueRef,
|
||||||
|
handleSubmit,
|
||||||
|
handleChange,
|
||||||
|
handleCancel,
|
||||||
|
elRef,
|
||||||
|
getComponent,
|
||||||
|
getRule,
|
||||||
|
onClickOutside,
|
||||||
|
ruleMessage,
|
||||||
|
getRuleVisible,
|
||||||
|
getComponentProps,
|
||||||
|
handleOptionsChange,
|
||||||
|
getWrapperStyle,
|
||||||
|
getWrapperClass,
|
||||||
|
getRowEditable,
|
||||||
|
getValues,
|
||||||
|
handleEnter,
|
||||||
|
// getSize,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.editable-cell {
|
||||||
|
&-content {
|
||||||
|
position: relative;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
.edit-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
//position: absolute;
|
||||||
|
//top: 4px;
|
||||||
|
//right: 0;
|
||||||
|
display: none;
|
||||||
|
width: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.edit-icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
15
src/components/Table/src/components/editable/helper.ts
Normal file
15
src/components/Table/src/components/editable/helper.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { ComponentType } from '../../types/componentType';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 生成placeholder
|
||||||
|
*/
|
||||||
|
export function createPlaceholderMessage(component: ComponentType) {
|
||||||
|
if (component === 'NInput') return '请输入';
|
||||||
|
if (
|
||||||
|
['NPicker', 'NSelect', 'NCheckbox', 'NRadio', 'NSwitch', 'NDatePicker', 'NTimePicker'].includes(
|
||||||
|
component
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return '请选择';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
49
src/components/Table/src/components/editable/index.ts
Normal file
49
src/components/Table/src/components/editable/index.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type { BasicColumn } from '@/components/Table/src/types/table';
|
||||||
|
import { h, Ref } from 'vue';
|
||||||
|
|
||||||
|
import EditableCell from './EditableCell.vue';
|
||||||
|
|
||||||
|
export function renderEditCell(column: BasicColumn) {
|
||||||
|
return (record, index) => {
|
||||||
|
const _key = column.key;
|
||||||
|
const value = record[_key];
|
||||||
|
record.onEdit = async (edit: boolean, submit = false) => {
|
||||||
|
if (!submit) {
|
||||||
|
record.editable = edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!edit && submit) {
|
||||||
|
const res = await record.onSubmitEdit?.();
|
||||||
|
if (res) {
|
||||||
|
record.editable = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// cancel
|
||||||
|
if (!edit && !submit) {
|
||||||
|
record.onCancelEdit?.();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return h(EditableCell, {
|
||||||
|
value,
|
||||||
|
record,
|
||||||
|
column,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EditRecordRow<T = Recordable> = Partial<
|
||||||
|
{
|
||||||
|
onEdit: (editable: boolean, submit?: boolean) => Promise<boolean>;
|
||||||
|
editable: boolean;
|
||||||
|
onCancel: Fn;
|
||||||
|
onSubmit: Fn;
|
||||||
|
submitCbs: Fn[];
|
||||||
|
cancelCbs: Fn[];
|
||||||
|
validCbs: Fn[];
|
||||||
|
editValueRefs: Recordable<Ref>;
|
||||||
|
} & T
|
||||||
|
>;
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
table.getColumns().forEach((item) => {
|
table.getColumns().forEach((item) => {
|
||||||
newRet.push({ ...item });
|
newRet.push({ ...item });
|
||||||
});
|
});
|
||||||
return newRet;
|
return newRet.filter((item) => item.key != 'action' && item.title != '操作');
|
||||||
}
|
}
|
||||||
|
|
||||||
//重置
|
//重置
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw, h } from 'vue';
|
||||||
import type { BasicColumn, BasicTableProps } from '../types/table';
|
import type { BasicColumn, BasicTableProps } from '../types/table';
|
||||||
import { isEqual, cloneDeep } from 'lodash-es';
|
import { isEqual, cloneDeep } from 'lodash-es';
|
||||||
import { isArray, isString, isBoolean, isFunction } from '@/utils/is';
|
import { isArray, isString, isBoolean, isFunction } from '@/utils/is';
|
||||||
import { usePermission } from '@/hooks/web/usePermission';
|
import { usePermission } from '@/hooks/web/usePermission';
|
||||||
import { ActionItem } from '@/components/Table';
|
import { ActionItem } from '@/components/Table';
|
||||||
|
import { renderEditCell } from '../components/editable';
|
||||||
|
import { NTooltip, NIcon } from 'naive-ui';
|
||||||
|
import { FormOutlined } from '@vicons/antd';
|
||||||
|
|
||||||
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||||
@@ -33,13 +36,48 @@ export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
|||||||
return isIfShow;
|
return isIfShow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const renderTooltip = (trigger, content) => {
|
||||||
|
return h(NTooltip, null, {
|
||||||
|
trigger: () => trigger,
|
||||||
|
default: () => content,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getPageColumns = computed(() => {
|
const getPageColumns = computed(() => {
|
||||||
const pageColumns = unref(getColumnsRef);
|
const pageColumns = unref(getColumnsRef);
|
||||||
const columns = cloneDeep(pageColumns);
|
const columns = cloneDeep(pageColumns);
|
||||||
return columns.filter((column) => {
|
return columns
|
||||||
// @ts-ignore
|
.filter((column) => {
|
||||||
return hasPermission(column.auth) && isIfShow(column);
|
// @ts-ignore
|
||||||
});
|
return hasPermission(column.auth) && isIfShow(column);
|
||||||
|
})
|
||||||
|
.map((column) => {
|
||||||
|
const { edit, editRow } = column;
|
||||||
|
if (edit) {
|
||||||
|
column.render = renderEditCell(column);
|
||||||
|
if (edit) {
|
||||||
|
const title: any = column.title;
|
||||||
|
column.title = () => {
|
||||||
|
return renderTooltip(
|
||||||
|
h('span', {}, [
|
||||||
|
h('span', { style: { 'margin-right': '5px' } }, title),
|
||||||
|
h(
|
||||||
|
NIcon,
|
||||||
|
{
|
||||||
|
size: 14,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => h(FormOutlined),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
'该列可编辑'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return column;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
|
import { propTypes } from '@/utils/propTypes';
|
||||||
import { BasicColumn } from './types/table';
|
import { BasicColumn } from './types/table';
|
||||||
|
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
@@ -16,7 +17,7 @@ export const basicProps = {
|
|||||||
},
|
},
|
||||||
tableData: {
|
tableData: {
|
||||||
type: [Object],
|
type: [Object],
|
||||||
default: () => {},
|
default: () => [],
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
type: [Array] as PropType<BasicColumn[]>,
|
type: [Array] as PropType<BasicColumn[]>,
|
||||||
@@ -36,6 +37,7 @@ export const basicProps = {
|
|||||||
type: [Object, Boolean],
|
type: [Object, Boolean],
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
|
//废弃
|
||||||
showPagination: {
|
showPagination: {
|
||||||
type: [String, Boolean],
|
type: [String, Boolean],
|
||||||
default: 'auto',
|
default: 'auto',
|
||||||
@@ -44,4 +46,6 @@ export const basicProps = {
|
|||||||
type: Object as PropType<BasicColumn>,
|
type: Object as PropType<BasicColumn>,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
canResize: propTypes.bool.def(true),
|
||||||
|
resizeHeightOffset: propTypes.number.def(0),
|
||||||
};
|
};
|
||||||
|
|||||||
8
src/components/Table/src/types/componentType.ts
Normal file
8
src/components/Table/src/types/componentType.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export type ComponentType =
|
||||||
|
| 'NInput'
|
||||||
|
| 'NInputNumber'
|
||||||
|
| 'NSelect'
|
||||||
|
| 'NCheckbox'
|
||||||
|
| 'NSwitch'
|
||||||
|
| 'NDatePicker'
|
||||||
|
| 'NTimePicker';
|
||||||
@@ -1,6 +1,20 @@
|
|||||||
import type { TableBaseColumn } from 'naive-ui/lib/data-table/src/interface';
|
import type { TableBaseColumn } from 'naive-ui/lib/data-table/src/interface';
|
||||||
|
import { ComponentType } from './componentType';
|
||||||
export type BasicColumn = TableBaseColumn;
|
export interface BasicColumn extends TableBaseColumn {
|
||||||
|
//编辑表格
|
||||||
|
edit?: boolean;
|
||||||
|
editRow?: boolean;
|
||||||
|
editable?: boolean;
|
||||||
|
editComponent?: ComponentType;
|
||||||
|
editComponentProps?: Recordable;
|
||||||
|
editRule?: boolean | ((text: string, record: Recordable) => Promise<string>);
|
||||||
|
editValueMap?: (value: any) => string;
|
||||||
|
onEditRow?: () => void;
|
||||||
|
// 权限编码控制是否显示
|
||||||
|
auth?: RoleEnum | RoleEnum[] | string | string[];
|
||||||
|
// 业务控制是否显示
|
||||||
|
ifShow?: boolean | ((column: BasicColumn) => boolean);
|
||||||
|
}
|
||||||
|
|
||||||
export interface TableActionType {
|
export interface TableActionType {
|
||||||
reload: (opt) => Promise<void>;
|
reload: (opt) => Promise<void>;
|
||||||
@@ -16,4 +30,6 @@ export interface BasicTableProps {
|
|||||||
pagination: object;
|
pagination: object;
|
||||||
showPagination: boolean;
|
showPagination: boolean;
|
||||||
actionColumn: any[];
|
actionColumn: any[];
|
||||||
|
canResize: boolean;
|
||||||
|
resizeHeightOffset: number;
|
||||||
}
|
}
|
||||||
|
|||||||
86
src/directives/clickOutside.ts
Normal file
86
src/directives/clickOutside.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { on } from '@/utils/domUtils';
|
||||||
|
import { isServer } from '@/utils/is';
|
||||||
|
import type { ComponentPublicInstance, DirectiveBinding, ObjectDirective } from 'vue';
|
||||||
|
|
||||||
|
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
|
||||||
|
|
||||||
|
type FlushList = Map<
|
||||||
|
HTMLElement,
|
||||||
|
{
|
||||||
|
documentHandler: DocumentHandler;
|
||||||
|
bindingFn: (...args: unknown[]) => unknown;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
const nodeList: FlushList = new Map();
|
||||||
|
|
||||||
|
let startClick: MouseEvent;
|
||||||
|
|
||||||
|
if (!isServer) {
|
||||||
|
on(document, 'mousedown', (e: MouseEvent) => (startClick = e));
|
||||||
|
on(document, 'mouseup', (e: MouseEvent) => {
|
||||||
|
for (const { documentHandler } of nodeList.values()) {
|
||||||
|
documentHandler(e, startClick);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDocumentHandler(el: HTMLElement, binding: DirectiveBinding): DocumentHandler {
|
||||||
|
let excludes: HTMLElement[] = [];
|
||||||
|
if (Array.isArray(binding.arg)) {
|
||||||
|
excludes = binding.arg;
|
||||||
|
} else {
|
||||||
|
// due to current implementation on binding type is wrong the type casting is necessary here
|
||||||
|
excludes.push(binding.arg as unknown as HTMLElement);
|
||||||
|
}
|
||||||
|
return function (mouseup, mousedown) {
|
||||||
|
const popperRef = (
|
||||||
|
binding.instance as ComponentPublicInstance<{
|
||||||
|
popperRef: Nullable<HTMLElement>;
|
||||||
|
}>
|
||||||
|
).popperRef;
|
||||||
|
const mouseUpTarget = mouseup.target as Node;
|
||||||
|
const mouseDownTarget = mousedown.target as Node;
|
||||||
|
const isBound = !binding || !binding.instance;
|
||||||
|
const isTargetExists = !mouseUpTarget || !mouseDownTarget;
|
||||||
|
const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget);
|
||||||
|
const isSelf = el === mouseUpTarget;
|
||||||
|
|
||||||
|
const isTargetExcluded =
|
||||||
|
(excludes.length && excludes.some((item) => item?.contains(mouseUpTarget))) ||
|
||||||
|
(excludes.length && excludes.includes(mouseDownTarget as HTMLElement));
|
||||||
|
const isContainedByPopper =
|
||||||
|
popperRef && (popperRef.contains(mouseUpTarget) || popperRef.contains(mouseDownTarget));
|
||||||
|
if (
|
||||||
|
isBound ||
|
||||||
|
isTargetExists ||
|
||||||
|
isContainedByEl ||
|
||||||
|
isSelf ||
|
||||||
|
isTargetExcluded ||
|
||||||
|
isContainedByPopper
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binding.value();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClickOutside: ObjectDirective = {
|
||||||
|
beforeMount(el, binding) {
|
||||||
|
nodeList.set(el, {
|
||||||
|
documentHandler: createDocumentHandler(el, binding),
|
||||||
|
bindingFn: binding.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updated(el, binding) {
|
||||||
|
nodeList.set(el, {
|
||||||
|
documentHandler: createDocumentHandler(el, binding),
|
||||||
|
bindingFn: binding.value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
nodeList.delete(el);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClickOutside;
|
||||||
36
src/hooks/event/useWindowSizeFn.ts
Normal file
36
src/hooks/event/useWindowSizeFn.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
|
||||||
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
|
interface WindowSizeOptions {
|
||||||
|
once?: boolean;
|
||||||
|
immediate?: boolean;
|
||||||
|
listenerOptions?: AddEventListenerOptions | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useWindowSizeFn<T>(fn: Fn<T>, wait = 150, options?: WindowSizeOptions) {
|
||||||
|
let handler = () => {
|
||||||
|
fn();
|
||||||
|
};
|
||||||
|
const handleSize = useDebounceFn(handler, wait);
|
||||||
|
handler = handleSize;
|
||||||
|
|
||||||
|
const start = () => {
|
||||||
|
if (options && options.immediate) {
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
window.removeEventListener('resize', handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
tryOnMounted(() => {
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
tryOnUnmounted(() => {
|
||||||
|
stop();
|
||||||
|
});
|
||||||
|
return [start, stop];
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.page-footer {
|
.page-footer {
|
||||||
margin: 48px 0 24px 0;
|
//margin: 28px 0 24px 0;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,18 @@
|
|||||||
<div class="drawer-setting-item justify-center dark-switch">
|
<div class="drawer-setting-item justify-center dark-switch">
|
||||||
<n-tooltip placement="bottom">
|
<n-tooltip placement="bottom">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-switch v-model:value="designStore.darkTheme" />
|
<n-switch v-model:value="designStore.darkTheme" class="dark-theme-switch">
|
||||||
|
<template #checked>
|
||||||
|
<n-icon size="14" color="#ffd93b">
|
||||||
|
<SunnySharp />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<template #unchecked>
|
||||||
|
<n-icon size="14" color="#ffd93b">
|
||||||
|
<Moon />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
</n-switch>
|
||||||
</template>
|
</template>
|
||||||
<span>深色主题</span>
|
<span>深色主题</span>
|
||||||
</n-tooltip>
|
</n-tooltip>
|
||||||
@@ -146,13 +157,13 @@
|
|||||||
<n-switch v-model:value="settingStore.multiTabsSetting.show" />
|
<n-switch v-model:value="settingStore.multiTabsSetting.show" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--1.15废弃,没啥用,占用操作空间-->
|
||||||
<div class="drawer-setting-item">
|
<!-- <div class="drawer-setting-item">-->
|
||||||
<div class="drawer-setting-item-title"> 显示页脚 </div>
|
<!-- <div class="drawer-setting-item-title"> 显示页脚 </div>-->
|
||||||
<div class="drawer-setting-item-action">
|
<!-- <div class="drawer-setting-item-action">-->
|
||||||
<n-switch v-model:value="settingStore.showFooter" />
|
<!-- <n-switch v-model:value="settingStore.showFooter" />-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
|
|
||||||
<div class="drawer-setting-item">
|
<div class="drawer-setting-item">
|
||||||
<n-alert type="warning" :showIcon="false">
|
<n-alert type="warning" :showIcon="false">
|
||||||
@@ -169,11 +180,12 @@
|
|||||||
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
import { useProjectSettingStore } from '@/store/modules/projectSetting';
|
||||||
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
import { useDesignSettingStore } from '@/store/modules/designSetting';
|
||||||
import { CheckOutlined } from '@vicons/antd';
|
import { CheckOutlined } from '@vicons/antd';
|
||||||
|
import { Moon, SunnySharp } from '@vicons/ionicons5';
|
||||||
import { darkTheme } from 'naive-ui';
|
import { darkTheme } from 'naive-ui';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ProjectSetting',
|
name: 'ProjectSetting',
|
||||||
components: { CheckOutlined },
|
components: { CheckOutlined, Moon, SunnySharp },
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -300,8 +312,7 @@
|
|||||||
.justify-center {
|
.justify-center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
.dark-switch .n-switch {
|
||||||
.dark-switch .n-switch--active {
|
|
||||||
::v-deep(.n-switch__rail) {
|
::v-deep(.n-switch__rail) {
|
||||||
background-color: #000e1c;
|
background-color: #000e1c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<div class="layout-header-trigger layout-header-trigger-min">
|
<div class="layout-header-trigger layout-header-trigger-min">
|
||||||
<n-dropdown trigger="hover" @select="avatarSelect" :options="avatarOptions">
|
<n-dropdown trigger="hover" @select="avatarSelect" :options="avatarOptions">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<n-avatar>
|
<n-avatar round>
|
||||||
{{ username }}
|
{{ username }}
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<UserOutlined />
|
<UserOutlined />
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
:collapsed="collapsed"
|
:collapsed="collapsed"
|
||||||
:collapsed-width="64"
|
:collapsed-width="64"
|
||||||
:collapsed-icon-size="20"
|
:collapsed-icon-size="20"
|
||||||
|
:indent="28"
|
||||||
:expanded-keys="openKeys"
|
:expanded-keys="openKeys"
|
||||||
v-model:value="selectedKeys"
|
v-model:value="selectedKeys"
|
||||||
@update:value="clickMenuItem"
|
@update:value="clickMenuItem"
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, computed, watch, toRefs } from 'vue';
|
import { defineComponent, reactive, computed, watch, toRefs, unref } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
import { generatorMenu } from '@/utils/index';
|
import { generatorMenu } from '@/utils/index';
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
// 获取当前打开的子菜单
|
// 获取当前打开的子菜单
|
||||||
const matched = currentRoute.matched;
|
const matched = currentRoute.matched;
|
||||||
|
|
||||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : [];
|
const getOpenKeys = matched && matched.length ? matched.map((item) => item.name) : [];
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
openKeys: getOpenKeys,
|
openKeys: getOpenKeys,
|
||||||
@@ -73,8 +74,7 @@
|
|||||||
() => currentRoute.fullPath,
|
() => currentRoute.fullPath,
|
||||||
() => {
|
() => {
|
||||||
const matched = currentRoute.matched;
|
const matched = currentRoute.matched;
|
||||||
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : [];
|
state.openKeys = matched.map((item) => item.name);
|
||||||
state.openKeys = getOpenKeys;
|
|
||||||
state.selectedKeys = currentRoute.name;
|
state.selectedKeys = currentRoute.name;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -90,10 +90,22 @@
|
|||||||
|
|
||||||
//展开菜单
|
//展开菜单
|
||||||
function menuExpanded(openKeys: string[]) {
|
function menuExpanded(openKeys: string[]) {
|
||||||
console.log(openKeys);
|
|
||||||
if (!openKeys) return;
|
if (!openKeys) return;
|
||||||
const latestOpenKey = openKeys.pop();
|
const latestOpenKey = openKeys.find((key) => state.openKeys.indexOf(key) === -1);
|
||||||
state.openKeys = latestOpenKey ? [latestOpenKey] : [];
|
const isExistChildren = findChildrenLen(latestOpenKey as string);
|
||||||
|
state.openKeys = isExistChildren ? (latestOpenKey ? [latestOpenKey] : []) : openKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
//查找是否存在子路由
|
||||||
|
function findChildrenLen(key: string) {
|
||||||
|
if (!key) return false;
|
||||||
|
const subRouteChildren: string[] = [];
|
||||||
|
for (const { children, key } of unref(menus)) {
|
||||||
|
if (children && children.length > 0) {
|
||||||
|
subRouteChildren.push(key as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subRouteChildren.includes(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -46,10 +46,10 @@
|
|||||||
<MainView />
|
<MainView />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!--1.15废弃,没啥用,占用操作空间-->
|
||||||
<NLayoutFooter v-if="getShowFooter">
|
<!-- <NLayoutFooter v-if="getShowFooter">-->
|
||||||
<PageFooter />
|
<!-- <PageFooter />-->
|
||||||
</NLayoutFooter>
|
<!-- </NLayoutFooter>-->
|
||||||
</NLayoutContent>
|
</NLayoutContent>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
</NLayout>
|
</NLayout>
|
||||||
|
|||||||
3
src/layout/parentLayout.vue
Normal file
3
src/layout/parentLayout.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
14
src/main.ts
14
src/main.ts
@@ -5,9 +5,12 @@ import router, { setupRouter } from './router';
|
|||||||
import { setupStore } from '@/store';
|
import { setupStore } from '@/store';
|
||||||
import MakeitCaptcha from 'makeit-captcha';
|
import MakeitCaptcha from 'makeit-captcha';
|
||||||
import 'makeit-captcha/dist/captcha.min.css';
|
import 'makeit-captcha/dist/captcha.min.css';
|
||||||
import { setupNaive, setupDirectives, setupGlobalMethods, setupCustomComponents } from '@/plugins';
|
import { setupNaive, setupDirectives } from '@/plugins';
|
||||||
|
import { AppProvider } from '@/components/Application';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
|
const appProvider = createApp(AppProvider);
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(MakeitCaptcha);
|
app.use(MakeitCaptcha);
|
||||||
@@ -15,18 +18,21 @@ async function bootstrap() {
|
|||||||
// 注册全局常用的 naive-ui 组件
|
// 注册全局常用的 naive-ui 组件
|
||||||
setupNaive(app);
|
setupNaive(app);
|
||||||
|
|
||||||
// 注册全局自定义组件,如:<svg-icon />
|
// 注册全局自定义组件
|
||||||
setupCustomComponents(app);
|
//setupCustomComponents();
|
||||||
|
|
||||||
// 注册全局自定义指令,如:v-permission权限指令
|
// 注册全局自定义指令,如:v-permission权限指令
|
||||||
setupDirectives(app);
|
setupDirectives(app);
|
||||||
|
|
||||||
// 注册全局方法,如:app.config.globalProperties.$message = message
|
// 注册全局方法,如:app.config.globalProperties.$message = message
|
||||||
setupGlobalMethods(app);
|
//setupGlobalMethods(app);
|
||||||
|
|
||||||
// 挂载状态管理
|
// 挂载状态管理
|
||||||
setupStore(app);
|
setupStore(app);
|
||||||
|
|
||||||
|
//优先挂载一下 Provider 解决路由守卫,Axios中可使用,Dialog,Message 等之类组件
|
||||||
|
appProvider.mount('#appProvider', true);
|
||||||
|
|
||||||
// 挂载路由
|
// 挂载路由
|
||||||
await setupRouter(app);
|
await setupRouter(app);
|
||||||
|
|
||||||
|
|||||||
@@ -3,3 +3,5 @@ export const RedirectName = 'Redirect';
|
|||||||
export const ErrorPage = () => import('@/views/exception/404.vue');
|
export const ErrorPage = () => import('@/views/exception/404.vue');
|
||||||
|
|
||||||
export const Layout = () => import('@/layout/index.vue');
|
export const Layout = () => import('@/layout/index.vue');
|
||||||
|
|
||||||
|
export const ParentLayout = () => import('@/layout/parentLayout.vue');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import { Layout } from '@/router/constant';
|
import { Layout, ParentLayout } from '@/router/constant';
|
||||||
import { WalletOutlined } from '@vicons/antd';
|
import { WalletOutlined } from '@vicons/antd';
|
||||||
import { renderIcon } from '@/utils/index';
|
import { renderIcon } from '@/utils/index';
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
{
|
{
|
||||||
path: '/comp',
|
path: '/comp',
|
||||||
name: routeName,
|
name: routeName,
|
||||||
redirect: '/comp/console',
|
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
redirect: '/comp/table',
|
||||||
meta: {
|
meta: {
|
||||||
title: '组件示例',
|
title: '组件示例',
|
||||||
icon: renderIcon(WalletOutlined),
|
icon: renderIcon(WalletOutlined),
|
||||||
@@ -31,10 +31,37 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
{
|
{
|
||||||
path: 'table',
|
path: 'table',
|
||||||
name: `${routeName}_table`,
|
name: `${routeName}_table`,
|
||||||
|
redirect: '/comp/table/basic',
|
||||||
|
component: ParentLayout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '表格',
|
title: '表格',
|
||||||
},
|
},
|
||||||
component: () => import('@/views/comp/table/list.vue'),
|
children: [
|
||||||
|
{
|
||||||
|
path: 'basic',
|
||||||
|
name: `${routeName}_table_basic`,
|
||||||
|
meta: {
|
||||||
|
title: '基础表格',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/comp/table/basic.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'editCell',
|
||||||
|
name: `${routeName}_table_editCell`,
|
||||||
|
meta: {
|
||||||
|
title: '单元格编辑',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/comp/table/editCell.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'editRow',
|
||||||
|
name: `${routeName}_table_editRow`,
|
||||||
|
meta: {
|
||||||
|
title: '整行编辑',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/comp/table/editRow.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'upload',
|
path: 'upload',
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { isNavigationFailure, Router } from 'vue-router';
|
import { isNavigationFailure, Router } from 'vue-router';
|
||||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
||||||
import NProgress from 'nprogress'; // progress bar
|
|
||||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
||||||
import { storage } from '@/utils/Storage';
|
import { storage } from '@/utils/Storage';
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false }); // NProgress Configuration
|
|
||||||
|
|
||||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||||
|
|
||||||
const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
||||||
@@ -15,8 +12,9 @@ const whitePathList = [LOGIN_PATH]; // no redirect whitelist
|
|||||||
export function createRouterGuards(router: Router) {
|
export function createRouterGuards(router: Router) {
|
||||||
const userStore = useUserStoreWidthOut();
|
const userStore = useUserStoreWidthOut();
|
||||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||||
|
const Loading = window['$loading'] || null;
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.start();
|
Loading && Loading.start();
|
||||||
if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
||||||
next(PageEnum.BASE_HOME);
|
next(PageEnum.BASE_HOME);
|
||||||
return;
|
return;
|
||||||
@@ -70,7 +68,7 @@ export function createRouterGuards(router: Router) {
|
|||||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||||
asyncRouteStore.setDynamicAddedRoute(true);
|
asyncRouteStore.setDynamicAddedRoute(true);
|
||||||
next(nextData);
|
next(nextData);
|
||||||
NProgress.done();
|
Loading && Loading.finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to, _, failure) => {
|
router.afterEach((to, _, failure) => {
|
||||||
@@ -93,7 +91,7 @@ export function createRouterGuards(router: Router) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
asyncRouteStore.setKeepAliveComponents(keepAliveComponents);
|
asyncRouteStore.setKeepAliveComponents(keepAliveComponents);
|
||||||
NProgress.done(); // finish progress bar
|
Loading && Loading.finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.onError((error) => {
|
router.onError((error) => {
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ export const appThemeList: string[] = [
|
|||||||
'#0096c7',
|
'#0096c7',
|
||||||
'#9c27b0',
|
'#9c27b0',
|
||||||
'#ff9800',
|
'#ff9800',
|
||||||
|
'#FF3D68',
|
||||||
|
'#00C1D4',
|
||||||
|
'#71EFA3',
|
||||||
|
'#171010',
|
||||||
|
'#78DEC7',
|
||||||
|
'#1768AC',
|
||||||
|
'#FB9300',
|
||||||
|
'#FC5404',
|
||||||
];
|
];
|
||||||
|
|
||||||
const setting = {
|
const setting = {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
@primaryColor: #2d8cf0;
|
|
||||||
@primaryColorHover: 57 a3f3;
|
|
||||||
@header-height: 64px;
|
|
||||||
@footer-height: 70px;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
body {
|
|
||||||
.ant-message {
|
|
||||||
z-index: 999999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
165
src/utils/domUtils.ts
Normal file
165
src/utils/domUtils.ts
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
import { upperFirst } from 'lodash-es';
|
||||||
|
|
||||||
|
export interface ViewportOffsetResult {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
rightIncludeBody: number;
|
||||||
|
bottomIncludeBody: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBoundingClientRect(element: Element): DOMRect | number {
|
||||||
|
if (!element || !element.getBoundingClientRect) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return element.getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
function trim(string: string) {
|
||||||
|
return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function hasClass(el: Element, cls: string) {
|
||||||
|
if (!el || !cls) return false;
|
||||||
|
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
|
||||||
|
if (el.classList) {
|
||||||
|
return el.classList.contains(cls);
|
||||||
|
} else {
|
||||||
|
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function addClass(el: Element, cls: string) {
|
||||||
|
if (!el) return;
|
||||||
|
let curClass = el.className;
|
||||||
|
const classes = (cls || '').split(' ');
|
||||||
|
|
||||||
|
for (let i = 0, j = classes.length; i < j; i++) {
|
||||||
|
const clsName = classes[i];
|
||||||
|
if (!clsName) continue;
|
||||||
|
|
||||||
|
if (el.classList) {
|
||||||
|
el.classList.add(clsName);
|
||||||
|
} else if (!hasClass(el, clsName)) {
|
||||||
|
curClass += ' ' + clsName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!el.classList) {
|
||||||
|
el.className = curClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function removeClass(el: Element, cls: string) {
|
||||||
|
if (!el || !cls) return;
|
||||||
|
const classes = cls.split(' ');
|
||||||
|
let curClass = ' ' + el.className + ' ';
|
||||||
|
|
||||||
|
for (let i = 0, j = classes.length; i < j; i++) {
|
||||||
|
const clsName = classes[i];
|
||||||
|
if (!clsName) continue;
|
||||||
|
|
||||||
|
if (el.classList) {
|
||||||
|
el.classList.remove(clsName);
|
||||||
|
} else if (hasClass(el, clsName)) {
|
||||||
|
curClass = curClass.replace(' ' + clsName + ' ', ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!el.classList) {
|
||||||
|
el.className = trim(curClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the left and top offset of the current element
|
||||||
|
* left: the distance between the leftmost element and the left side of the document
|
||||||
|
* top: the distance from the top of the element to the top of the document
|
||||||
|
* right: the distance from the far right of the element to the right of the document
|
||||||
|
* bottom: the distance from the bottom of the element to the bottom of the document
|
||||||
|
* rightIncludeBody: the distance between the leftmost element and the right side of the document
|
||||||
|
* bottomIncludeBody: the distance from the bottom of the element to the bottom of the document
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
export function getViewportOffset(element: Element): ViewportOffsetResult {
|
||||||
|
const doc = document.documentElement;
|
||||||
|
|
||||||
|
const docScrollLeft = doc.scrollLeft;
|
||||||
|
const docScrollTop = doc.scrollTop;
|
||||||
|
const docClientLeft = doc.clientLeft;
|
||||||
|
const docClientTop = doc.clientTop;
|
||||||
|
|
||||||
|
const pageXOffset = window.pageXOffset;
|
||||||
|
const pageYOffset = window.pageYOffset;
|
||||||
|
|
||||||
|
const box = getBoundingClientRect(element);
|
||||||
|
|
||||||
|
const { left: retLeft, top: rectTop, width: rectWidth, height: rectHeight } = box as DOMRect;
|
||||||
|
|
||||||
|
const scrollLeft = (pageXOffset || docScrollLeft) - (docClientLeft || 0);
|
||||||
|
const scrollTop = (pageYOffset || docScrollTop) - (docClientTop || 0);
|
||||||
|
const offsetLeft = retLeft + pageXOffset;
|
||||||
|
const offsetTop = rectTop + pageYOffset;
|
||||||
|
|
||||||
|
const left = offsetLeft - scrollLeft;
|
||||||
|
const top = offsetTop - scrollTop;
|
||||||
|
|
||||||
|
const clientWidth = window.document.documentElement.clientWidth;
|
||||||
|
const clientHeight = window.document.documentElement.clientHeight;
|
||||||
|
return {
|
||||||
|
left: left,
|
||||||
|
top: top,
|
||||||
|
right: clientWidth - rectWidth - left,
|
||||||
|
bottom: clientHeight - rectHeight - top,
|
||||||
|
rightIncludeBody: clientWidth - left,
|
||||||
|
bottomIncludeBody: clientHeight - top,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hackCss(attr: string, value: string) {
|
||||||
|
const prefix: string[] = ['webkit', 'Moz', 'ms', 'OT'];
|
||||||
|
|
||||||
|
const styleObj: any = {};
|
||||||
|
prefix.forEach((item) => {
|
||||||
|
styleObj[`${item}${upperFirst(attr)}`] = value;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...styleObj,
|
||||||
|
[attr]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function on(
|
||||||
|
element: Element | HTMLElement | Document | Window,
|
||||||
|
event: string,
|
||||||
|
handler: EventListenerOrEventListenerObject
|
||||||
|
): void {
|
||||||
|
if (element && event && handler) {
|
||||||
|
element.addEventListener(event, handler, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function off(
|
||||||
|
element: Element | HTMLElement | Document | Window,
|
||||||
|
event: string,
|
||||||
|
handler: Fn
|
||||||
|
): void {
|
||||||
|
if (element && event && handler) {
|
||||||
|
element.removeEventListener(event, handler, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
export function once(el: HTMLElement, event: string, fn: EventListener): void {
|
||||||
|
const listener = function (this: any, ...args: unknown[]) {
|
||||||
|
if (fn) {
|
||||||
|
fn.apply(this, args);
|
||||||
|
}
|
||||||
|
off(el, event, listener);
|
||||||
|
};
|
||||||
|
on(el, event, listener);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
export function checkStatus(status: number, msg: string): void {
|
export function checkStatus(status: number, msg: string, message: any): void {
|
||||||
const message = window['$message'];
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 400:
|
case 400:
|
||||||
message.error(`${msg}`);
|
message.error(`${msg}`);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
// axios配置 可自行根据项目进行更改,只需更改该文件即可,其他文件可以不动
|
||||||
|
|
||||||
import { VAxios } from './Axios';
|
import { VAxios } from './Axios';
|
||||||
import { AxiosTransform } from './axiosTransform';
|
import { AxiosTransform } from './axiosTransform';
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
@@ -18,8 +17,6 @@ import { useUserStoreWidthOut } from '@/store/modules/user';
|
|||||||
|
|
||||||
const globSetting = useGlobSetting();
|
const globSetting = useGlobSetting();
|
||||||
const urlPrefix = globSetting.urlPrefix || '';
|
const urlPrefix = globSetting.urlPrefix || '';
|
||||||
// @ts-ignore
|
|
||||||
const { $message: Message, $dialog: Modal } = window;
|
|
||||||
|
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { storage } from '@/utils/Storage';
|
import { storage } from '@/utils/Storage';
|
||||||
@@ -32,18 +29,35 @@ const transform: AxiosTransform = {
|
|||||||
* @description: 处理请求数据
|
* @description: 处理请求数据
|
||||||
*/
|
*/
|
||||||
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
|
transformRequestData: (res: AxiosResponse<Result>, options: RequestOptions) => {
|
||||||
|
const { $message: Message, $dialog: Modal } = window;
|
||||||
const {
|
const {
|
||||||
isTransformRequestResult,
|
|
||||||
isShowMessage = true,
|
isShowMessage = true,
|
||||||
isShowErrorMessage,
|
isShowErrorMessage,
|
||||||
isShowSuccessMessage,
|
isShowSuccessMessage,
|
||||||
successMessageText,
|
successMessageText,
|
||||||
errorMessageText,
|
errorMessageText,
|
||||||
|
isTransformResponse,
|
||||||
|
isReturnNativeResponse,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||||
|
if (isReturnNativeResponse) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
// 不进行任何处理,直接返回
|
||||||
|
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||||
|
if (!isTransformResponse) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
|
||||||
const reject = Promise.reject;
|
const reject = Promise.reject;
|
||||||
|
|
||||||
const { data } = res;
|
const { data } = res;
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
// return '[HTTP] Request has no return value';
|
||||||
|
return reject(data);
|
||||||
|
}
|
||||||
// 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
|
// 这里 code,result,message为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
|
||||||
const { code, result, message } = data;
|
const { code, result, message } = data;
|
||||||
// 请求成功
|
// 请求成功
|
||||||
@@ -58,19 +72,14 @@ const transform: AxiosTransform = {
|
|||||||
Message.error(message || errorMessageText || '操作失败!');
|
Message.error(message || errorMessageText || '操作失败!');
|
||||||
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||||
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||||
Modal.confirm({ title: '错误提示', content: message });
|
Modal.info({
|
||||||
|
title: '提示',
|
||||||
|
content: message,
|
||||||
|
positiveText: '确定',
|
||||||
|
onPositiveClick: () => {},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 不进行任何处理,直接返回
|
|
||||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
|
||||||
if (!isTransformRequestResult) {
|
|
||||||
return res.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
// return '[HTTP] Request has no return value';
|
|
||||||
return reject(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接口请求成功,直接返回结果
|
// 接口请求成功,直接返回结果
|
||||||
if (code === ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
@@ -94,19 +103,21 @@ const transform: AxiosTransform = {
|
|||||||
if (router.currentRoute.value.name == 'login') return;
|
if (router.currentRoute.value.name == 'login') return;
|
||||||
// 到登录页
|
// 到登录页
|
||||||
const timeoutMsg = '登录超时,请重新登录!';
|
const timeoutMsg = '登录超时,请重新登录!';
|
||||||
Modal.destroyAll();
|
|
||||||
Modal.warning({
|
Modal.warning({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '登录身份已失效,请重新登录!',
|
content: '登录身份已失效,请重新登录!',
|
||||||
onOk: () => {
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
storage.clear();
|
||||||
router.replace({
|
router.replace({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
query: {
|
query: {
|
||||||
redirect: router.currentRoute.value.fullPath,
|
redirect: router.currentRoute.value.fullPath,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
storage.clear();
|
|
||||||
},
|
},
|
||||||
|
onNegativeClick: () => {},
|
||||||
});
|
});
|
||||||
return reject(new Error(timeoutMsg));
|
return reject(new Error(timeoutMsg));
|
||||||
}
|
}
|
||||||
@@ -175,9 +186,11 @@ const transform: AxiosTransform = {
|
|||||||
* @description: 响应错误处理
|
* @description: 响应错误处理
|
||||||
*/
|
*/
|
||||||
responseInterceptorsCatch: (error: any) => {
|
responseInterceptorsCatch: (error: any) => {
|
||||||
|
const { $message: Message, $dialog: Modal } = window;
|
||||||
const { response, code, message } = error || {};
|
const { response, code, message } = error || {};
|
||||||
|
// TODO 此处要根据后端接口返回格式修改
|
||||||
const msg: string =
|
const msg: string =
|
||||||
response && response.data && response.data.error ? response.data.error.message : '';
|
response && response.data && response.data.message ? response.data.message : '';
|
||||||
const err: string = error.toString();
|
const err: string = error.toString();
|
||||||
try {
|
try {
|
||||||
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
|
||||||
@@ -185,9 +198,11 @@ const transform: AxiosTransform = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (err && err.includes('Network Error')) {
|
if (err && err.includes('Network Error')) {
|
||||||
Modal.confirm({
|
Modal.info({
|
||||||
title: '网络异常',
|
title: '网络异常',
|
||||||
content: '请检查您的网络连接是否正常!',
|
content: '请检查您的网络连接是否正常!',
|
||||||
|
positiveText: '确定',
|
||||||
|
onPositiveClick: () => {},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -197,7 +212,7 @@ const transform: AxiosTransform = {
|
|||||||
// 请求是否被取消
|
// 请求是否被取消
|
||||||
const isCancel = axios.isCancel(error);
|
const isCancel = axios.isCancel(error);
|
||||||
if (!isCancel) {
|
if (!isCancel) {
|
||||||
checkStatus(error.response && error.response.status, msg);
|
checkStatus(error.response && error.response.status, msg, Message);
|
||||||
} else {
|
} else {
|
||||||
console.warn(error, '请求被取消!');
|
console.warn(error, '请求被取消!');
|
||||||
}
|
}
|
||||||
@@ -206,20 +221,20 @@ const transform: AxiosTransform = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Axios = new VAxios({
|
const Axios = new VAxios({
|
||||||
timeout: 15 * 1000,
|
timeout: 10 * 1000,
|
||||||
// 基础接口地址
|
// 接口前缀
|
||||||
// baseURL: globSetting.apiUrl,
|
prefixUrl: urlPrefix,
|
||||||
// 接口可能会有通用的地址部分,可以统一抽取出来
|
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||||
// prefixUrl: prefix,
|
|
||||||
headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
|
|
||||||
// 数据处理方式
|
// 数据处理方式
|
||||||
transform,
|
transform,
|
||||||
// 配置项,下面的选项都可以在独立的接口请求中覆盖
|
// 配置项,下面的选项都可以在独立的接口请求中覆盖
|
||||||
requestOptions: {
|
requestOptions: {
|
||||||
// 默认将prefix 添加到url
|
// 默认将prefix 添加到url
|
||||||
joinPrefix: true,
|
joinPrefix: true,
|
||||||
|
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
|
||||||
|
isReturnNativeResponse: false,
|
||||||
// 需要对返回数据进行处理
|
// 需要对返回数据进行处理
|
||||||
isTransformRequestResult: true,
|
isTransformResponse: true,
|
||||||
// post请求的时候添加参数到url
|
// post请求的时候添加参数到url
|
||||||
joinParamsToUrl: false,
|
joinParamsToUrl: false,
|
||||||
// 格式化提交参数时间
|
// 格式化提交参数时间
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export interface RequestOptions {
|
|||||||
// 格式化请求参数时间
|
// 格式化请求参数时间
|
||||||
formatDate?: boolean;
|
formatDate?: boolean;
|
||||||
// 是否处理请求结果
|
// 是否处理请求结果
|
||||||
isTransformRequestResult?: boolean;
|
isTransformResponse?: boolean;
|
||||||
// 是否显示提示信息
|
// 是否显示提示信息
|
||||||
isShowMessage?: boolean;
|
isShowMessage?: boolean;
|
||||||
// 是否解析成JSON
|
// 是否解析成JSON
|
||||||
@@ -34,6 +34,10 @@ export interface RequestOptions {
|
|||||||
errorMessageMode?: 'none' | 'modal';
|
errorMessageMode?: 'none' | 'modal';
|
||||||
// 是否添加时间戳
|
// 是否添加时间戳
|
||||||
joinTime?: boolean;
|
joinTime?: boolean;
|
||||||
|
// 不进行任何处理,直接返回
|
||||||
|
isTransformResponse?: boolean;
|
||||||
|
// 是否返回原生响应头
|
||||||
|
isReturnNativeResponse?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Result<T = any> {
|
export interface Result<T = any> {
|
||||||
|
|||||||
75
src/views/comp/table/CellColumns.ts
Normal file
75
src/views/comp/table/CellColumns.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { h } from 'vue';
|
||||||
|
import { NAvatar } from 'naive-ui';
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '编码',
|
||||||
|
key: 'no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
key: 'name',
|
||||||
|
editComponent: 'NInput',
|
||||||
|
// 默认必填校验
|
||||||
|
editRule: true,
|
||||||
|
edit: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '头像',
|
||||||
|
key: 'avatar',
|
||||||
|
render(row) {
|
||||||
|
return h(NAvatar, {
|
||||||
|
size: 48,
|
||||||
|
src: row.avatar,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '地址',
|
||||||
|
key: 'address',
|
||||||
|
editComponent: 'NSelect',
|
||||||
|
editComponentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '广东省',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '浙江省',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
edit: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始日期',
|
||||||
|
key: 'beginTime',
|
||||||
|
edit: true,
|
||||||
|
width: 250,
|
||||||
|
editComponent: 'NDatePicker',
|
||||||
|
editComponentProps: {
|
||||||
|
type: 'datetime',
|
||||||
|
format: 'yyyy-MM-dd HH:mm:ss',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束日期',
|
||||||
|
key: 'endTime',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '停留时间',
|
||||||
|
key: 'time',
|
||||||
|
},
|
||||||
|
];
|
||||||
121
src/views/comp/table/basic.vue
Normal file
121
src/views/comp/table/basic.vue
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<template>
|
||||||
|
<n-card :bordered="false" class="proCard">
|
||||||
|
<BasicTable
|
||||||
|
title="表格列表"
|
||||||
|
titleTooltip="这是一个提示"
|
||||||
|
:columns="columns"
|
||||||
|
:request="loadDataTable"
|
||||||
|
:row-key="(row) => row.id"
|
||||||
|
ref="actionRef"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
|
@update:checked-row-keys="onCheckedRow"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</n-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs, ref, h } from 'vue';
|
||||||
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
|
import { getTableList } from '@/api/table/list';
|
||||||
|
import { columns } from './basicColumns';
|
||||||
|
import { useDialog, useMessage } from 'naive-ui';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicTable },
|
||||||
|
setup() {
|
||||||
|
const message = useMessage();
|
||||||
|
const dialog = useDialog();
|
||||||
|
const actionRef = ref();
|
||||||
|
const state = reactive({
|
||||||
|
params: {
|
||||||
|
pageSize: 5,
|
||||||
|
name: 'xiaoMa',
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 150,
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction, {
|
||||||
|
style: 'button',
|
||||||
|
actions: createActions(record),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function createActions(record) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
icon: 'ic:outline-delete-outline',
|
||||||
|
onClick: handleDelete.bind(null, record),
|
||||||
|
// 根据业务控制是否显示 isShow 和 auth 是并且关系
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 根据权限控制是否显示: 有权限,会显示,支持多个
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
ifShow: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
auth: ['basic_list'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDataTable = async (params) => {
|
||||||
|
const data = await getTableList(params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function onCheckedRow(rowKeys) {
|
||||||
|
console.log(rowKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
actionRef.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(record) {
|
||||||
|
console.log(record);
|
||||||
|
dialog.info({
|
||||||
|
title: '提示',
|
||||||
|
content: `您想删除${record.name}`,
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
message.success('删除成功');
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(record) {
|
||||||
|
console.log(record);
|
||||||
|
message.success('您点击了编辑按钮');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
columns,
|
||||||
|
actionRef,
|
||||||
|
loadDataTable,
|
||||||
|
onCheckedRow,
|
||||||
|
reloadTable,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
66
src/views/comp/table/basicColumns.ts
Normal file
66
src/views/comp/table/basicColumns.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { h } from 'vue';
|
||||||
|
import { NAvatar, NTag } from 'naive-ui';
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '编码',
|
||||||
|
key: 'no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
key: 'name',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '头像',
|
||||||
|
key: 'avatar',
|
||||||
|
render(row) {
|
||||||
|
return h(NAvatar, {
|
||||||
|
size: 48,
|
||||||
|
src: row.avatar,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '地址',
|
||||||
|
key: 'address',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始日期',
|
||||||
|
key: 'beginTime',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束日期',
|
||||||
|
key: 'endTime',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
render(row) {
|
||||||
|
return h(
|
||||||
|
NTag,
|
||||||
|
{
|
||||||
|
type: row.status ? 'success' : 'error',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => (row.status ? '启用' : '禁用'),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '停留时间',
|
||||||
|
key: 'time',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
import { h } from 'vue';
|
|
||||||
import { NAvatar, NButton } from 'naive-ui';
|
|
||||||
|
|
||||||
export const columns = [
|
|
||||||
{
|
|
||||||
title: 'id',
|
|
||||||
key: 'id',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '名称',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '头像',
|
|
||||||
key: 'avatar',
|
|
||||||
render(row) {
|
|
||||||
return h(NAvatar, {
|
|
||||||
size: 48,
|
|
||||||
src: row.avatar,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '地址',
|
|
||||||
key: 'address',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '开始日期',
|
|
||||||
key: 'beginTime',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '结束日期',
|
|
||||||
key: 'endTime',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 150,
|
|
||||||
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
|
||||||
render() {
|
|
||||||
return [
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
type: 'error',
|
|
||||||
style: 'margin-right:10px',
|
|
||||||
onClick: () => {},
|
|
||||||
},
|
|
||||||
{ default: () => '删除' }
|
|
||||||
),
|
|
||||||
h(
|
|
||||||
NButton,
|
|
||||||
{
|
|
||||||
size: 'small',
|
|
||||||
onClick: () => {},
|
|
||||||
},
|
|
||||||
{ default: () => '编辑' }
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
131
src/views/comp/table/editCell.vue
Normal file
131
src/views/comp/table/editCell.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<n-card :bordered="false" class="proCard">
|
||||||
|
<BasicTable
|
||||||
|
title="表格列表"
|
||||||
|
titleTooltip="这是一个提示"
|
||||||
|
:columns="columns"
|
||||||
|
:request="loadDataTable"
|
||||||
|
:row-key="(row) => row.id"
|
||||||
|
ref="actionRef"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
|
@edit-end="editEnd"
|
||||||
|
@edit-change="onEditChange"
|
||||||
|
@update:checked-row-keys="onCheckedRow"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</n-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs, ref, h } from 'vue';
|
||||||
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
|
import { getTableList } from '@/api/table/list';
|
||||||
|
import { columns } from './CellColumns';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicTable },
|
||||||
|
setup() {
|
||||||
|
const actionRef = ref();
|
||||||
|
const currentEditKeyRef = ref('');
|
||||||
|
const state = reactive({
|
||||||
|
params: {
|
||||||
|
pageSize: 5,
|
||||||
|
name: 'xiaoMa',
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 150,
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction, {
|
||||||
|
style: 'button',
|
||||||
|
actions: createActions(record),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleEdit(record) {
|
||||||
|
currentEditKeyRef.value = record.key;
|
||||||
|
record.onEdit?.(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel(record: EditRecordRow) {
|
||||||
|
currentEditKeyRef.value = '';
|
||||||
|
record.onEdit?.(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditChange({ column, value, record }) {
|
||||||
|
if (column.dataIndex === 'id') {
|
||||||
|
record.editValueRefs.name4.value = `${value}`;
|
||||||
|
}
|
||||||
|
console.log(column, value, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave(record: EditRecordRow) {
|
||||||
|
const pass = await record.onEdit?.(false, true);
|
||||||
|
if (pass) {
|
||||||
|
currentEditKeyRef.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createActions(record) {
|
||||||
|
if (!record.editable) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '保存',
|
||||||
|
onClick: handleSave.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '取消',
|
||||||
|
onClick: handleCancel.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDataTable = async (params) => {
|
||||||
|
const data = await getTableList(params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function onCheckedRow(rowKeys) {
|
||||||
|
console.log(rowKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
console.log(actionRef.value);
|
||||||
|
actionRef.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editEnd({ record, index, key, value }) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
columns,
|
||||||
|
actionRef,
|
||||||
|
loadDataTable,
|
||||||
|
onCheckedRow,
|
||||||
|
reloadTable,
|
||||||
|
editEnd,
|
||||||
|
onEditChange,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
131
src/views/comp/table/editRow.vue
Normal file
131
src/views/comp/table/editRow.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<n-card :bordered="false" class="proCard">
|
||||||
|
<BasicTable
|
||||||
|
title="表格列表"
|
||||||
|
titleTooltip="这是一个提示"
|
||||||
|
:columns="columns"
|
||||||
|
:request="loadDataTable"
|
||||||
|
:row-key="(row) => row.id"
|
||||||
|
ref="actionRef"
|
||||||
|
:actionColumn="actionColumn"
|
||||||
|
@edit-end="editEnd"
|
||||||
|
@edit-change="onEditChange"
|
||||||
|
@update:checked-row-keys="onCheckedRow"
|
||||||
|
>
|
||||||
|
<template #toolbar>
|
||||||
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
|
</template>
|
||||||
|
</BasicTable>
|
||||||
|
</n-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs, ref, h } from 'vue';
|
||||||
|
import { BasicTable, TableAction } from '@/components/Table';
|
||||||
|
import { getTableList } from '@/api/table/list';
|
||||||
|
import { columns } from './rowColumns';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicTable },
|
||||||
|
setup() {
|
||||||
|
const actionRef = ref();
|
||||||
|
const currentEditKeyRef = ref('');
|
||||||
|
const state = reactive({
|
||||||
|
params: {
|
||||||
|
pageSize: 5,
|
||||||
|
name: 'xiaoMa',
|
||||||
|
},
|
||||||
|
actionColumn: {
|
||||||
|
width: 150,
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
align: 'center',
|
||||||
|
render(record) {
|
||||||
|
return h(TableAction, {
|
||||||
|
style: 'button',
|
||||||
|
actions: createActions(record),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleEdit(record) {
|
||||||
|
currentEditKeyRef.value = record.key;
|
||||||
|
record.onEdit?.(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancel(record: EditRecordRow) {
|
||||||
|
currentEditKeyRef.value = '';
|
||||||
|
record.onEdit?.(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEditChange({ column, value, record }) {
|
||||||
|
if (column.dataIndex === 'id') {
|
||||||
|
record.editValueRefs.name4.value = `${value}`;
|
||||||
|
}
|
||||||
|
console.log(column, value, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave(record: EditRecordRow) {
|
||||||
|
const pass = await record.onEdit?.(false, true);
|
||||||
|
if (pass) {
|
||||||
|
currentEditKeyRef.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createActions(record) {
|
||||||
|
if (!record.editable) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '编辑',
|
||||||
|
onClick: handleEdit.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '保存',
|
||||||
|
onClick: handleSave.bind(null, record),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '取消',
|
||||||
|
onClick: handleCancel.bind(null, record),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadDataTable = async (params) => {
|
||||||
|
const data = await getTableList(params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
function onCheckedRow(rowKeys) {
|
||||||
|
console.log(rowKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
console.log(actionRef.value);
|
||||||
|
actionRef.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function editEnd({ record, index, key, value }) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
columns,
|
||||||
|
actionRef,
|
||||||
|
loadDataTable,
|
||||||
|
onCheckedRow,
|
||||||
|
reloadTable,
|
||||||
|
editEnd,
|
||||||
|
onEditChange,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
<template>
|
|
||||||
<n-card :bordered="false" class="proCard">
|
|
||||||
<BasicTable
|
|
||||||
title="表格列表"
|
|
||||||
titleTooltip="这是一个提示"
|
|
||||||
:columns="columns"
|
|
||||||
:request="loadDataTable"
|
|
||||||
:row-key="(row) => row.id"
|
|
||||||
ref="actionRef"
|
|
||||||
@update:checked-row-keys="onCheckedRow"
|
|
||||||
>
|
|
||||||
<template #toolbar>
|
|
||||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
|
||||||
</template>
|
|
||||||
</BasicTable>
|
|
||||||
</n-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, reactive, toRefs, ref } from 'vue';
|
|
||||||
import { BasicTable } from '@/components/Table';
|
|
||||||
import { getTableList } from '@/api/table/list';
|
|
||||||
import { columns } from './columns';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { BasicTable },
|
|
||||||
setup() {
|
|
||||||
const actionRef = ref();
|
|
||||||
const state = reactive({
|
|
||||||
params: {
|
|
||||||
pageSize: 5,
|
|
||||||
name: 'xiaoMa',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const loadDataTable = async (params) => {
|
|
||||||
const data = await getTableList(params);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
function onCheckedRow(rowKeys) {
|
|
||||||
console.log(rowKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadTable() {
|
|
||||||
console.log(actionRef.value);
|
|
||||||
actionRef.value.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
columns,
|
|
||||||
actionRef,
|
|
||||||
loadDataTable,
|
|
||||||
onCheckedRow,
|
|
||||||
reloadTable,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
|
||||||
89
src/views/comp/table/rowColumns.ts
Normal file
89
src/views/comp/table/rowColumns.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { h } from 'vue';
|
||||||
|
import { NAvatar } from 'naive-ui';
|
||||||
|
|
||||||
|
export const columns = [
|
||||||
|
{
|
||||||
|
title: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '编码',
|
||||||
|
key: 'no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
key: 'name',
|
||||||
|
editComponent: 'NInput',
|
||||||
|
editRow: true,
|
||||||
|
// 默认必填校验
|
||||||
|
editRule: true,
|
||||||
|
edit: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '头像',
|
||||||
|
key: 'avatar',
|
||||||
|
render(row) {
|
||||||
|
return h(NAvatar, {
|
||||||
|
size: 48,
|
||||||
|
src: row.avatar,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '地址',
|
||||||
|
key: 'address',
|
||||||
|
editRow: true,
|
||||||
|
editComponent: 'NSelect',
|
||||||
|
editComponentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: '广东省',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '浙江省',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
edit: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始日期',
|
||||||
|
key: 'beginTime',
|
||||||
|
editRow: true,
|
||||||
|
edit: true,
|
||||||
|
width: 250,
|
||||||
|
editComponent: 'NDatePicker',
|
||||||
|
editComponentProps: {
|
||||||
|
type: 'datetime',
|
||||||
|
format: 'yyyy-MM-dd HH:mm:ss',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束日期',
|
||||||
|
key: 'endTime',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'status',
|
||||||
|
editRow: true,
|
||||||
|
edit: true,
|
||||||
|
width: 100,
|
||||||
|
editComponent: 'NSwitch',
|
||||||
|
editValueMap: (value) => {
|
||||||
|
return value ? '启用' : '禁用';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '停留时间',
|
||||||
|
key: 'time',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.view-account {
|
.view-account {
|
||||||
background-image: url('@/assets/images/login.svg');
|
background-image: url('../../assets/images/login.svg');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
页面数据为 Mock 示例数据,非真实数据。
|
页面数据为 Mock 示例数据,非真实数据。
|
||||||
</n-card>
|
</n-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-grid class="mt-4" cols="1 s:1 m:1 l:3 xl:3 2xl:3" responsive="screen" :x-gap="12">
|
<n-grid class="mt-4" cols="1 s:1 m:1 l:3 xl:3 2xl:3" responsive="screen" :x-gap="12">
|
||||||
<n-gi span="1">
|
<n-gi span="1">
|
||||||
<n-card :segmented="{ content: 'hard' }" :bordered="false" size="small">
|
<n-card :segmented="{ content: 'hard' }" :bordered="false" size="small">
|
||||||
@@ -35,7 +34,6 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="w-full menu">
|
<div class="w-full menu">
|
||||||
<n-input type="input" v-model:value="pattern" placeholder="输入菜单名称搜索">
|
<n-input type="input" v-model:value="pattern" placeholder="输入菜单名称搜索">
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
@@ -61,6 +59,7 @@
|
|||||||
:expandedKeys="expandedKeys"
|
:expandedKeys="expandedKeys"
|
||||||
style="max-height: 650px; overflow: hidden"
|
style="max-height: 650px; overflow: hidden"
|
||||||
@update:selected-keys="selectedTree"
|
@update:selected-keys="selectedTree"
|
||||||
|
@update:expanded-keys="onExpandedKeys"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,7 +76,7 @@
|
|||||||
<span>编辑菜单{{ treeItemTitle ? `:${treeItemTitle}` : '' }}</span>
|
<span>编辑菜单{{ treeItemTitle ? `:${treeItemTitle}` : '' }}</span>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
<n-alert type="info" closable> 从菜单列表选择一项后,进行编辑 </n-alert>
|
<n-alert type="info" closable> 从菜单列表选择一项后,进行编辑</n-alert>
|
||||||
<n-form
|
<n-form
|
||||||
:model="formParams"
|
:model="formParams"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
@@ -122,17 +121,15 @@
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
|
|
||||||
<CreateDrawer ref="createDrawerRef" :title="drawerTitle" />
|
<CreateDrawer ref="createDrawerRef" :title="drawerTitle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, unref, reactive, toRefs, onMounted, computed } from 'vue';
|
import { defineComponent, ref, unref, reactive, toRefs, onMounted, computed } from 'vue';
|
||||||
import { useMessage } from 'naive-ui';
|
import { useMessage } from 'naive-ui';
|
||||||
import { DownOutlined, AlignLeftOutlined, SearchOutlined, FormOutlined } from '@vicons/antd';
|
import { DownOutlined, AlignLeftOutlined, SearchOutlined, FormOutlined } from '@vicons/antd';
|
||||||
import { getMenuList } from '@/api/system/menu';
|
import { getMenuList } from '@/api/system/menu';
|
||||||
import { getTreeItem } from '@/utils/index';
|
import { getTreeItem } from '@/utils';
|
||||||
import CreateDrawer from './CreateDrawer.vue';
|
import CreateDrawer from './CreateDrawer.vue';
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -156,35 +153,49 @@
|
|||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
|
|
||||||
const isAddSon = computed(() => {
|
const isAddSon = computed(() => {
|
||||||
return state.treeItemKey.length ? false : true;
|
return !state.treeItemKey.length;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const addMenuOptions = ref([
|
||||||
|
{
|
||||||
|
label: '添加顶级菜单',
|
||||||
|
key: 'home',
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '添加子菜单',
|
||||||
|
key: 'son',
|
||||||
|
disabled: isAddSon,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const treeItemKey: string[] = reactive([]);
|
||||||
|
|
||||||
|
const expandedKeys: string[] = reactive([]);
|
||||||
|
|
||||||
|
const treeData: string[] = reactive([]);
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
addMenuOptions: [
|
|
||||||
{
|
|
||||||
label: '添加顶级菜单',
|
|
||||||
key: 'home',
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '添加子菜单',
|
|
||||||
key: 'son',
|
|
||||||
disabled: isAddSon,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
loading: true,
|
loading: true,
|
||||||
subLoading: false,
|
subLoading: false,
|
||||||
isEditMenu: false,
|
isEditMenu: false,
|
||||||
treeData: [],
|
|
||||||
treeItemKey: [],
|
|
||||||
treeItemTitle: '',
|
treeItemTitle: '',
|
||||||
expandedKeys: [],
|
formParams: {
|
||||||
formParams: {},
|
type: 1,
|
||||||
|
label: '',
|
||||||
|
subtitle: '',
|
||||||
|
path: '',
|
||||||
|
auth: '',
|
||||||
|
openType: 1,
|
||||||
|
},
|
||||||
pattern: '',
|
pattern: '',
|
||||||
drawerTitle: '',
|
drawerTitle: '',
|
||||||
|
treeItemKey,
|
||||||
|
expandedKeys,
|
||||||
|
treeData,
|
||||||
});
|
});
|
||||||
|
|
||||||
function selectAddMenu(key) {
|
function selectAddMenu(key: string) {
|
||||||
state.drawerTitle = key === 'home' ? '添加顶栏菜单' : `添加子菜单:${state.treeItemTitle}`;
|
state.drawerTitle = key === 'home' ? '添加顶栏菜单' : `添加子菜单:${state.treeItemTitle}`;
|
||||||
openCreateDrawer();
|
openCreateDrawer();
|
||||||
}
|
}
|
||||||
@@ -194,7 +205,7 @@
|
|||||||
openDrawer();
|
openDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectedTree(keys) {
|
function selectedTree(keys: string[]) {
|
||||||
if (keys.length) {
|
if (keys.length) {
|
||||||
const treeItem = getTreeItem(unref(state.treeData), keys[0]);
|
const treeItem = getTreeItem(unref(state.treeData), keys[0]);
|
||||||
state.treeItemKey = keys;
|
state.treeItemKey = keys;
|
||||||
@@ -214,7 +225,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formSubmit() {
|
function formSubmit() {
|
||||||
formRef.value.validate((errors) => {
|
formRef.value.validate((errors: boolean) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
message.error('抱歉,您没有该权限');
|
message.error('抱歉,您没有该权限');
|
||||||
} else {
|
} else {
|
||||||
@@ -227,21 +238,10 @@
|
|||||||
if (state.expandedKeys.length) {
|
if (state.expandedKeys.length) {
|
||||||
state.expandedKeys = [];
|
state.expandedKeys = [];
|
||||||
} else {
|
} else {
|
||||||
state.expandedKeys = state.treeData.map((item) => item.key);
|
state.expandedKeys = state.treeData.map((item: any) => item.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onExpandedKeys() {
|
|
||||||
// let key = keys[0]
|
|
||||||
// console.log(state.expandedKeys)
|
|
||||||
// if(state.expandedKeys.includes(key)){
|
|
||||||
// state.expandedKeys.splice(state.expandedKeys.findIndex(item => item === key), 1)
|
|
||||||
// console.log(state.expandedKeys)
|
|
||||||
// }else{
|
|
||||||
// state.expandedKeys.push(key)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const treeMenuList = await getMenuList();
|
const treeMenuList = await getMenuList();
|
||||||
state.expandedKeys = treeMenuList.list.map((item) => item.key);
|
state.expandedKeys = treeMenuList.list.map((item) => item.key);
|
||||||
@@ -249,8 +249,13 @@
|
|||||||
state.loading = false;
|
state.loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function onExpandedKeys(keys: string[]) {
|
||||||
|
state.expandedKeys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
|
addMenuOptions,
|
||||||
createDrawerRef,
|
createDrawerRef,
|
||||||
formRef,
|
formRef,
|
||||||
rules,
|
rules,
|
||||||
|
|||||||
5
types/global.d.ts
vendored
5
types/global.d.ts
vendored
@@ -66,6 +66,9 @@ declare global {
|
|||||||
VITE_DROP_CONSOLE: boolean;
|
VITE_DROP_CONSOLE: boolean;
|
||||||
VITE_GLOB_PROD_MOCK: boolean;
|
VITE_GLOB_PROD_MOCK: boolean;
|
||||||
VITE_GLOB_IMG_URL: string;
|
VITE_GLOB_IMG_URL: string;
|
||||||
|
VITE_PROXY: [string, string][];
|
||||||
|
VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
|
||||||
|
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function parseInt(s: string | number, radix?: number): number;
|
declare function parseInt(s: string | number, radix?: number): number;
|
||||||
@@ -94,6 +97,6 @@ declare global {
|
|||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export type JSXComponent<Props = any> =
|
export type JSXComponent<Props = any> =
|
||||||
| { new(): ComponentPublicInstance<Props> }
|
| { new (): ComponentPublicInstance<Props> }
|
||||||
| FunctionalComponent<Props>;
|
| FunctionalComponent<Props>;
|
||||||
}
|
}
|
||||||
|
|||||||
6
types/index.d.ts
vendored
6
types/index.d.ts
vendored
@@ -8,6 +8,12 @@ declare interface PromiseFn<T = any, R = T> {
|
|||||||
|
|
||||||
declare type RefType<T> = T | null;
|
declare type RefType<T> = T | null;
|
||||||
|
|
||||||
|
declare type LabelValueOptions = {
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
disabled: boolean;
|
||||||
|
[key: string]: string | number | boolean;
|
||||||
|
}[];
|
||||||
|
|
||||||
declare type EmitType = (event: string, ...args: any[]) => void;
|
declare type EmitType = (event: string, ...args: any[]) => void;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { resolve } from 'path';
|
|||||||
import { wrapperEnv } from './build/utils';
|
import { wrapperEnv } from './build/utils';
|
||||||
import { createVitePlugins } from './build/vite/plugin';
|
import { createVitePlugins } from './build/vite/plugin';
|
||||||
import { OUTPUT_DIR } from './build/constant';
|
import { OUTPUT_DIR } from './build/constant';
|
||||||
|
import { createProxy } from './build/vite/proxy';
|
||||||
|
|
||||||
function pathResolve(dir: string) {
|
function pathResolve(dir: string) {
|
||||||
return resolve(process.cwd(), '.', dir);
|
return resolve(process.cwd(), '.', dir);
|
||||||
@@ -13,7 +14,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
const root = process.cwd();
|
const root = process.cwd();
|
||||||
const env = loadEnv(mode, root);
|
const env = loadEnv(mode, root);
|
||||||
const viteEnv = wrapperEnv(env);
|
const viteEnv = wrapperEnv(env);
|
||||||
const { VITE_PUBLIC_PATH, VITE_DROP_CONSOLE, VITE_PORT, VITE_GLOB_PROD_MOCK } = viteEnv;
|
const { VITE_PUBLIC_PATH, VITE_DROP_CONSOLE, VITE_PORT, VITE_GLOB_PROD_MOCK, VITE_PROXY } =
|
||||||
|
viteEnv;
|
||||||
const prodMock = VITE_GLOB_PROD_MOCK;
|
const prodMock = VITE_GLOB_PROD_MOCK;
|
||||||
const isBuild = command === 'build';
|
const isBuild = command === 'build';
|
||||||
return {
|
return {
|
||||||
@@ -38,15 +40,14 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
less: {
|
less: {
|
||||||
modifyVars: {},
|
modifyVars: {},
|
||||||
javascriptEnabled: true,
|
javascriptEnabled: true,
|
||||||
additionalData: `@import "src/styles/global.less";`,
|
additionalData: `@import "src/styles/var.less";`,
|
||||||
},
|
|
||||||
scss: {
|
|
||||||
additionalData: `@import "src/styles/global.less";`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
host: true,
|
||||||
port: VITE_PORT,
|
port: VITE_PORT,
|
||||||
|
proxy: createProxy(VITE_PROXY),
|
||||||
// proxy: {
|
// proxy: {
|
||||||
// '/api': {
|
// '/api': {
|
||||||
// target: '',
|
// target: '',
|
||||||
|
|||||||
82
yarn.lock
82
yarn.lock
@@ -641,13 +641,6 @@
|
|||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@fullhuman/postcss-purgecss@^4.0.3":
|
|
||||||
version "4.0.3"
|
|
||||||
resolved "https://registry.npm.taobao.org/@fullhuman/postcss-purgecss/download/@fullhuman/postcss-purgecss-4.0.3.tgz#55d71712ec1c7a88e0d1ba5f10ce7fb6aa05beb4"
|
|
||||||
integrity sha1-VdcXEuwceojg0bpfEM5/tqoFvrQ=
|
|
||||||
dependencies:
|
|
||||||
purgecss "^4.0.3"
|
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.nlark.com/@istanbuljs/load-nyc-config/download/@istanbuljs/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
resolved "https://registry.nlark.com/@istanbuljs/load-nyc-config/download/@istanbuljs/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||||
@@ -2093,7 +2086,7 @@ collect-v8-coverage@^1.0.0:
|
|||||||
resolved "https://registry.npm.taobao.org/collect-v8-coverage/download/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
resolved "https://registry.npm.taobao.org/collect-v8-coverage/download/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
|
||||||
integrity sha1-zCyOlPwYu9/+ZNZTRXDIpnOyf1k=
|
integrity sha1-zCyOlPwYu9/+ZNZTRXDIpnOyf1k=
|
||||||
|
|
||||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
color-convert@^1.9.0, color-convert@^1.9.3:
|
||||||
version "1.9.3"
|
version "1.9.3"
|
||||||
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||||
@@ -2117,21 +2110,21 @@ color-name@^1.0.0, color-name@~1.1.4:
|
|||||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||||
|
|
||||||
color-string@^1.5.4:
|
color-string@^1.6.0:
|
||||||
version "1.5.5"
|
version "1.6.0"
|
||||||
resolved "https://registry.npm.taobao.org/color-string/download/color-string-1.5.5.tgz?cache=0&sync_timestamp=1614967162868&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-string%2Fdownload%2Fcolor-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014"
|
resolved "https://registry.nlark.com/color-string/download/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312"
|
||||||
integrity sha1-ZUdKjw50OWJfPSemoZ2J/EUiMBQ=
|
integrity sha1-w5FfYf4mdnLLfh4GTJ1pIhn2wxI=
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name "^1.0.0"
|
color-name "^1.0.0"
|
||||||
simple-swizzle "^0.2.2"
|
simple-swizzle "^0.2.2"
|
||||||
|
|
||||||
color@^3.1.3:
|
color@^3.2.0:
|
||||||
version "3.1.3"
|
version "3.2.1"
|
||||||
resolved "https://registry.npm.taobao.org/color/download/color-3.1.3.tgz?cache=0&sync_timestamp=1602228883047&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor%2Fdownload%2Fcolor-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e"
|
resolved "https://registry.nlark.com/color/download/color-3.2.1.tgz?cache=0&sync_timestamp=1626601281817&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcolor%2Fdownload%2Fcolor-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164"
|
||||||
integrity sha1-ymf7TnuX1hHc3jns7tQiBn2RWW4=
|
integrity sha1-NUTcGYyvRJDD7MmnkLVP6f9F4WQ=
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^1.9.1"
|
color-convert "^1.9.3"
|
||||||
color-string "^1.5.4"
|
color-string "^1.6.0"
|
||||||
|
|
||||||
colorette@^1.2.1, colorette@^1.2.2:
|
colorette@^1.2.1, colorette@^1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
@@ -2489,10 +2482,10 @@ detective@^5.2.0:
|
|||||||
defined "^1.0.0"
|
defined "^1.0.0"
|
||||||
minimist "^1.1.1"
|
minimist "^1.1.1"
|
||||||
|
|
||||||
didyoumean@^1.2.1:
|
didyoumean@^1.2.2:
|
||||||
version "1.2.1"
|
version "1.2.2"
|
||||||
resolved "https://registry.npm.taobao.org/didyoumean/download/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff"
|
resolved "https://registry.nlark.com/didyoumean/download/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
|
||||||
integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=
|
integrity sha1-mJNG/+noObRVXs9WZu3qDT6K0Dc=
|
||||||
|
|
||||||
diff-sequences@^26.6.2:
|
diff-sequences@^26.6.2:
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
@@ -5006,10 +4999,10 @@ mute-stream@0.0.7:
|
|||||||
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||||
|
|
||||||
naive-ui@^2.15.5:
|
naive-ui@^2.15.11:
|
||||||
version "2.15.5"
|
version "2.15.11"
|
||||||
resolved "https://registry.nlark.com/naive-ui/download/naive-ui-2.15.5.tgz#50ffc1834fd64765621a31f0b09e64d72770cd6c"
|
resolved "https://registry.nlark.com/naive-ui/download/naive-ui-2.15.11.tgz#1a7fa5ca6d42ed2c4d50cde827b994f42edc5743"
|
||||||
integrity sha1-UP/Bg0/WR2ViGjHwsJ5k1ydwzWw=
|
integrity sha1-Gn+lym1C7SxNUM3oJ7mU9C7cV0M=
|
||||||
dependencies:
|
dependencies:
|
||||||
"@css-render/plugin-bem" "^0.15.4"
|
"@css-render/plugin-bem" "^0.15.4"
|
||||||
"@css-render/vue3-ssr" "^0.15.4"
|
"@css-render/vue3-ssr" "^0.15.4"
|
||||||
@@ -5912,8 +5905,8 @@ reusify@^1.0.4:
|
|||||||
|
|
||||||
rimraf@^3.0.0, rimraf@^3.0.2:
|
rimraf@^3.0.0, rimraf@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
resolved "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
integrity sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
@@ -6454,22 +6447,21 @@ table@^6.0.9, table@^6.6.0:
|
|||||||
string-width "^4.2.0"
|
string-width "^4.2.0"
|
||||||
strip-ansi "^6.0.0"
|
strip-ansi "^6.0.0"
|
||||||
|
|
||||||
tailwindcss@^2.2.4:
|
tailwindcss@^2.2.7:
|
||||||
version "2.2.4"
|
version "2.2.7"
|
||||||
resolved "https://registry.nlark.com/tailwindcss/download/tailwindcss-2.2.4.tgz#6a2e259b1e26125aeaa7cdc479963fd217c308b0"
|
resolved "https://registry.nlark.com/tailwindcss/download/tailwindcss-2.2.7.tgz#795d07a14ef46c2dc4a1610f7f906f697daaf731"
|
||||||
integrity sha1-ai4lmx4mElrqp83EeZY/0hfDCLA=
|
integrity sha1-eV0HoU70bC3EoWEPf5BvaX2q9zE=
|
||||||
dependencies:
|
dependencies:
|
||||||
"@fullhuman/postcss-purgecss" "^4.0.3"
|
|
||||||
arg "^5.0.0"
|
arg "^5.0.0"
|
||||||
bytes "^3.0.0"
|
bytes "^3.0.0"
|
||||||
chalk "^4.1.1"
|
chalk "^4.1.1"
|
||||||
chokidar "^3.5.2"
|
chokidar "^3.5.2"
|
||||||
color "^3.1.3"
|
color "^3.2.0"
|
||||||
cosmiconfig "^7.0.0"
|
cosmiconfig "^7.0.0"
|
||||||
detective "^5.2.0"
|
detective "^5.2.0"
|
||||||
didyoumean "^1.2.1"
|
didyoumean "^1.2.2"
|
||||||
dlv "^1.1.3"
|
dlv "^1.1.3"
|
||||||
fast-glob "^3.2.5"
|
fast-glob "^3.2.7"
|
||||||
fs-extra "^10.0.0"
|
fs-extra "^10.0.0"
|
||||||
glob-parent "^6.0.0"
|
glob-parent "^6.0.0"
|
||||||
html-tags "^3.1.0"
|
html-tags "^3.1.0"
|
||||||
@@ -6486,6 +6478,7 @@ tailwindcss@^2.2.4:
|
|||||||
postcss-selector-parser "^6.0.6"
|
postcss-selector-parser "^6.0.6"
|
||||||
postcss-value-parser "^4.1.0"
|
postcss-value-parser "^4.1.0"
|
||||||
pretty-hrtime "^1.0.3"
|
pretty-hrtime "^1.0.3"
|
||||||
|
purgecss "^4.0.3"
|
||||||
quick-lru "^5.1.1"
|
quick-lru "^5.1.1"
|
||||||
reduce-css-calc "^2.1.8"
|
reduce-css-calc "^2.1.8"
|
||||||
resolve "^1.20.0"
|
resolve "^1.20.0"
|
||||||
@@ -6691,10 +6684,10 @@ typedarray-to-buffer@^3.1.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-typedarray "^1.0.0"
|
is-typedarray "^1.0.0"
|
||||||
|
|
||||||
typescript@^4.3.2:
|
typescript@^4.3.5:
|
||||||
version "4.3.2"
|
version "4.3.5"
|
||||||
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
resolved "https://registry.nlark.com/typescript/download/typescript-4.3.5.tgz?cache=0&sync_timestamp=1627197601863&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ftypescript%2Fdownload%2Ftypescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
|
||||||
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
integrity sha1-TRw3zBbok5c8RaBohrcRMjTxGfQ=
|
||||||
|
|
||||||
unified@^9.1.0:
|
unified@^9.1.0:
|
||||||
version "9.2.1"
|
version "9.2.1"
|
||||||
@@ -6826,6 +6819,15 @@ vfonts@^0.1.0:
|
|||||||
resolved "https://registry.nlark.com/vfonts/download/vfonts-0.1.0.tgz#c16af37ca044b2725ae55553049280efbe6222a9"
|
resolved "https://registry.nlark.com/vfonts/download/vfonts-0.1.0.tgz#c16af37ca044b2725ae55553049280efbe6222a9"
|
||||||
integrity sha1-wWrzfKBEsnJa5VVTBJKA775iIqk=
|
integrity sha1-wWrzfKBEsnJa5VVTBJKA775iIqk=
|
||||||
|
|
||||||
|
vite-plugin-compression@^0.3.1:
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.nlark.com/vite-plugin-compression/download/vite-plugin-compression-0.3.1.tgz#b8947d0fd7bba60f6c3385cfcb8ce3205028c541"
|
||||||
|
integrity sha1-uJR9D9e7pg9sM4XPy4zjIFAoxUE=
|
||||||
|
dependencies:
|
||||||
|
chalk "^4.1.1"
|
||||||
|
debug "^4.3.2"
|
||||||
|
fs-extra "^9.1.0"
|
||||||
|
|
||||||
vite-plugin-html@^2.0.7:
|
vite-plugin-html@^2.0.7:
|
||||||
version "2.0.7"
|
version "2.0.7"
|
||||||
resolved "https://registry.npm.taobao.org/vite-plugin-html/download/vite-plugin-html-2.0.7.tgz#55139ff8a843ba3b3d5cd48610ca6f6afdf7c23d"
|
resolved "https://registry.npm.taobao.org/vite-plugin-html/download/vite-plugin-html-2.0.7.tgz#55139ff8a843ba3b3d5cd48610ca6f6afdf7c23d"
|
||||||
|
|||||||
Reference in New Issue
Block a user