mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-25 16:16:20 +08:00
Compare commits
118 Commits
v3.0.0-alp
...
2.10.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fa5803f8c7 | ||
![]() |
aa6168fe20 | ||
![]() |
855a410557 | ||
![]() |
b1559e2cad | ||
![]() |
c5d24e07f0 | ||
![]() |
1cf2a81f2a | ||
![]() |
5665fd62a9 | ||
![]() |
b7554fdb74 | ||
![]() |
3b62afe110 | ||
![]() |
dbdd811705 | ||
![]() |
a6b65b58a1 | ||
![]() |
556575f501 | ||
![]() |
e4778757ad | ||
![]() |
b43fe7adbc | ||
![]() |
4aaddef06f | ||
![]() |
ce6b25d03b | ||
![]() |
74a2f6209f | ||
![]() |
99ddc3598a | ||
![]() |
d3fd22dbbd | ||
![]() |
bf2f6390ad | ||
![]() |
bd024cc521 | ||
![]() |
4ac08e5ae5 | ||
![]() |
b0c2ca5393 | ||
![]() |
1ac8c56c6b | ||
![]() |
f4149c2f1d | ||
![]() |
3ed49c3f8f | ||
![]() |
356f132610 | ||
![]() |
1c668f21bf | ||
![]() |
a244dcd261 | ||
![]() |
aaf2fde3cf | ||
![]() |
607a24632a | ||
![]() |
89d7a19f3f | ||
![]() |
a9017da294 | ||
![]() |
1c1ce4b0e3 | ||
![]() |
e31525a803 | ||
![]() |
7b26c5994c | ||
![]() |
248f9d5e81 | ||
![]() |
a2a78f40da | ||
![]() |
2c74e790cb | ||
![]() |
b660f96220 | ||
![]() |
5dd3babc4e | ||
![]() |
5425dc241f | ||
![]() |
44f2b1c644 | ||
![]() |
573d395b69 | ||
![]() |
df1fceb291 | ||
![]() |
9650122736 | ||
![]() |
c99ef68b7b | ||
![]() |
a95ba47b74 | ||
![]() |
3fd193eb8b | ||
![]() |
6fbc552ed1 | ||
![]() |
247665513a | ||
![]() |
351e1e55c2 | ||
![]() |
8a27f5f277 | ||
![]() |
befb508f7a | ||
![]() |
e6c820792a | ||
![]() |
30ccbfa695 | ||
![]() |
7e12259ec4 | ||
![]() |
244eeb18aa | ||
![]() |
1108d78a06 | ||
![]() |
ebe2047ae0 | ||
![]() |
c7f4e6a459 | ||
![]() |
833b31129b | ||
![]() |
86d5752ed7 | ||
![]() |
9babbc43fc | ||
![]() |
a2451be5bc | ||
![]() |
eea414e04b | ||
![]() |
0bd98b3c27 | ||
![]() |
a065de4fbc | ||
![]() |
fa5ecb090f | ||
![]() |
4f9c711012 | ||
![]() |
12924fb3fa | ||
![]() |
16b4b6d57c | ||
![]() |
c28224f3f8 | ||
![]() |
3b0b8d0baa | ||
![]() |
b30270a3fb | ||
![]() |
cb64e5d24c | ||
![]() |
a4e70b9efe | ||
![]() |
4e3e721650 | ||
![]() |
c6e135195a | ||
![]() |
1262e13067 | ||
![]() |
c659c14c5a | ||
![]() |
5ad5c8cdc7 | ||
![]() |
27cb958c2e | ||
![]() |
155ad45848 | ||
![]() |
7535db377f | ||
![]() |
a0fdceeae7 | ||
![]() |
c6e5c0f5f1 | ||
![]() |
ca997c15ca | ||
![]() |
4c381596a9 | ||
![]() |
7bcdb46148 | ||
![]() |
d33ccd042f | ||
![]() |
08f479f3e1 | ||
![]() |
c054d73fe6 | ||
![]() |
1f287145f4 | ||
![]() |
9c43c74131 | ||
![]() |
c516d39225 | ||
![]() |
a1283c1322 | ||
![]() |
cc88e1a66c | ||
![]() |
6aa3f934d0 | ||
![]() |
7ca007ecd5 | ||
![]() |
361a189f6a | ||
![]() |
19fd49e22d | ||
![]() |
1e8fab3fe5 | ||
![]() |
7e0456cc6c | ||
![]() |
c118e83a2b | ||
![]() |
31d44ad372 | ||
![]() |
f810a0892d | ||
![]() |
5de89b5ec5 | ||
![]() |
0347c83620 | ||
![]() |
eb0fdb2cfc | ||
![]() |
e154d1366c | ||
![]() |
34237ef033 | ||
![]() |
b13c4a81fc | ||
![]() |
c46b04d548 | ||
![]() |
afacf68825 | ||
![]() |
60a3b6a9f9 | ||
![]() |
b97d588392 | ||
![]() |
6e716c5607 |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.vscode/
|
22
.env.docker
Normal file
22
.env.docker
Normal file
@@ -0,0 +1,22 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK = false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# timeout(seconds)
|
||||
VITE_TIMEOUT = 15
|
||||
# Delete console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
# Whether to enable gzip or brotli compression
|
||||
# Optional: gzip | brotli | none
|
||||
# If you need multiple forms, you can use `,` to separate
|
||||
VITE_BUILD_COMPRESS = 'none'
|
||||
VITE_GLOB_API_URL="__vg_base_url"
|
||||
|
||||
# File upload address, optional
|
||||
# It can be forwarded by nginx or write the actual address directly
|
||||
VITE_GLOB_UPLOAD_URL=/files/upload
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
@@ -1,4 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben'],
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
},
|
||||
};
|
||||
|
36
.github/workflows/deploy.yml
vendored
36
.github/workflows/deploy.yml
vendored
@@ -60,6 +60,8 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# - uses: NullVoxPopuli/action-setup-pnpm@v2
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -67,22 +69,28 @@ jobs:
|
||||
sed -i "s#VITE_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production
|
||||
cat ./.env.production
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: use Node.js 16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
# - name: Get yarn cache directory path
|
||||
# id: yarn-cache-dir-path
|
||||
# run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
#
|
||||
# - name: Cache dependencies
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-yarn-
|
||||
|
||||
- name: Set SSH Environment
|
||||
env:
|
||||
@@ -100,8 +108,8 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
run: |
|
||||
yarn install
|
||||
yarn run build
|
||||
pnpm install --no-frozen-lockfile
|
||||
pnpm build
|
||||
touch dist/.nojekyll
|
||||
cp dist/index.html dist/404.html
|
||||
|
||||
|
36
.github/workflows/node.js.yml
vendored
Normal file
36
.github/workflows/node.js.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, thin]
|
||||
pull_request:
|
||||
branches: [main, thin]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
@@ -8,3 +8,5 @@ node_modules
|
||||
|
||||
public
|
||||
.npmrc
|
||||
|
||||
*-lock.yaml
|
||||
|
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# node 构建
|
||||
FROM node:16-alpine as build-stage
|
||||
# 署名
|
||||
MAINTAINER Adoin 'adoin@qq.com'
|
||||
WORKDIR /app
|
||||
COPY . ./
|
||||
# 设置 node 阿里镜像
|
||||
RUN npm config set registry https://registry.npm.taobao.org
|
||||
# 设置--max-old-space-size
|
||||
ENV NODE_OPTIONS=--max-old-space-size=16384
|
||||
# 设置阿里镜像、pnpm、依赖、编译
|
||||
RUN npm install pnpm -g && \
|
||||
pnpm install --frozen-lockfile && \
|
||||
pnpm build:docker
|
||||
# node部分结束
|
||||
RUN echo "🎉 编 🎉 译 🎉 成 🎉 功 🎉"
|
||||
# nginx 部署
|
||||
FROM nginx:1.23.3-alpine as production-stage
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html/dist
|
||||
COPY --from=build-stage /app/nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
## 将/usr/share/nginx/html/dist/assets/index.js 和/usr/share/nginx/html/dist/_app.config.js中的"$vg_base_url"替换为环境变量中的VG_BASE_URL,$vg_sub_domain 替换成VG_SUB_DOMAIN,$vg_default_user替换成VG_DEFAULT_USER,$vg_default_password替换成VG_DEFAULT_PASSWORD 而后启动nginx
|
||||
CMD sed -i "s|__vg_base_url|$VG_BASE_URL|g" /usr/share/nginx/html/dist/assets/index.js && \
|
||||
sed -i "s|__vg_base_url|$VG_BASE_URL|g" /usr/share/nginx/html/dist/_app.config.js && \
|
||||
nginx -g 'daemon off;'
|
||||
RUN echo "🎉 架 🎉 设 🎉 成 🎉 功 🎉"
|
22
README.md
22
README.md
@@ -9,7 +9,7 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite2`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||
Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite4`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||
|
||||
## Feature
|
||||
|
||||
@@ -54,7 +54,7 @@ Open the project in Gitpod (free online dev environment for GitHub) and start co
|
||||
- [TypeScript](https://www.typescriptlang.org/) - Familiar with the basic syntax of `TypeScript`
|
||||
- [Es6+](http://es6.ruanyifeng.com/) - Familiar with es6 basic syntax
|
||||
- [Vue-Router-Next](https://next.router.vuejs.org/) - Familiar with the basic use of vue-router
|
||||
- [Ant-Design-Vue](https://2x.antdv.com/docs/vue/introduce-cn/) - ui basic use
|
||||
- [Ant-Design-Vue](https://antdv.com/docs/vue/introduce-cn/) - ui basic use
|
||||
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs basic syntax
|
||||
|
||||
## Install and use
|
||||
@@ -86,6 +86,24 @@ pnpm serve
|
||||
pnpm build
|
||||
```
|
||||
|
||||
- docker
|
||||
|
||||
### The dockerFile is located in the project root directory and supports differential deployment
|
||||
|
||||
#### build image
|
||||
|
||||
```bash
|
||||
docker build -t vue-vben-admin .
|
||||
```
|
||||
|
||||
#### Environment variables are dynamically used to achieve differentiated container deployment. Different VG_BASE_URL environment variables point to different back-end service addresses. In the following example, http://localhost:3333 is used as the back-end service address and the container is mapped to port 6666
|
||||
|
||||
```bash
|
||||
docker run --name vue-vben-admin -d -p 6666:80 -e VG_BASE_URL=http://localhost:3333 vue-vben-admin
|
||||
```
|
||||
|
||||
Then you can navigate http://localhost:6666
|
||||
|
||||
## Change Log
|
||||
|
||||
[CHANGELOG](./CHANGELOG.zh_CN.md)
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
## 简介
|
||||
|
||||
Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite2`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||
Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite4`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||
|
||||
## 特性
|
||||
|
||||
@@ -54,7 +54,7 @@ Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3
|
||||
- [TypeScript](https://www.typescriptlang.org/) - 熟悉`TypeScript`基本语法
|
||||
- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法
|
||||
- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用
|
||||
- [Ant-Design-Vue](https://2x.antdv.com/docs/vue/introduce-cn/) - ui 基本使用
|
||||
- [Ant-Design-Vue](https://antdv.com/docs/vue/introduce-cn/) - ui 基本使用
|
||||
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法
|
||||
|
||||
## 安装使用
|
||||
@@ -86,6 +86,24 @@ pnpm serve
|
||||
pnpm build
|
||||
```
|
||||
|
||||
- docker
|
||||
|
||||
### dockerFile 位于项目根目录下 并且支持差异化部署
|
||||
|
||||
#### 构建镜像
|
||||
|
||||
```bash
|
||||
docker build -t vue-vben-admin .
|
||||
```
|
||||
|
||||
#### 动态使用环境变量实现容器差异化部署,通过不同的 VG_BASE_URL 环境变量,指向不同的后端服务地址,下面例子使用 http://localhost:3333 作为后端服务地址,并且将容器映射到 6666 端口
|
||||
|
||||
```bash
|
||||
docker run --name vue-vben-admin -d -p 6666:80 -e VG_BASE_URL=http://localhost:3333 vue-vben-admin
|
||||
```
|
||||
|
||||
而后可以打开 http://localhost:6666 访问
|
||||
|
||||
## 更新日志
|
||||
|
||||
[CHANGELOG](./CHANGELOG.zh_CN.md)
|
||||
|
@@ -11,9 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^11.1.1",
|
||||
"koa": "^2.14.1",
|
||||
"koa": "^2.14.2",
|
||||
"koa-body": "^6.0.1",
|
||||
"koa-bodyparser": "^4.4.0",
|
||||
"koa-bodyparser": "^4.4.1",
|
||||
"koa-route": "^3.2.0",
|
||||
"koa-router": "^12.0.0",
|
||||
"koa-static": "^5.0.0",
|
||||
@@ -24,13 +24,13 @@
|
||||
"@types/koa": "^2.13.6",
|
||||
"@types/koa-bodyparser": "^5.0.2",
|
||||
"@types/koa-router": "^7.4.4",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/node": "^20.4.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"pm2": "^5.3.0",
|
||||
"rimraf": "^4.4.1",
|
||||
"rimraf": "^5.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsup": "^6.7.0",
|
||||
"typescript": "^5.0.3"
|
||||
"tsup": "^7.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
@@ -36,14 +36,14 @@
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||
"@typescript-eslint/parser": "^6.3.0",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.28.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-vue": "^9.10.0",
|
||||
"vue-eslint-parser": "^9.1.1"
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"vue-eslint-parser": "^9.3.1"
|
||||
}
|
||||
}
|
||||
|
@@ -31,18 +31,18 @@
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "^8.4.21",
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-less": "^6.0.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"prettier": "^2.8.7",
|
||||
"stylelint": "^15.4.0",
|
||||
"prettier": "^2.8.8",
|
||||
"stylelint": "^15.10.1",
|
||||
"stylelint-config-property-sort-order-smacss": "^9.1.0",
|
||||
"stylelint-config-recommended": "^11.0.0",
|
||||
"stylelint-config-recommended-scss": "^9.0.1",
|
||||
"stylelint-config-recommended": "^13.0.0",
|
||||
"stylelint-config-recommended-scss": "^12.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^32.0.0",
|
||||
"stylelint-config-standard-scss": "^7.0.1",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"stylelint-order": "^6.0.3",
|
||||
"stylelint-prettier": "^3.0.0"
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ export default {
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'media-feature-range-notation': null,
|
||||
'selector-not-notation': null,
|
||||
'import-notation': null,
|
||||
'function-no-unknown': null,
|
||||
|
@@ -15,12 +15,11 @@
|
||||
"files": [
|
||||
"base.json",
|
||||
"node.json",
|
||||
"vue.json",
|
||||
"vue-app.json",
|
||||
"node-server.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/node": "^18.15.11",
|
||||
"unplugin-vue-define-options": "^1.3.3",
|
||||
"vite": "^4.3.0-beta.2"
|
||||
"@types/node": "^20.4.0",
|
||||
"vite": "^4.4.0"
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,5 @@
|
||||
"jsx": "preserve",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"noImplicitAny": false
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -32,25 +32,24 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"vite": "^4.3.0-beta.2"
|
||||
"vite": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"ant-design-vue": "^3.2.17",
|
||||
"dayjs": "^1.11.7",
|
||||
"dotenv": "^16.0.3",
|
||||
"ant-design-vue": "^3.2.20",
|
||||
"dayjs": "^1.11.9",
|
||||
"dotenv": "^16.3.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"less": "^4.1.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"pkg-types": "^1.0.2",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"sass": "^1.60.0",
|
||||
"unocss": "^0.50.6",
|
||||
"unplugin-vue-define-options": "^1.3.3",
|
||||
"pkg-types": "^1.0.3",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.63.6",
|
||||
"unocss": "^0.53.4",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-dts": "^2.2.0",
|
||||
"vite-plugin-dts": "^3.1.0",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-purge-icons": "^0.9.2",
|
||||
|
@@ -21,7 +21,10 @@ function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
return defineConfig(async ({ command, mode }) => {
|
||||
const root = process.cwd();
|
||||
const isBuild = command === 'build';
|
||||
const { VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv(mode, root);
|
||||
const { VITE_PUBLIC_PATH, VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv(
|
||||
mode,
|
||||
root,
|
||||
);
|
||||
|
||||
const defineData = await createDefineData(root);
|
||||
const plugins = await createPlugins({
|
||||
@@ -35,6 +38,7 @@ function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
const pathResolve = (pathname: string) => resolve(root, '.', pathname);
|
||||
|
||||
const applicationConfig: UserConfig = {
|
||||
base: VITE_PUBLIC_PATH,
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
@@ -69,6 +73,8 @@ function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
cssTarget: 'chrome80',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// 入口文件名
|
||||
entryFileNames: 'assets/[name].js',
|
||||
manualChunks: {
|
||||
vue: ['vue', 'pinia', 'vue-router'],
|
||||
antd: ['ant-design-vue', '@ant-design/icons-vue'],
|
||||
@@ -87,7 +93,7 @@ function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
plugins,
|
||||
};
|
||||
|
||||
const mergedConfig = mergeConfig(commonConfig, applicationConfig);
|
||||
const mergedConfig = mergeConfig(commonConfig(mode), applicationConfig);
|
||||
|
||||
return mergeConfig(mergedConfig, overrides);
|
||||
});
|
||||
|
@@ -2,12 +2,12 @@ import { presetTypography, presetUno } from 'unocss';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import { type UserConfig } from 'vite';
|
||||
|
||||
const commonConfig: UserConfig = {
|
||||
const commonConfig: (mode: string) => UserConfig = (mode) => ({
|
||||
server: {
|
||||
host: true,
|
||||
},
|
||||
esbuild: {
|
||||
drop: ['console', 'debugger'],
|
||||
drop: mode === 'production' ? ['console', 'debugger'] : [],
|
||||
},
|
||||
build: {
|
||||
reportCompressedSize: false,
|
||||
@@ -22,6 +22,6 @@ const commonConfig: UserConfig = {
|
||||
presets: [presetUno(), presetTypography()],
|
||||
}),
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
export { commonConfig };
|
||||
|
@@ -14,7 +14,7 @@ interface DefineOptions {
|
||||
function definePackageConfig(defineOptions: DefineOptions = {}) {
|
||||
const { overrides = {} } = defineOptions;
|
||||
const root = process.cwd();
|
||||
return defineConfig(async () => {
|
||||
return defineConfig(async ({ mode }) => {
|
||||
const { dependencies = {}, peerDependencies = {} } = await readPackageJSON(root);
|
||||
const packageConfig: UserConfig = {
|
||||
build: {
|
||||
@@ -33,7 +33,7 @@ function definePackageConfig(defineOptions: DefineOptions = {}) {
|
||||
}),
|
||||
],
|
||||
};
|
||||
const mergedConfig = mergeConfig(commonConfig, packageConfig);
|
||||
const mergedConfig = mergeConfig(commonConfig(mode), packageConfig);
|
||||
|
||||
return mergeConfig(mergedConfig, overrides);
|
||||
});
|
||||
|
@@ -27,8 +27,8 @@ async function createAppConfigPlugin({
|
||||
return {
|
||||
name: PLUGIN_NAME,
|
||||
async configResolved(_config) {
|
||||
let appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? '';
|
||||
appTitle = appTitle.replace(/\s/g, '_');
|
||||
const appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? '';
|
||||
// appTitle = appTitle.replace(/\s/g, '_').replace(/-/g, '_');
|
||||
publicPath = _config.base;
|
||||
source = await getConfigSource(appTitle);
|
||||
},
|
||||
@@ -37,7 +37,7 @@ async function createAppConfigPlugin({
|
||||
|
||||
const appConfigSrc = `${
|
||||
publicPath || '/'
|
||||
}${GLOBAL_CONFIG_FILE_NAME}?v=${version}-${createContentHash(source)}}`;
|
||||
}${GLOBAL_CONFIG_FILE_NAME}?v=${version}-${createContentHash(source)}`;
|
||||
|
||||
return {
|
||||
html,
|
||||
@@ -74,7 +74,15 @@ async function createAppConfigPlugin({
|
||||
* @param env
|
||||
*/
|
||||
const getVariableName = (title: string) => {
|
||||
return `__PRODUCTION__${title || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
|
||||
function strToHex(str: string) {
|
||||
const result: string[] = [];
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
const hex = str.charCodeAt(i).toString(16);
|
||||
result.push(('000' + hex).slice(-4));
|
||||
}
|
||||
return result.join('').toUpperCase();
|
||||
}
|
||||
return `__PRODUCTION__${strToHex(title) || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
|
||||
};
|
||||
|
||||
async function getConfigSource(appTitle: string) {
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
// @ts-ignore: type unless
|
||||
import DefineOptions from 'unplugin-vue-define-options/vite';
|
||||
import { type PluginOption } from 'vite';
|
||||
import purgeIcons from 'vite-plugin-purge-icons';
|
||||
|
||||
@@ -21,7 +19,7 @@ interface Options {
|
||||
}
|
||||
|
||||
async function createPlugins({ isBuild, root, enableMock, compress, enableAnalyze }: Options) {
|
||||
const vitePlugins: (PluginOption | PluginOption[])[] = [vue(), vueJsx(), DefineOptions()];
|
||||
const vitePlugins: (PluginOption | PluginOption[])[] = [vue(), vueJsx()];
|
||||
|
||||
const appConfigPlugin = await createAppConfigPlugin({ root, isBuild });
|
||||
vitePlugins.push(appConfigPlugin);
|
||||
|
@@ -16,14 +16,14 @@ import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
|
||||
// return pre;
|
||||
// }, [] as any[]);
|
||||
|
||||
const modules = import.meta.globEager('./**/*.ts');
|
||||
const modules = import.meta.glob('./**/*.ts', { eager: true });
|
||||
|
||||
const mockModules: any[] = [];
|
||||
Object.keys(modules).forEach((key) => {
|
||||
if (key.includes('/_')) {
|
||||
return;
|
||||
}
|
||||
mockModules.push(...modules[key].default);
|
||||
mockModules.push(...(modules as Recordable)[key].default);
|
||||
});
|
||||
|
||||
/**
|
||||
|
38
nginx.conf
Normal file
38
nginx.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
#user nobody;
|
||||
worker_processes 1;
|
||||
|
||||
#error_log logs/error.log;
|
||||
#error_log logs/error.log notice;
|
||||
#error_log logs/error.log info;
|
||||
|
||||
#pid logs/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
root /usr/share/nginx/html/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
index index.html;
|
||||
# Enable CORS
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
if ($request_filename ~* ^.*?.(html|htm|js)$) {
|
||||
add_header Cache-Control no-cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
package.json
84
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vben-admin",
|
||||
"version": "2.10.0",
|
||||
"version": "2.10.1",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
@@ -17,10 +17,11 @@
|
||||
},
|
||||
"scripts": {
|
||||
"bootstrap": "pnpm install",
|
||||
"build": "cross-env NODE_ENV=production pnpm vite build",
|
||||
"build:analyze": "pnpm vite build --mode analyze",
|
||||
"build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build",
|
||||
"build:analyze": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode analyze",
|
||||
"build:docker": "vite build --mode docker",
|
||||
"build:no-cache": "pnpm clean:cache && npm run build",
|
||||
"build:test": "pnpm vite build --mode test",
|
||||
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode test",
|
||||
"commit": "czg",
|
||||
"dev": "pnpm vite",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
@@ -28,7 +29,7 @@
|
||||
"lint": "turbo run lint",
|
||||
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
||||
"lint:prettier": "prettier --write .",
|
||||
"lint:stylelint": "stylelint \"**/*.{vue,css,less.scss}\" --fix --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:stylelint": "stylelint \"**/*.{vue,css,less,scss}\" --fix --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"prepare": "husky install",
|
||||
"preview": "npm run build && vite preview",
|
||||
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
|
||||
@@ -67,20 +68,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@iconify/iconify": "^3.1.0",
|
||||
"@logicflow/core": "^1.2.1",
|
||||
"@logicflow/extension": "^1.2.1",
|
||||
"@iconify/iconify": "^3.1.1",
|
||||
"@logicflow/core": "^1.2.9",
|
||||
"@logicflow/extension": "^1.2.9",
|
||||
"@vben/hooks": "workspace:*",
|
||||
"@vue/shared": "^3.2.47",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"@vueuse/shared": "^9.13.0",
|
||||
"@zxcvbn-ts/core": "^2.2.1",
|
||||
"ant-design-vue": "^3.2.17",
|
||||
"axios": "^1.3.5",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"@vueuse/shared": "^10.2.1",
|
||||
"@zxcvbn-ts/core": "^3.0.2",
|
||||
"ant-design-vue": "^4.0.2",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^5.65.12",
|
||||
"cropperjs": "^1.5.13",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"dayjs": "^1.11.9",
|
||||
"echarts": "^5.4.2",
|
||||
"exceljs": "^4.3.0",
|
||||
"intro.js": "^7.0.1",
|
||||
@@ -88,63 +89,62 @@
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"pinia": "2.0.33",
|
||||
"pinia": "2.1.4",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcode": "^1.5.1",
|
||||
"qs": "^6.11.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"showdown": "^2.1.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tinymce": "^5.10.7",
|
||||
"vditor": "^3.9.1",
|
||||
"vue": "^3.2.47",
|
||||
"vditor": "^3.9.4",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-json-pretty": "^2.2.4",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-types": "^5.0.2",
|
||||
"vue-router": "^4.2.3",
|
||||
"vue-types": "^5.1.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vxe-table": "^4.3.11",
|
||||
"vxe-table": "^4.4.5",
|
||||
"vxe-table-plugin-export-xlsx": "^3.0.4",
|
||||
"xe-utils": "^3.5.7",
|
||||
"xe-utils": "^3.5.11",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.5.1",
|
||||
"@commitlint/config-conventional": "^17.4.4",
|
||||
"@iconify/json": "^2.2.46",
|
||||
"@commitlint/cli": "^17.6.6",
|
||||
"@commitlint/config-conventional": "^17.6.6",
|
||||
"@iconify/json": "^2.2.87",
|
||||
"@purge-icons/generated": "^0.9.0",
|
||||
"@types/codemirror": "^5.60.7",
|
||||
"@types/codemirror": "^5.60.8",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/intro.js": "^5.1.1",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/mockjs": "^1.0.7",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@types/qrcode": "^1.5.1",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"@types/showdown": "^2.0.1",
|
||||
"@types/sortablejs": "^1.15.1",
|
||||
"@vben/eslint-config": "workspace:*",
|
||||
"@vben/stylelint-config": "workspace:*",
|
||||
"@vben/ts-config": "workspace:*",
|
||||
"@vben/types": "workspace:*",
|
||||
"@vben/vite-config": "workspace:*",
|
||||
"@vue/compiler-sfc": "^3.2.47",
|
||||
"@vue/test-utils": "^2.3.2",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@vue/test-utils": "^2.4.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cz-git": "^1.6.1",
|
||||
"czg": "^1.6.1",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "13.2.0",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-packagejson": "^2.4.3",
|
||||
"rimraf": "^4.4.1",
|
||||
"turbo": "^1.8.8",
|
||||
"typescript": "^5.0.3",
|
||||
"unbuild": "^1.2.0",
|
||||
"unplugin-vue-define-options": "^1.3.3",
|
||||
"vite": "^4.3.0-beta.2",
|
||||
"lint-staged": "13.2.3",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-packagejson": "^2.4.4",
|
||||
"rimraf": "^5.0.1",
|
||||
"turbo": "^1.10.7",
|
||||
"typescript": "^5.1.6",
|
||||
"unbuild": "^1.2.1",
|
||||
"vite": "^4.4.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vue-tsc": "^1.2.0"
|
||||
"vue-tsc": "^1.8.4"
|
||||
},
|
||||
"packageManager": "pnpm@8.1.0",
|
||||
"engines": {
|
||||
|
@@ -29,8 +29,8 @@
|
||||
"lint": "pnpm eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"vue": "^3.2.47"
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"vue": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vben/types": "workspace:*"
|
||||
|
@@ -1,18 +1,18 @@
|
||||
import type { Ref } from 'vue';
|
||||
import type { ComponentPublicInstance, Ref } from 'vue';
|
||||
import { onBeforeUpdate, shallowRef } from 'vue';
|
||||
|
||||
function useRefs(): {
|
||||
refs: Ref<HTMLElement[]>;
|
||||
setRefs: (index: number) => (el: HTMLElement) => void;
|
||||
function useRefs<T = HTMLElement>(): {
|
||||
refs: Ref<T[]>;
|
||||
setRefs: (index: number) => (el: Element | ComponentPublicInstance | null) => void;
|
||||
} {
|
||||
const refs = shallowRef([]) as Ref<HTMLElement[]>;
|
||||
const refs = shallowRef([]) as Ref<T[]>;
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
refs.value = [];
|
||||
});
|
||||
|
||||
const setRefs = (index: number) => (el: HTMLElement) => {
|
||||
refs.value[index] = el;
|
||||
const setRefs = (index: number) => (el: Element | ComponentPublicInstance | null) => {
|
||||
refs.value[index] = el as T;
|
||||
};
|
||||
|
||||
return {
|
||||
|
5701
pnpm-lock.yaml
generated
5701
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ConfigProvider :locale="getAntdLocale">
|
||||
<ConfigProvider :locale="getAntdLocale" :theme="isDark ? darkTheme : {}">
|
||||
<AppProvider>
|
||||
<RouterView />
|
||||
</AppProvider>
|
||||
@@ -11,11 +11,15 @@
|
||||
import { AppProvider } from '@/components/Application';
|
||||
import { useTitle } from '@/hooks/web/useTitle';
|
||||
import { useLocale } from '@/locales/useLocale';
|
||||
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import { useDarkModeTheme } from '@/hooks/setting/useDarkModeTheme';
|
||||
|
||||
// support Multi-language
|
||||
const { getAntdLocale } = useLocale();
|
||||
|
||||
const { isDark, darkTheme } = useDarkModeTheme();
|
||||
|
||||
// Listening to page changes and dynamically changing site titles
|
||||
useTitle();
|
||||
</script>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { BasicFetchResult } from '/@/api/model/baseModel';
|
||||
|
||||
export interface DemoOptionsItem {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface selectParams {
|
||||
|
@@ -17,7 +17,7 @@ export interface RoleInfo {
|
||||
export interface LoginResultModel {
|
||||
userId: string | number;
|
||||
token: string;
|
||||
role: RoleInfo;
|
||||
roles: RoleInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -4,7 +4,7 @@ import appLogo from './src/AppLogo.vue';
|
||||
import appProvider from './src/AppProvider.vue';
|
||||
import appSearch from './src/search/AppSearch.vue';
|
||||
import appLocalePicker from './src/AppLocalePicker.vue';
|
||||
// import appDarkModeToggle from './src/AppDarkModeToggle.vue';
|
||||
import appDarkModeToggle from './src/AppDarkModeToggle.vue';
|
||||
|
||||
export { useAppProviderContext } from './src/useAppContext';
|
||||
|
||||
@@ -12,4 +12,4 @@ export const AppLogo = withInstall(appLogo);
|
||||
export const AppProvider = withInstall(appProvider);
|
||||
export const AppSearch = withInstall(appSearch);
|
||||
export const AppLocalePicker = withInstall(appLocalePicker);
|
||||
// export const AppDarkModeToggle = withInstall(appDarkModeToggle);
|
||||
export const AppDarkModeToggle = withInstall(appDarkModeToggle);
|
||||
|
@@ -22,5 +22,5 @@ export const buttonProps = {
|
||||
* @default: 14
|
||||
*/
|
||||
iconSize: { type: Number, default: 14 },
|
||||
onClick: { type: Function as PropType<(...args) => any>, default: null },
|
||||
onClick: { type: [Function, Array] as PropType<(() => any) | (() => any)[]>, default: null },
|
||||
};
|
||||
|
@@ -61,7 +61,7 @@
|
||||
|
||||
<CardMeta>
|
||||
<template #title>
|
||||
<TypographyText :content="item.name" :ellipsis="{ tooltip: item.address }" />
|
||||
<TypographyParagraph :content="item.name" :ellipsis="{ tooltip: item.address }" />
|
||||
</template>
|
||||
<template #avatar>
|
||||
<Avatar :src="item.avatar" />
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
const ListItem = List.Item;
|
||||
const CardMeta = Card.Meta;
|
||||
const TypographyText = Typography.Text;
|
||||
const TypographyParagraph = Typography.Paragraph;
|
||||
// 获取slider属性
|
||||
const sliderProp = computed(() => useSlider(4));
|
||||
// 组件接收参数
|
||||
|
@@ -59,12 +59,12 @@
|
||||
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x;
|
||||
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
|
||||
return {
|
||||
...styles,
|
||||
position: 'absolute',
|
||||
width: `${width}px`,
|
||||
left: `${left + 1}px`,
|
||||
top: `${top + 1}px`,
|
||||
zIndex: 9999,
|
||||
...styles, // Not the first, fix options.styles.width not working
|
||||
};
|
||||
});
|
||||
|
||||
@@ -127,11 +127,15 @@
|
||||
}
|
||||
const { items } = props;
|
||||
return (
|
||||
<div class={prefixCls}>
|
||||
<Menu inlineIndent={12} mode="vertical" ref={wrapRef} style={unref(getStyle)}>
|
||||
<Menu
|
||||
inlineIndent={12}
|
||||
mode="vertical"
|
||||
class={prefixCls}
|
||||
ref={wrapRef}
|
||||
style={unref(getStyle)}
|
||||
>
|
||||
{renderMenuItem(items)}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
@@ -147,7 +151,7 @@
|
||||
.item-style() {
|
||||
li {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
height: @default-height;
|
||||
margin: 0 !important;
|
||||
line-height: @default-height;
|
||||
@@ -157,6 +161,8 @@
|
||||
}
|
||||
|
||||
> div {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
@@ -176,10 +182,12 @@
|
||||
width: 156px;
|
||||
margin: 0;
|
||||
border: 1px solid rgb(0 0 0 / 8%);
|
||||
border-radius: 0.25rem;
|
||||
border-radius: 8px;
|
||||
background-clip: padding-box;
|
||||
background-color: @component-background;
|
||||
box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 10%),
|
||||
box-shadow:
|
||||
0 2px 2px 0 rgb(0 0 0 / 14%),
|
||||
0 3px 1px -2px rgb(0 0 0 / 10%),
|
||||
0 1px 5px 0 rgb(0 0 0 / 6%);
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
|
@@ -20,11 +20,12 @@
|
||||
{{ btnText ? btnText : t('component.cropper.selectImage') }}
|
||||
</a-button>
|
||||
|
||||
<CopperModal
|
||||
<CropperModal
|
||||
@register="register"
|
||||
@upload-success="handleUploadSuccess"
|
||||
:uploadApi="uploadApi"
|
||||
:src="sourceValue"
|
||||
:size="size"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -39,7 +40,7 @@
|
||||
watch,
|
||||
PropType,
|
||||
} from 'vue';
|
||||
import CopperModal from './CopperModal.vue';
|
||||
import CropperModal from './CropperModal.vue';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
@@ -54,11 +55,12 @@
|
||||
btnProps: { type: Object as PropType<ButtonProps> },
|
||||
btnText: { type: String, default: '' },
|
||||
uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
|
||||
size: { type: Number, default: 5 },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CropperAvatar',
|
||||
components: { CopperModal, Icon },
|
||||
components: { CropperModal, Icon },
|
||||
props,
|
||||
emits: ['update:value', 'change'],
|
||||
setup(props, { emit, expose }) {
|
||||
|
@@ -130,13 +130,14 @@
|
||||
type: Function as PropType<(params: apiFunParams) => Promise<any>>,
|
||||
},
|
||||
src: { type: String },
|
||||
size: { type: Number },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CropperModal',
|
||||
components: { BasicModal, Space, CropperImage, Upload, Avatar, Tooltip },
|
||||
props,
|
||||
emits: ['uploadSuccess', 'register'],
|
||||
emits: ['uploadSuccess', 'uploadError', 'register'],
|
||||
setup(props, { emit }) {
|
||||
let filename = '';
|
||||
const src = ref(props.src || '');
|
||||
@@ -151,6 +152,10 @@
|
||||
|
||||
// Block upload
|
||||
function handleBeforeUpload(file: File) {
|
||||
if (props.size && file.size > 1024 * 1024 * props.size) {
|
||||
emit('uploadError', { msg: t('component.cropper.imageTooBig') });
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
src.value = '';
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
|
||||
<Drawer @close="onClose" v-bind="getBindValues">
|
||||
<template #title v-if="!$slots.title">
|
||||
<DrawerHeader
|
||||
:title="getMergeProps.title"
|
||||
@@ -58,9 +58,9 @@
|
||||
components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
|
||||
inheritAttrs: false,
|
||||
props: basicProps,
|
||||
emits: ['visible-change', 'ok', 'close', 'register'],
|
||||
emits: ['open-change', 'ok', 'close', 'register'],
|
||||
setup(props, { emit }) {
|
||||
const visibleRef = ref(false);
|
||||
const openRef = ref(false);
|
||||
const attrs = useAttrs();
|
||||
const propsRef = ref<Partial<DrawerProps | null>>(null);
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
const drawerInstance: DrawerInstance = {
|
||||
setDrawerProps: setDrawerProps as any,
|
||||
emitVisible: undefined,
|
||||
emitOpen: undefined,
|
||||
};
|
||||
|
||||
const instance = getCurrentInstance();
|
||||
@@ -85,7 +85,7 @@
|
||||
placement: 'right',
|
||||
...unref(attrs),
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
open: unref(openRef),
|
||||
};
|
||||
opt.title = undefined;
|
||||
const { isDetail, width, wrapClassName, getContainer } = opt;
|
||||
@@ -94,7 +94,7 @@
|
||||
opt.width = '100%';
|
||||
}
|
||||
const detailCls = `${prefixCls}__detail`;
|
||||
opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
|
||||
opt.rootClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
|
||||
|
||||
if (!getContainer) {
|
||||
// TODO type error?
|
||||
@@ -135,19 +135,19 @@
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
() => props.open,
|
||||
(newVal, oldVal) => {
|
||||
if (newVal !== oldVal) visibleRef.value = newVal;
|
||||
if (newVal !== oldVal) openRef.value = newVal;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => visibleRef.value,
|
||||
(visible) => {
|
||||
() => openRef.value,
|
||||
(open) => {
|
||||
nextTick(() => {
|
||||
emit('visible-change', visible);
|
||||
instance && drawerInstance.emitVisible?.(visible, instance.uid);
|
||||
emit('open-change', open);
|
||||
instance && drawerInstance.emitOpen?.(open, instance.uid);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -158,18 +158,18 @@
|
||||
emit('close', e);
|
||||
if (closeFunc && isFunction(closeFunc)) {
|
||||
const res = await closeFunc();
|
||||
visibleRef.value = !res;
|
||||
openRef.value = !res;
|
||||
return;
|
||||
}
|
||||
visibleRef.value = false;
|
||||
openRef.value = false;
|
||||
}
|
||||
|
||||
function setDrawerProps(props: Partial<DrawerProps>): void {
|
||||
// Keep the last setDrawerProps
|
||||
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||
|
||||
if (Reflect.has(props, 'visible')) {
|
||||
visibleRef.value = !!props.visible;
|
||||
if (Reflect.has(props, 'open')) {
|
||||
openRef.value = !!props.open;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ export const basicProps = {
|
||||
title: { type: String, default: '' },
|
||||
loadingText: { type: String },
|
||||
showDetailBack: { type: Boolean, default: true },
|
||||
visible: { type: Boolean },
|
||||
open: { type: Boolean },
|
||||
loading: { type: Boolean },
|
||||
maskClosable: { type: Boolean, default: true },
|
||||
getContainer: {
|
||||
|
@@ -3,23 +3,23 @@ import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
|
||||
import type { ScrollContainerOptions } from '/@/components/Container/index';
|
||||
|
||||
export interface DrawerInstance {
|
||||
setDrawerProps: (props: Partial<DrawerProps> | boolean) => void;
|
||||
emitVisible?: (visible: boolean, uid: number) => void;
|
||||
setDrawerProps: (props: Partial<DrawerProps>) => void;
|
||||
emitOpen?: (open: boolean, uid: number) => void;
|
||||
}
|
||||
|
||||
export interface ReturnMethods extends DrawerInstance {
|
||||
openDrawer: <T = any>(visible?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||
openDrawer: <T = any>(open?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||
closeDrawer: () => void;
|
||||
getVisible?: ComputedRef<boolean>;
|
||||
getOpen?: ComputedRef<boolean>;
|
||||
}
|
||||
|
||||
export type RegisterFn = (drawerInstance: DrawerInstance, uuid?: string) => void;
|
||||
export type RegisterFn = (drawerInstance: DrawerInstance, uuid: number) => void;
|
||||
|
||||
export interface ReturnInnerMethods extends DrawerInstance {
|
||||
closeDrawer: () => void;
|
||||
changeLoading: (loading: boolean) => void;
|
||||
changeOkLoading: (loading: boolean) => void;
|
||||
getVisible?: ComputedRef<boolean>;
|
||||
getOpen?: ComputedRef<boolean>;
|
||||
}
|
||||
|
||||
export type UseDrawerReturnType = [RegisterFn, ReturnMethods];
|
||||
@@ -73,7 +73,7 @@ export interface DrawerProps extends DrawerFooterProps {
|
||||
isDetail?: boolean;
|
||||
loading?: boolean;
|
||||
showDetailBack?: boolean;
|
||||
visible?: boolean;
|
||||
open?: boolean;
|
||||
/**
|
||||
* Built-in ScrollContainer component configuration
|
||||
* @type ScrollContainerOptions
|
||||
@@ -100,7 +100,7 @@ export interface DrawerProps extends DrawerFooterProps {
|
||||
* @default 'body'
|
||||
* @type any ( HTMLElement| () => HTMLElement | string)
|
||||
*/
|
||||
getContainer?: () => HTMLElement | string;
|
||||
getContainer?: string | false | HTMLElement | (() => HTMLElement);
|
||||
|
||||
/**
|
||||
* Whether to show mask or not.
|
||||
@@ -134,6 +134,7 @@ export interface DrawerProps extends DrawerFooterProps {
|
||||
*/
|
||||
wrapClassName?: string;
|
||||
class?: string;
|
||||
rootClassName?: string;
|
||||
/**
|
||||
* Style of wrapper element which **contains mask** compare to `drawerStyle`
|
||||
* @type object
|
||||
@@ -179,7 +180,7 @@ export interface DrawerProps extends DrawerFooterProps {
|
||||
* @type string
|
||||
*/
|
||||
placement?: 'top' | 'right' | 'bottom' | 'left';
|
||||
afterVisibleChange?: (visible?: boolean) => void;
|
||||
afterOpenChange?: (open?: boolean) => void;
|
||||
keyboard?: boolean;
|
||||
/**
|
||||
* Specify a callback that will be called when a user clicks mask, close button or Cancel button.
|
||||
|
@@ -23,7 +23,7 @@ import { error } from '/@/utils/log';
|
||||
|
||||
const dataTransferRef = reactive<any>({});
|
||||
|
||||
const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
const openData = reactive<{ [key: number]: boolean }>({});
|
||||
|
||||
/**
|
||||
* @description: Applicable to separate drawer and call outside
|
||||
@@ -34,9 +34,9 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
}
|
||||
const drawer = ref<DrawerInstance | null>(null);
|
||||
const loaded = ref<Nullable<boolean>>(false);
|
||||
const uid = ref<string>('');
|
||||
const uid = ref<number>(0);
|
||||
|
||||
function register(drawerInstance: DrawerInstance, uuid: string) {
|
||||
function register(drawerInstance: DrawerInstance, uuid: number) {
|
||||
isProdMode() &&
|
||||
tryOnUnmounted(() => {
|
||||
drawer.value = null;
|
||||
@@ -51,8 +51,8 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
drawer.value = drawerInstance;
|
||||
loaded.value = true;
|
||||
|
||||
drawerInstance.emitVisible = (visible: boolean, uid: number) => {
|
||||
visibleData[uid] = visible;
|
||||
drawerInstance.emitOpen = (open: boolean, uid: number) => {
|
||||
openData[uid] = open;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,13 +69,13 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
getInstance()?.setDrawerProps(props);
|
||||
},
|
||||
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uid)];
|
||||
getOpen: computed((): boolean => {
|
||||
return openData[~~unref(uid)];
|
||||
}),
|
||||
|
||||
openDrawer: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
||||
openDrawer: <T = any>(open = true, data?: T, openOnSet = true): void => {
|
||||
getInstance()?.setDrawerProps({
|
||||
visible: visible,
|
||||
open,
|
||||
});
|
||||
if (!data) return;
|
||||
|
||||
@@ -90,7 +90,7 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
}
|
||||
},
|
||||
closeDrawer: () => {
|
||||
getInstance()?.setDrawerProps({ visible: false });
|
||||
getInstance()?.setDrawerProps({ open: false });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ export function useDrawer(): UseDrawerReturnType {
|
||||
export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
||||
const drawerInstanceRef = ref<Nullable<DrawerInstance>>(null);
|
||||
const currentInstance = getCurrentInstance();
|
||||
const uidRef = ref<string>('');
|
||||
const uidRef = ref<number>(0);
|
||||
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useDrawerInner() can only be used inside setup() or functional components!');
|
||||
@@ -115,7 +115,7 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
||||
return instance;
|
||||
};
|
||||
|
||||
const register = (modalInstance: DrawerInstance, uuid: string) => {
|
||||
const register = (modalInstance: DrawerInstance, uuid: number) => {
|
||||
isProdMode() &&
|
||||
tryOnUnmounted(() => {
|
||||
drawerInstanceRef.value = null;
|
||||
@@ -145,12 +145,12 @@ export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
|
||||
changeOkLoading: (loading = true) => {
|
||||
getInstance()?.setDrawerProps({ confirmLoading: loading });
|
||||
},
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uidRef)];
|
||||
getOpen: computed((): boolean => {
|
||||
return openData[~~unref(uidRef)];
|
||||
}),
|
||||
|
||||
closeDrawer: () => {
|
||||
getInstance()?.setDrawerProps({ visible: false });
|
||||
getInstance()?.setDrawerProps({ open: false });
|
||||
},
|
||||
|
||||
setDrawerProps: (props: Partial<DrawerProps>) => {
|
||||
|
@@ -14,6 +14,7 @@
|
||||
<a-popconfirm
|
||||
v-if="popconfirm && item.popConfirm"
|
||||
v-bind="getPopConfirmAttrs(item.popConfirm)"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<template #icon v-if="item.popConfirm.icon">
|
||||
<Icon :icon="item.popConfirm.icon" />
|
||||
|
@@ -117,15 +117,18 @@
|
||||
const getSchema = computed((): FormSchema[] => {
|
||||
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
|
||||
for (const schema of schemas) {
|
||||
const { defaultValue, component, isHandleDateDefaultValue = true } = schema;
|
||||
const { defaultValue, component, componentProps,isHandleDateDefaultValue = true } = schema;
|
||||
// handle date type
|
||||
if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
|
||||
const valueFormat =componentProps ? componentProps['valueFormat'] : null;
|
||||
if (!Array.isArray(defaultValue)) {
|
||||
schema.defaultValue = dateUtil(defaultValue);
|
||||
schema.defaultValue = valueFormat
|
||||
? dateUtil(defaultValue).format(valueFormat)
|
||||
: dateUtil(defaultValue);
|
||||
} else {
|
||||
const def: any[] = [];
|
||||
defaultValue.forEach((item) => {
|
||||
def.push(dateUtil(item));
|
||||
def.push(valueFormat ? dateUtil(item).format(valueFormat) : dateUtil(item));
|
||||
});
|
||||
schema.defaultValue = def;
|
||||
}
|
||||
|
@@ -65,6 +65,7 @@ componentMap.set('MonthPicker', DatePicker.MonthPicker);
|
||||
componentMap.set('RangePicker', DatePicker.RangePicker);
|
||||
componentMap.set('WeekPicker', DatePicker.WeekPicker);
|
||||
componentMap.set('TimePicker', TimePicker);
|
||||
componentMap.set('TimeRangePicker', TimePicker.TimeRangePicker);
|
||||
componentMap.set('StrengthMeter', StrengthMeter);
|
||||
componentMap.set('IconPicker', IconPicker);
|
||||
componentMap.set('InputCountDown', CountdownInput);
|
||||
|
@@ -55,6 +55,7 @@
|
||||
valueField: propTypes.string.def('value'),
|
||||
immediate: propTypes.bool.def(true),
|
||||
alwaysLoad: propTypes.bool.def(false),
|
||||
options: propTypes.array.def([]),
|
||||
},
|
||||
emits: ['options-change', 'change', 'update:value'],
|
||||
setup(props, { emit }) {
|
||||
@@ -71,7 +72,7 @@
|
||||
const getOptions = computed(() => {
|
||||
const { labelField, valueField, numberToString } = props;
|
||||
|
||||
return unref(options).reduce((prev, next: any) => {
|
||||
let data = unref(options).reduce((prev, next: any) => {
|
||||
if (next) {
|
||||
const value = get(next, valueField);
|
||||
prev.push({
|
||||
@@ -82,6 +83,7 @@
|
||||
}
|
||||
return prev;
|
||||
}, [] as OptionsItem[]);
|
||||
return data.length > 0 ? data : props.options;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
@@ -142,6 +144,7 @@
|
||||
}
|
||||
|
||||
function handleChange(_, ...args) {
|
||||
emit('change', args[0] ? args[0].value : undefined);
|
||||
emitData.value = args;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-tree-select v-bind="getAttrs" @change="handleChange">
|
||||
<a-tree-select v-bind="getAttrs" @change="handleChange" :field-names="fieldNames">
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
@@ -11,7 +11,16 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { type Recordable } from '@vben/types';
|
||||
import { type PropType, computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
|
||||
import {
|
||||
type PropType,
|
||||
computed,
|
||||
defineComponent,
|
||||
watchEffect,
|
||||
watch,
|
||||
ref,
|
||||
onMounted,
|
||||
unref,
|
||||
} from 'vue';
|
||||
import { TreeSelect } from 'ant-design-vue';
|
||||
import { isArray, isFunction } from '/@/utils/is';
|
||||
import { get } from 'lodash-es';
|
||||
@@ -26,6 +35,9 @@
|
||||
params: { type: Object },
|
||||
immediate: { type: Boolean, default: true },
|
||||
resultField: propTypes.string.def(''),
|
||||
labelField: propTypes.string.def('title'),
|
||||
valueField: propTypes.string.def('value'),
|
||||
childrenField: propTypes.string.def('children'),
|
||||
},
|
||||
emits: ['options-change', 'change'],
|
||||
setup(props, { attrs, emit }) {
|
||||
@@ -38,6 +50,11 @@
|
||||
...attrs,
|
||||
};
|
||||
});
|
||||
const fieldNames = {
|
||||
children: props.childrenField,
|
||||
value: props.valueField,
|
||||
label: props.labelField,
|
||||
};
|
||||
|
||||
function handleChange(...args) {
|
||||
emit('change', ...args);
|
||||
@@ -64,7 +81,7 @@
|
||||
|
||||
async function fetch() {
|
||||
const { api } = props;
|
||||
if (!api || !isFunction(api)) return;
|
||||
if (!api || !isFunction(api) || loading.value) return;
|
||||
loading.value = true;
|
||||
treeData.value = [];
|
||||
let result;
|
||||
@@ -82,7 +99,7 @@
|
||||
isFirstLoaded.value = true;
|
||||
emit('options-change', treeData.value);
|
||||
}
|
||||
return { getAttrs, loading, handleChange };
|
||||
return { getAttrs, loading, handleChange, fieldNames };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -41,7 +41,6 @@
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { ColEx } from '../types/index';
|
||||
//import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
|
||||
import { defineComponent, computed, PropType } from 'vue';
|
||||
import { Form, Col } from 'ant-design-vue';
|
||||
import { Button, ButtonProps } from '/@/components/Button';
|
||||
|
@@ -159,7 +159,10 @@
|
||||
const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
|
||||
? rulesMessageJoinLabel
|
||||
: globalRulesMessageJoinLabel;
|
||||
const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;
|
||||
const assertLabel = joinLabel ? label : '';
|
||||
const defaultMsg = component
|
||||
? createPlaceholderMessage(component) + assertLabel
|
||||
: assertLabel;
|
||||
|
||||
function validator(rule: any, value: any) {
|
||||
const msg = rule.message || defaultMsg;
|
||||
@@ -299,7 +302,7 @@
|
||||
return <Comp {...compAttr} />;
|
||||
}
|
||||
const compSlot = isFunction(renderComponentContent)
|
||||
? { ...renderComponentContent(unref(getValues)) }
|
||||
? { ...renderComponentContent(unref(getValues), { disabled: unref(getDisable) }) }
|
||||
: {
|
||||
default: () => renderComponentContent,
|
||||
};
|
||||
@@ -333,7 +336,7 @@
|
||||
const { itemProps, slot, render, field, suffix, component } = props.schema;
|
||||
const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
|
||||
const { colon } = props.formProps;
|
||||
|
||||
const opts = { disabled: unref(getDisable) };
|
||||
if (component === 'Divider') {
|
||||
return (
|
||||
<Col span={24}>
|
||||
@@ -343,9 +346,9 @@
|
||||
} else {
|
||||
const getContent = () => {
|
||||
return slot
|
||||
? getSlot(slots, slot, unref(getValues))
|
||||
? getSlot(slots, slot, unref(getValues), opts)
|
||||
: render
|
||||
? render(unref(getValues))
|
||||
? render(unref(getValues), opts)
|
||||
: renderComponent();
|
||||
};
|
||||
|
||||
@@ -382,8 +385,8 @@
|
||||
}
|
||||
|
||||
return () => {
|
||||
const { colProps = {}, colSlot, renderColContent, component } = props.schema;
|
||||
if (!componentMap.has(component)) {
|
||||
const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema;
|
||||
if (!componentMap.has(component) && !slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -391,12 +394,13 @@
|
||||
const realColProps = { ...baseColProps, ...colProps };
|
||||
const { isIfShow, isShow } = getShow();
|
||||
const values = unref(getValues);
|
||||
const opts = { disabled: unref(getDisable) };
|
||||
|
||||
const getContent = () => {
|
||||
return colSlot
|
||||
? getSlot(slots, colSlot, values)
|
||||
? getSlot(slots, colSlot, values, opts)
|
||||
: renderColContent
|
||||
? renderColContent(values)
|
||||
? renderColContent(values, opts)
|
||||
: renderItem();
|
||||
};
|
||||
|
||||
|
@@ -78,7 +78,6 @@ export const NO_AUTO_LINK_COMPONENTS: ComponentType[] = [
|
||||
'Upload',
|
||||
'ApiTransfer',
|
||||
'ApiTree',
|
||||
'ApiSelect',
|
||||
'ApiTreeSelect',
|
||||
'ApiRadioGroup',
|
||||
'ApiCascader',
|
||||
|
@@ -87,23 +87,31 @@ export function useFormEvents({
|
||||
|
||||
Object.keys(formModel).forEach((key) => {
|
||||
const schema = unref(getSchema).find((item) => item.field === key);
|
||||
const isInput = schema?.component && defaultValueComponents.includes(schema.component);
|
||||
const defaultValue = cloneDeep(defaultValueRef.value[key]);
|
||||
formModel[key] = isInput ? defaultValue || '' : defaultValue;
|
||||
const defaultValueObj = schema?.defaultValueObj;
|
||||
const fieldKeys = Object.keys(defaultValueObj || {});
|
||||
if (fieldKeys.length) {
|
||||
fieldKeys.map((field) => {
|
||||
formModel[field] = defaultValueObj![field];
|
||||
});
|
||||
}
|
||||
formModel[key] = getDefaultValue(schema, defaultValueRef, key);
|
||||
});
|
||||
nextTick(() => clearValidate());
|
||||
|
||||
emit('reset', toRaw(formModel));
|
||||
submitOnReset && handleSubmit();
|
||||
}
|
||||
|
||||
// 获取表单fields
|
||||
const getAllFields = () =>
|
||||
unref(getSchema)
|
||||
.map((item) => [...(item.fields || []), item.field])
|
||||
.flat(1)
|
||||
.filter(Boolean);
|
||||
/**
|
||||
* @description: Set form value
|
||||
*/
|
||||
async function setFieldsValue(values: Recordable): Promise<void> {
|
||||
const fields = unref(getSchema)
|
||||
.map((item) => item.field)
|
||||
.filter(Boolean);
|
||||
const fields = getAllFields();
|
||||
|
||||
// key 支持 a.b.c 的嵌套写法
|
||||
const delimiter = '.';
|
||||
@@ -113,7 +121,7 @@ export function useFormEvents({
|
||||
fields.forEach((key) => {
|
||||
const schema = unref(getSchema).find((item) => item.field === key);
|
||||
let value = get(values, key);
|
||||
const hasKey = !!get(values, key);
|
||||
const hasKey = Reflect.has(values, key);
|
||||
|
||||
value = handleInputNumberValue(schema?.component, value);
|
||||
const { componentProps } = schema || {};
|
||||
@@ -210,21 +218,22 @@ export function useFormEvents({
|
||||
first = false,
|
||||
) {
|
||||
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
|
||||
|
||||
const addSchemaIds: string[] = Array.isArray(schema)
|
||||
? schema.map((item) => item.field)
|
||||
: [schema.field];
|
||||
if (schemaList.find((item) => addSchemaIds.includes(item.field))) {
|
||||
error('There are schemas that have already been added');
|
||||
return;
|
||||
}
|
||||
const index = schemaList.findIndex((schema) => schema.field === prefixField);
|
||||
const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]);
|
||||
if (!prefixField || index === -1 || first) {
|
||||
first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList);
|
||||
schemaRef.value = schemaList;
|
||||
_setDefaultValue(schema);
|
||||
return;
|
||||
}
|
||||
if (index !== -1) {
|
||||
} else if (index !== -1) {
|
||||
schemaList.splice(index + 1, 0, ..._schemaList);
|
||||
}
|
||||
_setDefaultValue(schema);
|
||||
|
||||
schemaRef.value = schemaList;
|
||||
_setDefaultValue(schema);
|
||||
}
|
||||
|
||||
async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
|
||||
@@ -334,8 +343,14 @@ export function useFormEvents({
|
||||
return unref(formElRef)?.validateFields(nameList);
|
||||
}
|
||||
|
||||
async function validate(nameList?: NamePath[] | undefined) {
|
||||
return await unref(formElRef)?.validate(nameList);
|
||||
async function validate(nameList?: NamePath[] | false | undefined) {
|
||||
let _nameList: any;
|
||||
if (nameList === undefined) {
|
||||
_nameList = getAllFields();
|
||||
} else {
|
||||
_nameList = nameList === Array.isArray(nameList) ? nameList : undefined;
|
||||
}
|
||||
return await unref(formElRef)?.validate(_nameList);
|
||||
}
|
||||
|
||||
async function clearValidate(name?: string | string[]) {
|
||||
@@ -385,3 +400,29 @@ export function useFormEvents({
|
||||
scrollToField,
|
||||
};
|
||||
}
|
||||
|
||||
function getDefaultValue(
|
||||
schema: FormSchema | undefined,
|
||||
defaultValueRef: UseFormActionContext['defaultValueRef'],
|
||||
key: string,
|
||||
) {
|
||||
let defaultValue = cloneDeep(defaultValueRef.value[key]);
|
||||
const isInput = checkIsInput(schema);
|
||||
if (isInput) {
|
||||
return defaultValue || '';
|
||||
}
|
||||
if (!defaultValue && schema && checkIsRangeSlider(schema)) {
|
||||
defaultValue = [0, 0];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function checkIsRangeSlider(schema: FormSchema) {
|
||||
if (schema.component === 'Slider' && schema.componentProps && schema.componentProps.range) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function checkIsInput(schema?: FormSchema) {
|
||||
return schema?.component && defaultValueComponents.includes(schema.component);
|
||||
}
|
||||
|
@@ -115,19 +115,37 @@ export function useFormValues({
|
||||
|
||||
const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format];
|
||||
|
||||
values[startTimeKey] = dateUtil(startTime).format(startTimeFormat);
|
||||
values[endTimeKey] = dateUtil(endTime).format(endTimeFormat);
|
||||
values[startTimeKey] = formatTime(startTime, startTimeFormat);
|
||||
values[endTimeKey] = formatTime(endTime, endTimeFormat);
|
||||
Reflect.deleteProperty(values, field);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
function formatTime(time: string, format: string) {
|
||||
if (format === 'timestamp') {
|
||||
return dateUtil(time).unix();
|
||||
} else if (format === 'timestampStartDay') {
|
||||
return dateUtil(time).startOf('day').unix();
|
||||
}
|
||||
return dateUtil(time).format(format);
|
||||
}
|
||||
|
||||
function initDefault() {
|
||||
const schemas = unref(getSchema);
|
||||
const obj: Recordable = {};
|
||||
schemas.forEach((item) => {
|
||||
const { defaultValue } = item;
|
||||
const { defaultValue, defaultValueObj } = item;
|
||||
const fieldKeys = Object.keys(defaultValueObj || {});
|
||||
if (fieldKeys.length) {
|
||||
fieldKeys.map((field) => {
|
||||
obj[field] = defaultValueObj![field];
|
||||
if (formModel[field] === undefined) {
|
||||
formModel[field] = defaultValueObj![field];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isNullOrUnDef(defaultValue)) {
|
||||
obj[item.field] = defaultValue;
|
||||
|
||||
|
@@ -39,7 +39,7 @@ export interface FormActionType {
|
||||
first?: boolean | undefined,
|
||||
) => Promise<void>;
|
||||
validateFields: (nameList?: NamePath[]) => Promise<any>;
|
||||
validate: (nameList?: NamePath[]) => Promise<any>;
|
||||
validate: (nameList?: NamePath[] | false) => Promise<any>;
|
||||
scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -123,15 +123,21 @@ export interface FormProps {
|
||||
transformDateFunc?: (date: any) => string;
|
||||
colon?: boolean;
|
||||
}
|
||||
export type RenderOpts = {
|
||||
disabled: boolean;
|
||||
[key: string]: any;
|
||||
};
|
||||
export interface FormSchema {
|
||||
// Field name
|
||||
field: string;
|
||||
// Extra Fields name[]
|
||||
fields?: string[];
|
||||
// Event name triggered by internal value change, default change
|
||||
changeEvent?: string;
|
||||
// Variable name bound to v-model Default value
|
||||
valueField?: string;
|
||||
// Label name
|
||||
label: string | VNode;
|
||||
label?: string | VNode;
|
||||
// Auxiliary text
|
||||
subLabel?: string;
|
||||
// Help text on the right side of the text
|
||||
@@ -175,6 +181,9 @@ export interface FormSchema {
|
||||
// 默认值
|
||||
defaultValue?: any;
|
||||
|
||||
// 额外默认值数组对象
|
||||
defaultValueObj?: { [key: string]: any };
|
||||
|
||||
// 是否自动处理与时间相关组件的默认值
|
||||
isHandleDateDefaultValue?: boolean;
|
||||
|
||||
@@ -188,13 +197,19 @@ export interface FormSchema {
|
||||
show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
|
||||
|
||||
// Render the content in the form-item tag
|
||||
render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
|
||||
render?: (
|
||||
renderCallbackParams: RenderCallbackParams,
|
||||
opts: RenderOpts,
|
||||
) => VNode | VNode[] | string;
|
||||
|
||||
// Rendering col content requires outer wrapper form-item
|
||||
renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
|
||||
renderColContent?: (
|
||||
renderCallbackParams: RenderCallbackParams,
|
||||
opts: RenderOpts,
|
||||
) => VNode | VNode[] | string;
|
||||
|
||||
renderComponentContent?:
|
||||
| ((renderCallbackParams: RenderCallbackParams) => any)
|
||||
| ((renderCallbackParams: RenderCallbackParams, opts: RenderOpts) => any)
|
||||
| VNode
|
||||
| VNode[]
|
||||
| string;
|
||||
|
@@ -106,6 +106,7 @@ export type ComponentType =
|
||||
| 'RangePicker'
|
||||
| 'WeekPicker'
|
||||
| 'TimePicker'
|
||||
| 'TimeRangePicker'
|
||||
| 'Switch'
|
||||
| 'StrengthMeter'
|
||||
| 'Upload'
|
||||
|
@@ -29,8 +29,8 @@
|
||||
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open,
|
||||
.ant-menu-submenu-active,
|
||||
.ant-menu-submenu-title:hover {
|
||||
color: #fff;
|
||||
background-color: @top-menu-active-bg-color !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ant-menu-item:hover,
|
||||
@@ -68,7 +68,7 @@
|
||||
}
|
||||
|
||||
.ant-menu-inline.ant-menu-sub {
|
||||
box-shadow: unset !important;
|
||||
transition: unset;
|
||||
box-shadow: unset !important;
|
||||
}
|
||||
}
|
||||
|
@@ -34,9 +34,9 @@
|
||||
:loading-tip="getProps.loadingTip"
|
||||
:minHeight="getProps.minHeight"
|
||||
:height="getWrapperHeight"
|
||||
:visible="visibleRef"
|
||||
:open="openRef"
|
||||
:modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
|
||||
v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
|
||||
v-bind="omit(getProps.wrapperProps, 'open', 'height', 'modalFooterHeight')"
|
||||
@ext-height="handleExtHeight"
|
||||
@height-change="handleHeightChange"
|
||||
>
|
||||
@@ -79,9 +79,9 @@
|
||||
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
|
||||
inheritAttrs: false,
|
||||
props: basicProps,
|
||||
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
|
||||
emits: ['open-change', 'height-change', 'cancel', 'ok', 'register', 'update:open'],
|
||||
setup(props, { emit, attrs }) {
|
||||
const visibleRef = ref(false);
|
||||
const openRef = ref(false);
|
||||
const propsRef = ref<Partial<ModalProps> | null>(null);
|
||||
const modalWrapperRef = ref<any>(null);
|
||||
const { prefixCls } = useDesign('basic-modal');
|
||||
@@ -90,7 +90,7 @@
|
||||
const extHeightRef = ref(0);
|
||||
const modalMethods: ModalMethods = {
|
||||
setModalProps,
|
||||
emitVisible: undefined,
|
||||
emitOpen: undefined,
|
||||
redoModalHeight: () => {
|
||||
nextTick(() => {
|
||||
if (unref(modalWrapperRef)) {
|
||||
@@ -123,7 +123,7 @@
|
||||
const getProps = computed((): Recordable => {
|
||||
const opt = {
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
open: unref(openRef),
|
||||
okButtonProps: undefined,
|
||||
cancelButtonProps: undefined,
|
||||
title: undefined,
|
||||
@@ -138,10 +138,10 @@
|
||||
const attr = {
|
||||
...attrs,
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
open: unref(openRef),
|
||||
};
|
||||
attr['wrapClassName'] = `${attr?.['wrapClassName'] || ''} ${unref(getWrapClassName)}`;
|
||||
|
||||
attr['wrapClassName'] =
|
||||
`${attr?.['wrapClassName'] || ''} ${unref(getWrapClassName)}` + 'vben-basic-modal-wrap';
|
||||
if (unref(fullScreenRef)) {
|
||||
return omit(attr, ['height', 'title']);
|
||||
}
|
||||
@@ -154,16 +154,16 @@
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
visibleRef.value = !!props.visible;
|
||||
openRef.value = !!props.open;
|
||||
fullScreenRef.value = !!props.defaultFullscreen;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => unref(visibleRef),
|
||||
() => unref(openRef),
|
||||
(v) => {
|
||||
emit('visible-change', v);
|
||||
emit('update:visible', v);
|
||||
instance && modalMethods.emitVisible?.(v, instance.uid);
|
||||
emit('open-change', v);
|
||||
emit('update:open', v);
|
||||
instance && modalMethods.emitOpen?.(v, instance.uid);
|
||||
nextTick(() => {
|
||||
if (props.scrollTop && v && unref(modalWrapperRef)) {
|
||||
(unref(modalWrapperRef) as any).scrollTop();
|
||||
@@ -182,11 +182,11 @@
|
||||
if ((e.target as HTMLElement)?.classList?.contains(prefixCls + '-close--custom')) return;
|
||||
if (props.closeFunc && isFunction(props.closeFunc)) {
|
||||
const isClose: boolean = await props.closeFunc();
|
||||
visibleRef.value = !isClose;
|
||||
openRef.value = !isClose;
|
||||
return;
|
||||
}
|
||||
|
||||
visibleRef.value = false;
|
||||
openRef.value = false;
|
||||
emit('cancel', e);
|
||||
}
|
||||
|
||||
@@ -196,8 +196,8 @@
|
||||
function setModalProps(props: Partial<ModalProps>): void {
|
||||
// Keep the last setModalProps
|
||||
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
|
||||
if (Reflect.has(props, 'visible')) {
|
||||
visibleRef.value = !!props.visible;
|
||||
if (Reflect.has(props, 'open')) {
|
||||
openRef.value = !!props.open;
|
||||
}
|
||||
if (Reflect.has(props, 'defaultFullscreen')) {
|
||||
fullScreenRef.value = !!props.defaultFullscreen;
|
||||
@@ -230,7 +230,7 @@
|
||||
fullScreenRef,
|
||||
getMergeProps,
|
||||
handleOk,
|
||||
visibleRef,
|
||||
openRef,
|
||||
omit,
|
||||
modalWrapperRef,
|
||||
handleExtHeight,
|
||||
|
@@ -11,10 +11,10 @@ export default defineComponent({
|
||||
props: basicProps as any,
|
||||
emits: ['cancel'],
|
||||
setup(props, { slots, emit }) {
|
||||
const { visible, draggable, destroyOnClose } = toRefs(props);
|
||||
const { open, draggable, destroyOnClose } = toRefs(props);
|
||||
const attrs = useAttrs();
|
||||
useModalDragMove({
|
||||
visible,
|
||||
open,
|
||||
destroyOnClose,
|
||||
draggable,
|
||||
});
|
||||
|
@@ -32,7 +32,7 @@
|
||||
minHeight: { type: Number, default: 200 },
|
||||
height: { type: Number },
|
||||
footerOffset: { type: Number, default: 0 },
|
||||
visible: { type: Boolean },
|
||||
open: { type: Boolean },
|
||||
fullScreen: { type: Boolean },
|
||||
loadingTip: { type: String },
|
||||
};
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
let stopElResizeFn: AnyFunction = () => {};
|
||||
|
||||
useWindowSizeFn(setModalHeight.bind(null, false));
|
||||
useWindowSizeFn(setModalHeight.bind(null));
|
||||
|
||||
useMutationObserver(
|
||||
spinRef,
|
||||
@@ -112,8 +112,8 @@
|
||||
|
||||
async function setModalHeight() {
|
||||
// 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
|
||||
// 加上这个,就必须在使用的时候传递父级的visible
|
||||
if (!props.visible) return;
|
||||
// 加上这个,就必须在使用的时候传递父级的open
|
||||
if (!props.open) return;
|
||||
const wrapperRefDom = unref(wrapperRef);
|
||||
if (!wrapperRefDom) return;
|
||||
|
||||
|
@@ -24,7 +24,7 @@ import { error } from '/@/utils/log';
|
||||
|
||||
const dataTransfer = reactive<any>({});
|
||||
|
||||
const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
const openData = reactive<{ [key: number]: boolean }>({});
|
||||
|
||||
/**
|
||||
* @description: Applicable to independent modal and call outside
|
||||
@@ -32,9 +32,9 @@ const visibleData = reactive<{ [key: number]: boolean }>({});
|
||||
export function useModal(): UseModalReturnType {
|
||||
const modal = ref<Nullable<ModalMethods>>(null);
|
||||
const loaded = ref<Nullable<boolean>>(false);
|
||||
const uid = ref<string>('');
|
||||
const uid = ref<number>(0);
|
||||
|
||||
function register(modalMethod: ModalMethods, uuid: string) {
|
||||
function register(modalMethod: ModalMethods, uuid: number) {
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useModal() can only be used inside setup() or functional components!');
|
||||
}
|
||||
@@ -43,14 +43,14 @@ export function useModal(): UseModalReturnType {
|
||||
onUnmounted(() => {
|
||||
modal.value = null;
|
||||
loaded.value = false;
|
||||
dataTransfer[unref(uid)] = null;
|
||||
dataTransfer[String(unref(uid))] = null;
|
||||
});
|
||||
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
|
||||
|
||||
modal.value = modalMethod;
|
||||
loaded.value = true;
|
||||
modalMethod.emitVisible = (visible: boolean, uid: number) => {
|
||||
visibleData[uid] = visible;
|
||||
modalMethod.emitOpen = (open: boolean, uid: number) => {
|
||||
openData[uid] = open;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,17 +67,17 @@ export function useModal(): UseModalReturnType {
|
||||
getInstance()?.setModalProps(props);
|
||||
},
|
||||
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uid)];
|
||||
getOpen: computed((): boolean => {
|
||||
return openData[~~unref(uid)];
|
||||
}),
|
||||
|
||||
redoModalHeight: () => {
|
||||
getInstance()?.redoModalHeight?.();
|
||||
},
|
||||
|
||||
openModal: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
||||
openModal: <T = any>(open = true, data?: T, openOnSet = true): void => {
|
||||
getInstance()?.setModalProps({
|
||||
visible: visible,
|
||||
open,
|
||||
});
|
||||
|
||||
if (!data) return;
|
||||
@@ -94,7 +94,7 @@ export function useModal(): UseModalReturnType {
|
||||
},
|
||||
|
||||
closeModal: () => {
|
||||
getInstance()?.setModalProps({ visible: false });
|
||||
getInstance()?.setModalProps({ open: false });
|
||||
},
|
||||
};
|
||||
return [register, methods];
|
||||
@@ -103,7 +103,7 @@ export function useModal(): UseModalReturnType {
|
||||
export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
const modalInstanceRef = ref<Nullable<ModalMethods>>(null);
|
||||
const currentInstance = getCurrentInstance();
|
||||
const uidRef = ref<string>('');
|
||||
const uidRef = ref<number>(0);
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(modalInstanceRef);
|
||||
@@ -113,7 +113,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
return instance;
|
||||
};
|
||||
|
||||
const register = (modalInstance: ModalMethods, uuid: string) => {
|
||||
const register = (modalInstance: ModalMethods, uuid: number) => {
|
||||
isProdMode() &&
|
||||
tryOnUnmounted(() => {
|
||||
modalInstanceRef.value = null;
|
||||
@@ -138,8 +138,8 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
changeLoading: (loading = true) => {
|
||||
getInstance()?.setModalProps({ loading });
|
||||
},
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uidRef)];
|
||||
getOpen: computed((): boolean => {
|
||||
return openData[~~unref(uidRef)];
|
||||
}),
|
||||
|
||||
changeOkLoading: (loading = true) => {
|
||||
@@ -147,7 +147,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
},
|
||||
|
||||
closeModal: () => {
|
||||
getInstance()?.setModalProps({ visible: false });
|
||||
getInstance()?.setModalProps({ open: false });
|
||||
},
|
||||
|
||||
setModalProps: (props: Partial<ModalProps>) => {
|
||||
|
@@ -4,7 +4,7 @@ import { useTimeoutFn } from '@vben/hooks';
|
||||
export interface UseModalDragMoveContext {
|
||||
draggable: Ref<boolean>;
|
||||
destroyOnClose: Ref<boolean | undefined> | undefined;
|
||||
visible: Ref<boolean>;
|
||||
open: Ref<boolean>;
|
||||
}
|
||||
|
||||
export function useModalDragMove(context: UseModalDragMoveContext) {
|
||||
@@ -97,7 +97,7 @@ export function useModalDragMove(context: UseModalDragMoveContext) {
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
if (!unref(context.visible) || !unref(context.draggable)) {
|
||||
if (!unref(context.open) || !unref(context.draggable)) {
|
||||
return;
|
||||
}
|
||||
useTimeoutFn(() => {
|
||||
|
@@ -1,11 +1,9 @@
|
||||
/* stylelint-disable media-feature-range-notation */
|
||||
.fullscreen-modal {
|
||||
overflow: hidden;
|
||||
|
||||
.ant-modal {
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
inset: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
|
||||
@@ -13,9 +11,13 @@
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal {
|
||||
.vben-basic-modal-wrap .ant-modal {
|
||||
width: 520px;
|
||||
padding-bottom: 0;
|
||||
|
||||
@@ -53,6 +55,7 @@
|
||||
}
|
||||
|
||||
&-content {
|
||||
padding: 12px 8px !important;
|
||||
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
|
||||
}
|
||||
|
||||
@@ -63,8 +66,10 @@
|
||||
}
|
||||
|
||||
&-close {
|
||||
font-weight: normal;
|
||||
width: auto !important;
|
||||
outline: none;
|
||||
background: transparent !important;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&-close-x {
|
||||
@@ -110,16 +115,19 @@
|
||||
.ant-modal-confirm .ant-modal-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 600px) {
|
||||
.ant-modal {
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 540px) {
|
||||
.ant-modal {
|
||||
top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 480px) {
|
||||
.ant-modal {
|
||||
top: 10px;
|
||||
|
@@ -6,7 +6,7 @@ import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
|
||||
export const modalProps = {
|
||||
visible: { type: Boolean },
|
||||
open: { type: Boolean },
|
||||
scrollTop: { type: Boolean, default: true },
|
||||
height: { type: Number },
|
||||
minHeight: { type: Number },
|
||||
@@ -73,7 +73,7 @@ export const basicProps = Object.assign({}, modalProps, {
|
||||
|
||||
title: { type: String },
|
||||
|
||||
visible: { type: Boolean },
|
||||
open: { type: Boolean },
|
||||
|
||||
width: [String, Number] as PropType<string | number>,
|
||||
|
||||
|
@@ -5,16 +5,16 @@ import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
|
||||
*/
|
||||
export interface ModalMethods {
|
||||
setModalProps: (props: Partial<ModalProps>) => void;
|
||||
emitVisible?: (visible: boolean, uid: number) => void;
|
||||
emitOpen?: (open: boolean, uid: number) => void;
|
||||
redoModalHeight?: () => void;
|
||||
}
|
||||
|
||||
export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void;
|
||||
export type RegisterFn = (modalMethods: ModalMethods, uuid: number) => void;
|
||||
|
||||
export interface ReturnMethods extends ModalMethods {
|
||||
openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void;
|
||||
closeModal: () => void;
|
||||
getVisible?: ComputedRef<boolean>;
|
||||
getOpen?: ComputedRef<boolean>;
|
||||
}
|
||||
|
||||
export type UseModalReturnType = [RegisterFn, ReturnMethods];
|
||||
@@ -23,7 +23,7 @@ export interface ReturnInnerMethods extends ModalMethods {
|
||||
closeModal: () => void;
|
||||
changeLoading: (loading: boolean) => void;
|
||||
changeOkLoading: (loading: boolean) => void;
|
||||
getVisible?: ComputedRef<boolean>;
|
||||
getOpen?: ComputedRef<boolean>;
|
||||
redoModalHeight: () => void;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export interface ModalProps {
|
||||
// 是否可以进行全屏
|
||||
canFullscreen?: boolean;
|
||||
defaultFullscreen?: boolean;
|
||||
visible?: boolean;
|
||||
open?: boolean;
|
||||
// 温馨提醒信息
|
||||
helpMessage: string | string[];
|
||||
|
||||
@@ -203,7 +203,7 @@ export interface ModalWrapperProps {
|
||||
modalFooterHeight: number;
|
||||
minHeight: number;
|
||||
height: number;
|
||||
visible: boolean;
|
||||
open: boolean;
|
||||
fullScreen: boolean;
|
||||
useWrapper: boolean;
|
||||
}
|
||||
|
@@ -5,5 +5,3 @@ import pageWrapper from './src/PageWrapper.vue';
|
||||
|
||||
export const PageFooter = withInstall(pageFooter);
|
||||
export const PageWrapper = withInstall(pageWrapper);
|
||||
|
||||
export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight';
|
||||
|
@@ -9,20 +9,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script lang="ts" setup>
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
export default defineComponent({
|
||||
defineOptions({
|
||||
name: 'PageFooter',
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
});
|
||||
|
||||
const { prefixCls } = useDesign('page-footer');
|
||||
const { getCalcContentWidth } = useMenuSetting();
|
||||
return { prefixCls, getCalcContentWidth };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-page-footer';
|
||||
|
@@ -4,6 +4,7 @@
|
||||
:ghost="ghost"
|
||||
:title="title"
|
||||
v-bind="omit($attrs, 'class')"
|
||||
:style="getHeaderStyle"
|
||||
ref="headerRef"
|
||||
v-if="getShowHeader"
|
||||
>
|
||||
@@ -32,16 +33,17 @@
|
||||
</PageFooter>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
CSSProperties,
|
||||
PropType,
|
||||
provide,
|
||||
defineComponent,
|
||||
computed,
|
||||
watch,
|
||||
ref,
|
||||
unref,
|
||||
useAttrs,
|
||||
useSlots,
|
||||
} from 'vue';
|
||||
|
||||
import PageFooter from './PageFooter.vue';
|
||||
@@ -51,27 +53,34 @@
|
||||
import { omit } from 'lodash-es';
|
||||
import { PageHeader } from 'ant-design-vue';
|
||||
import { useContentHeight } from '/@/hooks/web/useContentHeight';
|
||||
import { PageWrapperFixedHeightKey } from '..';
|
||||
import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
|
||||
import { PageWrapperFixedHeightKey } from '/@/enums/pageEnum';
|
||||
|
||||
export default defineComponent({
|
||||
defineOptions({
|
||||
name: 'PageWrapper',
|
||||
components: { PageFooter, PageHeader },
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
title: propTypes.string,
|
||||
dense: propTypes.bool,
|
||||
ghost: propTypes.bool,
|
||||
headerSticky: propTypes.bool,
|
||||
headerStyle: Object as PropType<CSSProperties>,
|
||||
content: propTypes.string,
|
||||
contentStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
},
|
||||
contentBackground: propTypes.bool,
|
||||
contentFullHeight: propTypes.bool,
|
||||
contentFullHeight: propTypes.bool.def(false),
|
||||
contentClass: propTypes.string,
|
||||
fixedHeight: propTypes.bool,
|
||||
upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
|
||||
},
|
||||
setup(props, { slots, attrs }) {
|
||||
});
|
||||
|
||||
const attrs = useAttrs();
|
||||
const slots = useSlots();
|
||||
|
||||
const wrapperRef = ref(null);
|
||||
const headerRef = ref(null);
|
||||
const contentRef = ref(null);
|
||||
@@ -107,6 +116,20 @@
|
||||
];
|
||||
});
|
||||
|
||||
const { headerHeightRef } = useLayoutHeight();
|
||||
const getHeaderStyle = computed((): CSSProperties => {
|
||||
const { headerSticky } = props;
|
||||
if (!headerSticky) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
position: 'sticky',
|
||||
top: `${unref(headerHeightRef)}px`,
|
||||
...props.headerStyle,
|
||||
};
|
||||
});
|
||||
|
||||
const getShowHeader = computed(
|
||||
() => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length,
|
||||
);
|
||||
@@ -152,23 +175,6 @@
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
getContentStyle,
|
||||
wrapperRef,
|
||||
headerRef,
|
||||
contentRef,
|
||||
footerRef,
|
||||
getClass,
|
||||
getHeaderSlots,
|
||||
prefixCls,
|
||||
getShowHeader,
|
||||
getShowFooter,
|
||||
omit,
|
||||
getContentClass,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-page-wrapper';
|
||||
|
49
src/components/Prompt/dialog.vue
Normal file
49
src/components/Prompt/dialog.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<Modal
|
||||
v-model:open="open"
|
||||
:title="title"
|
||||
@ok="handleSubmit"
|
||||
:destroyOnClose="true"
|
||||
:width="width || '500px'"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<div class="pt-5 pr-3px">
|
||||
<BasicForm @register="register" />
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
|
||||
const open = ref<boolean>(true);
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
addFormSchemas: FormSchema[];
|
||||
onOK?: Fn;
|
||||
width?: string;
|
||||
labelWidth?: number;
|
||||
layout?: 'horizontal' | 'vertical' | 'inline';
|
||||
}>();
|
||||
|
||||
const [register, { validate }] = useForm({
|
||||
schemas: props.addFormSchemas,
|
||||
showActionButtonGroup: false,
|
||||
labelWidth: props.labelWidth || 80,
|
||||
layout: props.layout || 'horizontal',
|
||||
});
|
||||
|
||||
async function handleSubmit() {
|
||||
const row = await validate();
|
||||
if (props.onOK) {
|
||||
await props.onOK(row.txt);
|
||||
}
|
||||
open.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
39
src/components/Prompt/index.ts
Normal file
39
src/components/Prompt/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { createVNode, VNode, defineComponent, h, render, reactive } from 'vue';
|
||||
import { PromptProps, genFormSchemas } from './state';
|
||||
import Dialog from './dialog.vue';
|
||||
|
||||
export function createPrompt(props: PromptProps) {
|
||||
let vm: Nullable<VNode> = null;
|
||||
const data = reactive({
|
||||
...props,
|
||||
addFormSchemas: genFormSchemas({
|
||||
label: props.label,
|
||||
required: props.required,
|
||||
inputType: props.inputType,
|
||||
defaultValue: props.defaultValue,
|
||||
}),
|
||||
});
|
||||
const DialogWrap = defineComponent({
|
||||
render() {
|
||||
return h(Dialog, { ...data } as any);
|
||||
},
|
||||
});
|
||||
|
||||
vm = createVNode(DialogWrap);
|
||||
|
||||
render(vm, document.createElement('div'));
|
||||
|
||||
function close() {
|
||||
if (vm?.el && vm.el.parentNode) {
|
||||
vm.el.parentNode.removeChild(vm.el);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
vm,
|
||||
close,
|
||||
get $el() {
|
||||
return vm?.el as HTMLElement;
|
||||
},
|
||||
};
|
||||
}
|
69
src/components/Prompt/state.ts
Normal file
69
src/components/Prompt/state.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
|
||||
type InputType = 'InputTextArea' | 'InputNumber' | 'Input';
|
||||
export interface PromptProps {
|
||||
title: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
onOK?: Fn;
|
||||
inputType?: InputType;
|
||||
labelWidth?: number;
|
||||
width?: string;
|
||||
layout?: 'horizontal' | 'vertical' | 'inline';
|
||||
defaultValue?: string | number;
|
||||
}
|
||||
|
||||
interface genFormSchemasProps {
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
inputType?: InputType;
|
||||
defaultValue?: string | number;
|
||||
}
|
||||
|
||||
const inputTypeMap: {
|
||||
[key in InputType]: {
|
||||
colProps: { span: number; offset?: number };
|
||||
componentProps: FormSchema['componentProps'];
|
||||
};
|
||||
} = {
|
||||
InputTextArea: {
|
||||
colProps: { span: 23 },
|
||||
componentProps: {
|
||||
placeholder: '请输入内容',
|
||||
autoSize: { minRows: 2, maxRows: 6 },
|
||||
maxlength: 255,
|
||||
showCount: true,
|
||||
},
|
||||
},
|
||||
InputNumber: {
|
||||
colProps: { span: 20, offset: 2 },
|
||||
componentProps: {
|
||||
placeholder: '请输入数字',
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
Input: {
|
||||
colProps: { span: 20, offset: 2 },
|
||||
componentProps: {
|
||||
placeholder: '请输入内容',
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function genFormSchemas({
|
||||
label = '备注信息',
|
||||
required = true,
|
||||
inputType = 'InputTextArea',
|
||||
defaultValue = '',
|
||||
}: genFormSchemasProps) {
|
||||
const formSchema: FormSchema = {
|
||||
field: 'txt',
|
||||
component: inputType,
|
||||
label,
|
||||
defaultValue,
|
||||
required: Boolean(required),
|
||||
...inputTypeMap[inputType],
|
||||
};
|
||||
return [formSchema];
|
||||
}
|
@@ -20,9 +20,10 @@
|
||||
placement="right"
|
||||
:overlayClassName="`${prefixCls}-menu-popover`"
|
||||
v-else
|
||||
:visible="getIsOpend"
|
||||
@visible-change="handleVisibleChange"
|
||||
:open="getIsOpend"
|
||||
@on-open-change="handleVisibleChange"
|
||||
:overlayStyle="getOverlayStyle"
|
||||
:overlayInnerStyle="{ padding: 0 }"
|
||||
:align="{ offset: [0, 0] }"
|
||||
>
|
||||
<div :class="getSubClass" v-bind="getEvents(false)">
|
||||
|
@@ -7,13 +7,13 @@
|
||||
|
||||
.light-border {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
width: 2px;
|
||||
content: '';
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
@@ -37,16 +37,16 @@
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 12px 20px;
|
||||
transition: all @transition-time @ease-in-out;
|
||||
color: @menu-dark-subsidiary-color;
|
||||
cursor: pointer;
|
||||
transition: all @transition-time @ease-in-out;
|
||||
|
||||
&-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 18px;
|
||||
transition: transform @transition-time @ease-in-out;
|
||||
transform: translateY(-50%) rotate(-90deg);
|
||||
transition: transform @transition-time @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,8 @@
|
||||
}
|
||||
|
||||
&-selected {
|
||||
color: #fff;
|
||||
background-color: @primary-color !important;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,8 +78,8 @@
|
||||
|
||||
&-selected {
|
||||
z-index: 2;
|
||||
color: @primary-color;
|
||||
background-color: fade(@primary-color, 10);
|
||||
color: @primary-color;
|
||||
|
||||
.light-border();
|
||||
}
|
||||
@@ -91,15 +91,15 @@
|
||||
.content();
|
||||
.content() {
|
||||
.@{menu-prefix-cls} {
|
||||
position: relative;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: @font-size-base;
|
||||
color: @text-color-base;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
color: @text-color-base;
|
||||
font-size: @font-size-base;
|
||||
list-style: none;
|
||||
|
||||
// .collapse-transition {
|
||||
// transition: @transition-time height ease-in-out, @transition-time padding-top ease-in-out,
|
||||
@@ -125,15 +125,15 @@
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: @font-size-base;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
font-size: @font-size-base;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
@@ -218,8 +218,8 @@
|
||||
&-light&-vertical &-item {
|
||||
&-active:not(.@{menu-prefix-cls}-submenu) {
|
||||
z-index: 2;
|
||||
color: @primary-color;
|
||||
background-color: fade(@primary-color, 10);
|
||||
color: @primary-color;
|
||||
|
||||
.light-border();
|
||||
}
|
||||
@@ -239,12 +239,12 @@
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
content: '';
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
@@ -254,8 +254,8 @@
|
||||
&-dark&-vertical &-submenu-title {
|
||||
color: @menu-dark-subsidiary-color;
|
||||
&-active:not(.@{menu-prefix-cls}-submenu) {
|
||||
color: #fff !important;
|
||||
background-color: @primary-color !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -267,16 +267,16 @@
|
||||
> li.@{menu-prefix-cls}-item-active,
|
||||
.@{menu-prefix-cls}-submenu-active {
|
||||
position: relative;
|
||||
color: #fff !important;
|
||||
background-color: @sider-dark-darken-bg-color !important;
|
||||
color: #fff !important;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
content: '';
|
||||
background-color: @primary-color;
|
||||
}
|
||||
|
||||
@@ -289,8 +289,8 @@
|
||||
&-dark&-vertical &-submenu &-item {
|
||||
&-active,
|
||||
&-active:hover {
|
||||
color: #fff;
|
||||
border-right: none;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -28,22 +28,22 @@
|
||||
.@{simple-prefix-cls} {
|
||||
&-sub-title {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&-tag {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: calc(50% - 8px);
|
||||
right: 30px;
|
||||
display: inline-block;
|
||||
padding: 2px 3px;
|
||||
margin-right: 4px;
|
||||
padding: 2px 3px;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
line-height: 14px;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
|
||||
&--collapse {
|
||||
top: 6px !important;
|
||||
|
@@ -21,12 +21,15 @@
|
||||
:rowClassName="getRowClassName"
|
||||
v-show="getEmptyDataIsShowTable"
|
||||
@change="handleTableChange"
|
||||
@resizeColumn="setColumnWidth"
|
||||
>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
<template #headerCell="{ column }">
|
||||
<slot name="headerCell" v-bind="{ column }">
|
||||
<HeaderCell :column="column" />
|
||||
</slot>
|
||||
</template>
|
||||
<!-- 增加对antdv3.x兼容 -->
|
||||
<template #bodyCell="data">
|
||||
@@ -49,7 +52,7 @@
|
||||
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
|
||||
import { Table } from 'ant-design-vue';
|
||||
import { BasicForm, useForm } from '/@/components/Form/index';
|
||||
import { PageWrapperFixedHeightKey } from '/@/components/Page';
|
||||
import { PageWrapperFixedHeightKey } from '/@/enums/pageEnum';
|
||||
import HeaderCell from './components/HeaderCell.vue';
|
||||
import { InnerHandlers } from './types/table';
|
||||
|
||||
@@ -74,15 +77,7 @@
|
||||
import { isFunction } from '/@/utils/is';
|
||||
import { warn } from '/@/utils/log';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicTable',
|
||||
components: {
|
||||
Table,
|
||||
BasicForm,
|
||||
HeaderCell,
|
||||
},
|
||||
props: basicProps,
|
||||
emits: [
|
||||
const events = [
|
||||
'fetch-success',
|
||||
'fetch-error',
|
||||
'selection-change',
|
||||
@@ -99,7 +94,17 @@
|
||||
'expanded-rows-change',
|
||||
'change',
|
||||
'columns-change',
|
||||
],
|
||||
];
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicTable',
|
||||
components: {
|
||||
Table,
|
||||
BasicForm,
|
||||
HeaderCell,
|
||||
},
|
||||
props: basicProps,
|
||||
emits: events,
|
||||
setup(props, { attrs, emit, slots, expose }) {
|
||||
const tableElRef = ref(null);
|
||||
const tableData = ref([]);
|
||||
@@ -184,6 +189,8 @@
|
||||
getViewColumns,
|
||||
getColumns,
|
||||
setCacheColumnsByField,
|
||||
setCacheColumns,
|
||||
setColumnWidth,
|
||||
setColumns,
|
||||
getColumnsRef,
|
||||
getCacheColumns,
|
||||
@@ -323,6 +330,7 @@
|
||||
getSize: () => {
|
||||
return unref(getBindValues).size as SizeType;
|
||||
},
|
||||
setCacheColumns,
|
||||
};
|
||||
createTableContext({ ...tableAction, wrapRef, getBindValues });
|
||||
|
||||
@@ -339,6 +347,7 @@
|
||||
handleSearchInfoChange,
|
||||
getEmptyDataIsShowTable,
|
||||
handleTableChange,
|
||||
setColumnWidth,
|
||||
getRowClassName,
|
||||
wrapRef,
|
||||
tableAction,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span>
|
||||
<span class="edit-header-cell">
|
||||
<slot></slot>
|
||||
{{ title }}
|
||||
<FormOutlined />
|
||||
|
@@ -1,11 +1,4 @@
|
||||
<template>
|
||||
<EditTableHeaderCell v-if="getIsEdit">
|
||||
{{ getTitle }}
|
||||
</EditTableHeaderCell>
|
||||
<span v-else>{{ getTitle }}</span>
|
||||
<BasicHelp v-if="getHelpMessage" :text="getHelpMessage" :class="`${prefixCls}__help`" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
<script lang="tsx">
|
||||
import type { PropType } from 'vue';
|
||||
import type { BasicColumn } from '../types/table';
|
||||
import { defineComponent, computed } from 'vue';
|
||||
@@ -29,10 +22,29 @@
|
||||
const { prefixCls } = useDesign('basic-table-header-cell');
|
||||
|
||||
const getIsEdit = computed(() => !!props.column?.edit);
|
||||
const getTitle = computed(() => props.column?.customTitle || props.column?.title);
|
||||
const getTitle = computed(() => {
|
||||
const column = props.column;
|
||||
if (typeof column.customHeaderRender === 'function') {
|
||||
return column.customHeaderRender(props.column);
|
||||
}
|
||||
return props.column?.customTitle || props.column?.title;
|
||||
});
|
||||
const getHelpMessage = computed(() => props.column?.helpMessage);
|
||||
|
||||
return { prefixCls, getIsEdit, getTitle, getHelpMessage };
|
||||
return () => {
|
||||
return (
|
||||
<div>
|
||||
{getIsEdit.value ? (
|
||||
<EditTableHeaderCell>{getTitle.value}</EditTableHeaderCell>
|
||||
) : (
|
||||
<span class="default-header-cell">{getTitle.value}</span>
|
||||
)}
|
||||
{getHelpMessage.value && (
|
||||
<BasicHelp text={getHelpMessage.value} class={`${prefixCls}__help`} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@@ -33,7 +33,7 @@ export const CellComponent: FunctionalComponent = (
|
||||
Popover,
|
||||
{
|
||||
overlayClassName: 'edit-cell-rule-popover',
|
||||
visible: !!popoverVisible,
|
||||
open: !!popoverVisible,
|
||||
...(getPopupContainer ? { getPopupContainer } : {}),
|
||||
},
|
||||
{
|
||||
|
@@ -483,6 +483,7 @@
|
||||
}
|
||||
.@{prefix-cls} {
|
||||
position: relative;
|
||||
min-height: 24px; //设置高度让其始终可被hover
|
||||
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { BasicColumn } from '/@/components/Table/src/types/table';
|
||||
|
||||
import { h, Ref } from 'vue';
|
||||
import { h, Ref, toRaw } from 'vue';
|
||||
|
||||
import EditableCell from './EditableCell.vue';
|
||||
import { isArray } from '/@/utils/is';
|
||||
@@ -13,7 +13,7 @@ interface Params {
|
||||
|
||||
export function renderEditCell(column: BasicColumn) {
|
||||
return ({ text: value, record, index }: Params) => {
|
||||
record.onValid = async () => {
|
||||
toRaw(record).onValid = async () => {
|
||||
if (isArray(record?.validCbs)) {
|
||||
const validFns = (record?.validCbs || []).map((fn) => fn());
|
||||
const res = await Promise.all(validFns);
|
||||
@@ -23,7 +23,7 @@ export function renderEditCell(column: BasicColumn) {
|
||||
}
|
||||
};
|
||||
|
||||
record.onEdit = async (edit: boolean, submit = false) => {
|
||||
toRaw(record).onEdit = async (edit: boolean, submit = false) => {
|
||||
if (!submit) {
|
||||
record.editable = edit;
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<Popover
|
||||
placement="bottomLeft"
|
||||
trigger="click"
|
||||
@visible-change="handleVisibleChange"
|
||||
@open-change="handleVisibleChange"
|
||||
:overlayClassName="`${prefixCls}__cloumn-list`"
|
||||
:getPopupContainer="getPopupContainer"
|
||||
>
|
||||
@@ -99,7 +99,7 @@
|
||||
</Tooltip>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
|
||||
import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table';
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
@@ -159,6 +159,10 @@
|
||||
|
||||
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
|
||||
let inited = false;
|
||||
// 是否当前的setColums触发的
|
||||
let isSetColumnsFromThis = false;
|
||||
// 是否当前组件触发的setProps
|
||||
let isSetPropsFromThis = false;
|
||||
|
||||
const cachePlainOptions = ref<Options[]>([]);
|
||||
const plainOptions = ref<Options[] | any>([]);
|
||||
@@ -172,7 +176,8 @@
|
||||
checkedList: [],
|
||||
defaultCheckList: [],
|
||||
});
|
||||
|
||||
/** 缓存初始化props */
|
||||
let cacheTableProps: Partial<BasicTableProps<any>> = {};
|
||||
const checkIndex = ref(false);
|
||||
const checkSelect = ref(false);
|
||||
|
||||
@@ -185,7 +190,9 @@
|
||||
watchEffect(() => {
|
||||
const columns = table.getColumns();
|
||||
setTimeout(() => {
|
||||
if (columns.length && !state.isInit) {
|
||||
if (isSetColumnsFromThis) {
|
||||
isSetColumnsFromThis = false;
|
||||
} else if (columns.length) {
|
||||
init();
|
||||
}
|
||||
}, 0);
|
||||
@@ -193,6 +200,11 @@
|
||||
|
||||
watchEffect(() => {
|
||||
const values = unref(getValues);
|
||||
if (isSetPropsFromThis) {
|
||||
isSetPropsFromThis = false;
|
||||
} else {
|
||||
cacheTableProps = cloneDeep(values);
|
||||
}
|
||||
checkIndex.value = !!values.showIndexColumn;
|
||||
checkSelect.value = !!values.rowSelection;
|
||||
});
|
||||
@@ -209,8 +221,17 @@
|
||||
return ret;
|
||||
}
|
||||
|
||||
function init() {
|
||||
const columns = getColumns();
|
||||
async function init(isReset = false) {
|
||||
// Sortablejs存在bug,不知道在哪个步骤中会向el append了一个childNode,因此这里先清空childNode
|
||||
// 有可能复现上述问题的操作:拖拽一个元素,快速的上下移动,最后放到最后的位置中松手
|
||||
plainOptions.value = [];
|
||||
const columnListEl = unref(columnListRef);
|
||||
if (columnListEl && (columnListEl as any).$el) {
|
||||
const el = (columnListEl as any).$el as Element;
|
||||
Array.from(el.children).forEach((item) => el.removeChild(item));
|
||||
}
|
||||
await nextTick();
|
||||
const columns = isReset ? cloneDeep(cachePlainOptions.value) : getColumns();
|
||||
|
||||
const checkList = table
|
||||
.getColumns({ ignoreAction: true, ignoreIndex: true })
|
||||
@@ -221,31 +242,25 @@
|
||||
return item.dataIndex || item.title;
|
||||
})
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
if (!plainOptions.value.length) {
|
||||
plainOptions.value = columns;
|
||||
plainSortOptions.value = columns;
|
||||
cachePlainOptions.value = columns;
|
||||
// 更新缓存配置
|
||||
table.setCacheColumns?.(columns);
|
||||
!isReset && (cachePlainOptions.value = cloneDeep(columns));
|
||||
state.defaultCheckList = checkList;
|
||||
} else {
|
||||
// const fixedColumns = columns.filter((item) =>
|
||||
// Reflect.has(item, 'fixed')
|
||||
// ) as BasicColumn[];
|
||||
|
||||
unref(plainOptions).forEach((item: BasicColumn) => {
|
||||
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
|
||||
if (findItem) {
|
||||
item.fixed = findItem.fixed;
|
||||
}
|
||||
});
|
||||
}
|
||||
state.isInit = true;
|
||||
state.checkedList = checkList;
|
||||
// 是否列展示全选
|
||||
state.checkAll = checkList.length === columns.length;
|
||||
inited = false;
|
||||
handleVisibleChange();
|
||||
}
|
||||
|
||||
// checkAll change
|
||||
function onCheckAllChange(e: CheckboxChangeEvent) {
|
||||
const checkList = plainOptions.value.map((item) => item.value);
|
||||
const checkList = plainSortOptions.value.map((item) => item.value);
|
||||
plainSortOptions.value.forEach(
|
||||
(item) => ((item as BasicColumn).defaultHidden = !e.target.checked),
|
||||
);
|
||||
if (e.target.checked) {
|
||||
state.checkedList = checkList;
|
||||
setColumns(checkList);
|
||||
@@ -270,6 +285,9 @@
|
||||
checkedList.sort((prev, next) => {
|
||||
return sortList.indexOf(prev) - sortList.indexOf(next);
|
||||
});
|
||||
unref(plainSortOptions).forEach((item) => {
|
||||
(item as BasicColumn).defaultHidden = !checkedList.includes(item.value);
|
||||
});
|
||||
setColumns(checkedList);
|
||||
}
|
||||
|
||||
@@ -277,11 +295,14 @@
|
||||
let sortableOrder: string[] = [];
|
||||
// reset columns
|
||||
function reset() {
|
||||
state.checkedList = [...state.defaultCheckList];
|
||||
state.checkAll = true;
|
||||
plainOptions.value = unref(cachePlainOptions);
|
||||
plainSortOptions.value = unref(cachePlainOptions);
|
||||
setColumns(table.getCacheColumns());
|
||||
setColumns(cachePlainOptions.value);
|
||||
init(true);
|
||||
checkIndex.value = !!cacheTableProps.showIndexColumn;
|
||||
checkSelect.value = !!cacheTableProps.rowSelection;
|
||||
table.setProps({
|
||||
showIndexColumn: checkIndex.value,
|
||||
rowSelection: checkSelect.value ? defaultRowSelection : undefined,
|
||||
});
|
||||
sortable.sort(sortableOrder);
|
||||
}
|
||||
|
||||
@@ -316,12 +337,7 @@
|
||||
}
|
||||
|
||||
plainSortOptions.value = columns;
|
||||
|
||||
setColumns(
|
||||
columns
|
||||
.map((col: Options) => col.value)
|
||||
.filter((value: string) => state.checkedList.includes(value)),
|
||||
);
|
||||
setColumns(columns.filter((item) => state.checkedList.includes(item.value)));
|
||||
},
|
||||
});
|
||||
// 记录原始order 序列
|
||||
@@ -332,6 +348,8 @@
|
||||
|
||||
// Control whether the serial number column is displayed
|
||||
function handleIndexCheckChange(e: CheckboxChangeEvent) {
|
||||
isSetPropsFromThis = true;
|
||||
isSetColumnsFromThis = true;
|
||||
table.setProps({
|
||||
showIndexColumn: e.target.checked,
|
||||
});
|
||||
@@ -339,6 +357,8 @@
|
||||
|
||||
// Control whether the check box is displayed
|
||||
function handleSelectCheckChange(e: CheckboxChangeEvent) {
|
||||
isSetPropsFromThis = true;
|
||||
isSetColumnsFromThis = true;
|
||||
table.setProps({
|
||||
rowSelection: e.target.checked ? defaultRowSelection : undefined,
|
||||
});
|
||||
@@ -360,11 +380,14 @@
|
||||
if (isFixed && !item.width) {
|
||||
item.width = 100;
|
||||
}
|
||||
updateSortOption(item);
|
||||
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
|
||||
setColumns(columns);
|
||||
}
|
||||
|
||||
function setColumns(columns: BasicColumn[] | string[]) {
|
||||
isSetPropsFromThis = true;
|
||||
isSetColumnsFromThis = true;
|
||||
table.setColumns(columns);
|
||||
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
|
||||
const visible =
|
||||
@@ -384,6 +407,14 @@
|
||||
: getParentContainer();
|
||||
}
|
||||
|
||||
function updateSortOption(column: BasicColumn) {
|
||||
plainSortOptions.value.forEach((item) => {
|
||||
if (item.value === column.dataIndex) {
|
||||
Object.assign(item, column);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
t,
|
||||
...toRefs(state),
|
||||
@@ -471,6 +502,7 @@
|
||||
}
|
||||
|
||||
.ant-checkbox-group {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
min-width: 260px;
|
||||
// flex-wrap: wrap;
|
||||
|
@@ -15,7 +15,7 @@ function handleItem(item: BasicColumn, ellipsis: boolean) {
|
||||
item.align = item.align || DEFAULT_ALIGN;
|
||||
if (ellipsis) {
|
||||
if (!key) {
|
||||
item.key = dataIndex;
|
||||
item.key = typeof dataIndex == 'object' ? dataIndex.join('-') : dataIndex;
|
||||
}
|
||||
if (!isBoolean(item.ellipsis)) {
|
||||
Object.assign(item, {
|
||||
@@ -260,14 +260,26 @@ export function useColumns(
|
||||
function getCacheColumns() {
|
||||
return cacheColumns;
|
||||
}
|
||||
function setCacheColumns(columns: BasicColumn[]) {
|
||||
if (!isArray(columns)) return;
|
||||
cacheColumns = columns.filter((item) => !item.flag);
|
||||
}
|
||||
/**
|
||||
* 拖拽列宽修改列的宽度
|
||||
*/
|
||||
function setColumnWidth(w: number, col: BasicColumn) {
|
||||
col.width = w;
|
||||
}
|
||||
|
||||
return {
|
||||
getColumnsRef,
|
||||
getCacheColumns,
|
||||
getColumns,
|
||||
setColumns,
|
||||
setColumnWidth,
|
||||
getViewColumns,
|
||||
setCacheColumnsByField,
|
||||
setCacheColumns,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -42,7 +42,7 @@ export function useCustomRow(
|
||||
if (!rowSelection || !clickToRowSelect) return;
|
||||
const keys = getSelectRowKeys() || [];
|
||||
const key = getKey(record, rowKey, unref(getAutoCreateKey));
|
||||
if (!key) return;
|
||||
if (key === null) return;
|
||||
|
||||
const isCheckbox = rowSelection.type === 'checkbox';
|
||||
if (isCheckbox) {
|
||||
@@ -55,7 +55,8 @@ export function useCustomRow(
|
||||
const checkBox = tr.querySelector('input[type=checkbox]');
|
||||
if (!checkBox || checkBox.hasAttribute('disabled')) return;
|
||||
if (!keys.includes(key)) {
|
||||
setSelectedRowKeys([...keys, key]);
|
||||
keys.push(key);
|
||||
setSelectedRowKeys(keys);
|
||||
return;
|
||||
}
|
||||
const keyIndex = keys.findIndex((item) => item === key);
|
||||
|
@@ -208,7 +208,7 @@ export function useDataSource(
|
||||
|
||||
function insertTableDataRecord(
|
||||
record: Recordable | Recordable[],
|
||||
index: number,
|
||||
index?: number,
|
||||
): Recordable[] | undefined {
|
||||
// if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
|
||||
index = index ?? dataSourceRef.value?.length;
|
||||
@@ -349,7 +349,7 @@ export function useDataSource(
|
||||
}
|
||||
|
||||
function setTableData<T = Recordable>(values: T[]) {
|
||||
dataSourceRef.value = values;
|
||||
dataSourceRef.value = values as Recordable[];
|
||||
}
|
||||
|
||||
function getDataSource<T = Recordable>() {
|
||||
|
@@ -4,13 +4,14 @@ import { computed, ComputedRef, nextTick, Ref, ref, toRaw, unref, watch } from '
|
||||
import { ROW_KEY } from '../const';
|
||||
import { omit } from 'lodash-es';
|
||||
import { findNodeAll } from '/@/utils/helper/treeHelper';
|
||||
import type { Key } from 'ant-design-vue/lib/table/interface';
|
||||
|
||||
export function useRowSelection(
|
||||
propsRef: ComputedRef<BasicTableProps>,
|
||||
tableData: Ref<Recordable[]>,
|
||||
emit: EmitType,
|
||||
) {
|
||||
const selectedRowKeysRef = ref<string[]>([]);
|
||||
const selectedRowKeysRef = ref<Key[]>([]);
|
||||
const selectedRowRef = ref<Recordable[]>([]);
|
||||
|
||||
const getRowSelectionRef = computed((): TableRowSelection | null => {
|
||||
@@ -21,7 +22,7 @@ export function useRowSelection(
|
||||
|
||||
return {
|
||||
selectedRowKeys: unref(selectedRowKeysRef),
|
||||
onChange: (selectedRowKeys: string[]) => {
|
||||
onChange: (selectedRowKeys: Key[]) => {
|
||||
setSelectedRowKeys(selectedRowKeys);
|
||||
},
|
||||
...omit(rowSelection, ['onChange']),
|
||||
@@ -30,7 +31,7 @@ export function useRowSelection(
|
||||
|
||||
watch(
|
||||
() => unref(propsRef).rowSelection?.selectedRowKeys,
|
||||
(v: string[]) => {
|
||||
(v?: Key[]) => {
|
||||
setSelectedRowKeys(v);
|
||||
},
|
||||
);
|
||||
@@ -62,8 +63,8 @@ export function useRowSelection(
|
||||
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
|
||||
});
|
||||
|
||||
function setSelectedRowKeys(rowKeys: string[]) {
|
||||
selectedRowKeysRef.value = rowKeys;
|
||||
function setSelectedRowKeys(rowKeys?: Key[]) {
|
||||
selectedRowKeysRef.value = rowKeys || [];
|
||||
const allSelectedRows = findNodeAll(
|
||||
toRaw(unref(tableData)).concat(toRaw(unref(selectedRowRef))),
|
||||
(item) => rowKeys?.includes(item[unref(getRowKey) as string]),
|
||||
@@ -72,7 +73,7 @@ export function useRowSelection(
|
||||
},
|
||||
);
|
||||
const trueSelectedRows: any[] = [];
|
||||
rowKeys?.forEach((key: string) => {
|
||||
rowKeys?.forEach((key: Key) => {
|
||||
const found = allSelectedRows.find((item) => item[unref(getRowKey) as string] === key);
|
||||
found && trueSelectedRows.push(found);
|
||||
});
|
||||
|
@@ -7,6 +7,7 @@ import { getDynamicProps } from '/@/utils';
|
||||
import { ref, onUnmounted, unref, watch, toRaw } from 'vue';
|
||||
import { isProdMode } from '/@/utils/env';
|
||||
import { error } from '/@/utils/log';
|
||||
import type { Key } from 'ant-design-vue/lib/table/interface';
|
||||
|
||||
type Props = Partial<DynamicProps<BasicTableProps>>;
|
||||
|
||||
@@ -92,7 +93,7 @@ export function useTable(tableProps?: Props): [
|
||||
const columns = getTableInstance().getColumns({ ignoreIndex }) || [];
|
||||
return toRaw(columns);
|
||||
},
|
||||
setColumns: (columns: BasicColumn[]) => {
|
||||
setColumns: (columns: BasicColumn[] | string[]) => {
|
||||
getTableInstance().setColumns(columns);
|
||||
},
|
||||
setTableData: (values: any[]) => {
|
||||
@@ -113,7 +114,7 @@ export function useTable(tableProps?: Props): [
|
||||
clearSelectedRowKeys: () => {
|
||||
getTableInstance().clearSelectedRowKeys();
|
||||
},
|
||||
setSelectedRowKeys: (keys: string[] | number[]) => {
|
||||
setSelectedRowKeys: (keys: (string | number)[]) => {
|
||||
getTableInstance().setSelectedRowKeys(keys);
|
||||
},
|
||||
getPaginationRef: () => {
|
||||
@@ -155,7 +156,7 @@ export function useTable(tableProps?: Props): [
|
||||
expandAll: () => {
|
||||
getTableInstance().expandAll();
|
||||
},
|
||||
expandRows: (keys: string[]) => {
|
||||
expandRows: (keys: Key[]) => {
|
||||
getTableInstance().expandRows(keys);
|
||||
},
|
||||
collapseAll: () => {
|
||||
|
@@ -8,7 +8,7 @@ export function useTableExpand(
|
||||
tableData: Ref<Recordable[]>,
|
||||
emit: EmitType,
|
||||
) {
|
||||
const expandedRowKeys = ref<string[]>([]);
|
||||
const expandedRowKeys = ref<(string | number)[]>([]);
|
||||
|
||||
const getAutoCreateKey = computed(() => {
|
||||
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
|
||||
@@ -37,7 +37,7 @@ export function useTableExpand(
|
||||
expandedRowKeys.value = keys;
|
||||
}
|
||||
|
||||
function expandRows(keys: string[]) {
|
||||
function expandRows(keys: (string | number)[]) {
|
||||
// use row ID expands the specified table row
|
||||
const { isTreeTable } = unref(propsRef);
|
||||
if (!isTreeTable) return;
|
||||
|
@@ -53,22 +53,7 @@ export function useTableScroll(
|
||||
let footerEl: HTMLElement | null;
|
||||
let bodyEl: HTMLElement | null;
|
||||
|
||||
async function calcTableHeight() {
|
||||
const { resizeHeightOffset, pagination, maxHeight, isCanResizeParent, useSearchForm } =
|
||||
unref(propsRef);
|
||||
const tableData = unref(getDataSourceRef);
|
||||
|
||||
const table = unref(tableElRef);
|
||||
if (!table) return;
|
||||
|
||||
const tableEl: Element = table.$el;
|
||||
if (!tableEl) return;
|
||||
|
||||
if (!bodyEl) {
|
||||
bodyEl = tableEl.querySelector('.ant-table-body');
|
||||
if (!bodyEl) return;
|
||||
}
|
||||
|
||||
function handleScrollBar(bodyEl: HTMLElement, tableEl: Element) {
|
||||
const hasScrollBarY = bodyEl.scrollHeight > bodyEl.clientHeight;
|
||||
const hasScrollBarX = bodyEl.scrollWidth > bodyEl.clientWidth;
|
||||
|
||||
@@ -85,20 +70,10 @@ export function useTableScroll(
|
||||
} else {
|
||||
!tableEl.classList.contains('hide-scrollbar-x') && tableEl.classList.add('hide-scrollbar-x');
|
||||
}
|
||||
}
|
||||
|
||||
bodyEl!.style.height = 'unset';
|
||||
|
||||
if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return;
|
||||
|
||||
await nextTick();
|
||||
// Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
|
||||
|
||||
const headEl = tableEl.querySelector('.ant-table-thead ');
|
||||
|
||||
if (!headEl) return;
|
||||
|
||||
// Table height from bottom height-custom offset
|
||||
let paddingHeight = 32;
|
||||
function caclPaginationHeight(tableEl: Element): number {
|
||||
const { pagination } = unref(propsRef);
|
||||
// Pager height
|
||||
let paginationHeight = 2;
|
||||
if (!isBoolean(pagination)) {
|
||||
@@ -113,7 +88,11 @@ export function useTableScroll(
|
||||
} else {
|
||||
paginationHeight = -8;
|
||||
}
|
||||
return paginationHeight;
|
||||
}
|
||||
|
||||
function caclFooterHeight(tableEl: Element): number {
|
||||
const { pagination } = unref(propsRef);
|
||||
let footerHeight = 0;
|
||||
if (!isBoolean(pagination)) {
|
||||
if (!footerEl) {
|
||||
@@ -123,12 +102,21 @@ export function useTableScroll(
|
||||
footerHeight += offsetHeight || 0;
|
||||
}
|
||||
}
|
||||
return footerHeight;
|
||||
}
|
||||
|
||||
function calcHeaderHeight(headEl: Element): number {
|
||||
let headerHeight = 0;
|
||||
if (headEl) {
|
||||
headerHeight = (headEl as HTMLElement).offsetHeight;
|
||||
}
|
||||
return headerHeight;
|
||||
}
|
||||
|
||||
function calcBottomAndPaddingHeight(tableEl: Element, headEl: Element) {
|
||||
const { pagination, isCanResizeParent, useSearchForm } = unref(propsRef);
|
||||
// Table height from bottom height-custom offset
|
||||
let paddingHeight = 30;
|
||||
let bottomIncludeBody = 0;
|
||||
if (unref(wrapRef) && isCanResizeParent) {
|
||||
const tablePadding = 12;
|
||||
@@ -158,6 +146,45 @@ export function useTableScroll(
|
||||
bottomIncludeBody = getViewportOffset(headEl).bottomIncludeBody;
|
||||
}
|
||||
|
||||
return {
|
||||
paddingHeight,
|
||||
bottomIncludeBody,
|
||||
};
|
||||
}
|
||||
|
||||
async function calcTableHeight() {
|
||||
const { resizeHeightOffset, maxHeight } = unref(propsRef);
|
||||
const tableData = unref(getDataSourceRef);
|
||||
|
||||
const table = unref(tableElRef);
|
||||
if (!table) return;
|
||||
|
||||
const tableEl: Element = table.$el;
|
||||
if (!tableEl) return;
|
||||
|
||||
if (!bodyEl) {
|
||||
bodyEl = tableEl.querySelector('.ant-table-body');
|
||||
if (!bodyEl) return;
|
||||
}
|
||||
|
||||
handleScrollBar(bodyEl, tableEl);
|
||||
|
||||
bodyEl!.style.height = 'unset';
|
||||
|
||||
if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return;
|
||||
|
||||
await nextTick();
|
||||
// Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
|
||||
|
||||
const headEl = tableEl.querySelector('.ant-table-thead ');
|
||||
|
||||
if (!headEl) return;
|
||||
|
||||
const paginationHeight = caclPaginationHeight(tableEl);
|
||||
const footerHeight = caclFooterHeight(tableEl);
|
||||
const headerHeight = calcHeaderHeight(headEl);
|
||||
const { paddingHeight, bottomIncludeBody } = calcBottomAndPaddingHeight(tableEl, headEl);
|
||||
|
||||
let height =
|
||||
bottomIncludeBody -
|
||||
(resizeHeightOffset || 0) -
|
||||
|
@@ -80,7 +80,7 @@ export interface ColumnProps<T> {
|
||||
* Whether filterDropdown is visible
|
||||
* @type boolean
|
||||
*/
|
||||
filterDropdownVisible?: boolean;
|
||||
filterDropdownOpen?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the dataSource is filtered
|
||||
|
@@ -1,7 +1,10 @@
|
||||
import type { VNodeChild } from 'vue';
|
||||
import type { PaginationProps } from './pagination';
|
||||
import type { FormProps } from '/@/components/Form';
|
||||
import type { TableRowSelection as ITableRowSelection } from 'ant-design-vue/lib/table/interface';
|
||||
import type {
|
||||
TableRowSelection as ITableRowSelection,
|
||||
Key,
|
||||
} from 'ant-design-vue/lib/table/interface';
|
||||
import type { ColumnProps } from 'ant-design-vue/lib/table';
|
||||
|
||||
import { ComponentType } from './componentType';
|
||||
@@ -19,7 +22,7 @@ export interface TableRowSelection<T = any> extends ITableRowSelection {
|
||||
* Callback executed when selected rows change
|
||||
* @type Function
|
||||
*/
|
||||
onChange?: (selectedRowKeys: string[] | number[], selectedRows: T[]) => any;
|
||||
onChange?: (selectedRowKeys: Key[], selectedRows: T[]) => any;
|
||||
|
||||
/**
|
||||
* Callback executed when select/deselect one row
|
||||
@@ -37,7 +40,7 @@ export interface TableRowSelection<T = any> extends ITableRowSelection {
|
||||
* Callback executed when row selection is inverted
|
||||
* @type Function
|
||||
*/
|
||||
onSelectInvert?: (selectedRows: string[] | number[]) => any;
|
||||
onSelectInvert?: (selectedRows: Key[]) => any;
|
||||
}
|
||||
|
||||
export interface TableCustomRecord<T> {
|
||||
@@ -88,7 +91,7 @@ export interface TableActionType {
|
||||
getSelectRows: <T = Recordable>() => T[];
|
||||
clearSelectedRowKeys: () => void;
|
||||
expandAll: () => void;
|
||||
expandRows: (keys: string[] | number[]) => void;
|
||||
expandRows: (keys: (string | number)[]) => void;
|
||||
collapseAll: () => void;
|
||||
scrollTo: (pos: string) => void; // pos: id | "top" | "bottom"
|
||||
getSelectRowKeys: () => string[];
|
||||
@@ -106,7 +109,7 @@ export interface TableActionType {
|
||||
setLoading: (loading: boolean) => void;
|
||||
setProps: (props: Partial<BasicTableProps>) => void;
|
||||
redoHeight: () => void;
|
||||
setSelectedRowKeys: (rowKeys: string[] | number[]) => void;
|
||||
setSelectedRowKeys: (rowKeys: Key[]) => void;
|
||||
getPaginationRef: () => PaginationProps | boolean;
|
||||
getSize: () => SizeType;
|
||||
getRowSelection: () => TableRowSelection<Recordable>;
|
||||
@@ -116,6 +119,7 @@ export interface TableActionType {
|
||||
setShowPagination: (show: boolean) => Promise<void>;
|
||||
getShowPagination: () => boolean;
|
||||
setCacheColumnsByField?: (dataIndex: string | undefined, value: BasicColumn) => void;
|
||||
setCacheColumns?: (columns: BasicColumn[]) => void;
|
||||
}
|
||||
|
||||
export interface FetchSetting {
|
||||
@@ -429,6 +433,8 @@ export interface BasicColumn extends ColumnProps<Recordable> {
|
||||
|
||||
slots?: Recordable;
|
||||
|
||||
// 自定义header渲染
|
||||
customHeaderRender?: (column: BasicColumn) => string | VNodeChild | JSX.Element;
|
||||
// Whether to hide the column by default, it can be displayed in the column configuration
|
||||
defaultHidden?: boolean;
|
||||
|
||||
|
@@ -129,7 +129,7 @@
|
||||
getSelectedNode,
|
||||
} = useTree(treeDataRef, getFieldNames);
|
||||
|
||||
function getIcon(params: Recordable, icon?: string) {
|
||||
function getIcon(params: TreeItem, icon?: string) {
|
||||
if (!icon) {
|
||||
if (props.renderIcon && isFunction(props.renderIcon)) {
|
||||
return props.renderIcon(params);
|
||||
|
@@ -105,7 +105,7 @@ export const treeProps = buildProps({
|
||||
},
|
||||
|
||||
beforeRightClick: {
|
||||
type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>,
|
||||
type: Function as PropType<(...arg: any) => Promise<ContextMenuItem[] | ContextMenuOptions>>,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
|
@@ -17,8 +17,8 @@
|
||||
}
|
||||
|
||||
&__title {
|
||||
position: relative;
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding-right: 10px;
|
||||
@@ -35,15 +35,15 @@
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
//top: 2px;
|
||||
right: 3px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__action {
|
||||
margin-left: 4px;
|
||||
visibility: hidden;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
&-header {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
/* stylelint-disable scss/percent-placeholder-pattern */
|
||||
%ResetBorder {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
@@ -13,11 +14,13 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.vxe-form {
|
||||
.vxe-form--item-content {
|
||||
@extend %CompWidth;
|
||||
}
|
||||
}
|
||||
|
||||
.vxe-table--filter-antd-wrapper {
|
||||
& > .ant-input,
|
||||
& > .ant-input-number,
|
||||
@@ -26,16 +29,20 @@
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.vxe-cell,
|
||||
.vxe-tree-cell {
|
||||
@extend %CompWidth;
|
||||
|
||||
& > .ant-rate {
|
||||
vertical-align: bottom;
|
||||
|
||||
.anticon-star {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.col--valid-error {
|
||||
& > .vxe-cell,
|
||||
& > .vxe-tree-cell {
|
||||
@@ -50,55 +57,67 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vxe-table.cell--highlight {
|
||||
.vxe-cell,
|
||||
.vxe-tree-cell {
|
||||
& > .ant-input,
|
||||
& > .ant-input-number {
|
||||
padding: 0;
|
||||
@extend %ResetBorder;
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& > .ant-select {
|
||||
.ant-input {
|
||||
padding: 0;
|
||||
@extend %ResetBorder;
|
||||
}
|
||||
}
|
||||
& > .ant-input-number {
|
||||
.ant-input-number-input {
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
.ant-input-number-handler-wrap,
|
||||
.ant-input-number-handler-down {
|
||||
@extend %ResetBorder;
|
||||
}
|
||||
}
|
||||
& > .ant-select {
|
||||
|
||||
.ant-select-selection {
|
||||
@extend %ResetBorder;
|
||||
|
||||
.ant-select-selection__rendered {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .ant-input-number {
|
||||
.ant-input-number-input {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ant-input-number-handler-wrap,
|
||||
.ant-input-number-handler-down {
|
||||
@extend %ResetBorder;
|
||||
}
|
||||
}
|
||||
|
||||
& > .ant-cascader-picker {
|
||||
.ant-input {
|
||||
@extend %ResetBorder;
|
||||
}
|
||||
|
||||
.ant-cascader-picker-label {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > .ant-calendar-picker {
|
||||
.ant-calendar-picker-input {
|
||||
padding: 0;
|
||||
@extend %ResetBorder;
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > .ant-time-picker {
|
||||
.ant-time-picker-input {
|
||||
padding: 0;
|
||||
@extend %ResetBorder;
|
||||
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
@import './common.scss';
|
||||
@import './variable.scss';
|
||||
@import './scrollbar.scss';
|
||||
@import './toolbar.scss';
|
||||
@import './component.scss';
|
||||
@import 'vxe-table/styles/index.scss';
|
||||
@import './common';
|
||||
@import './variable';
|
||||
@import './scrollbar';
|
||||
@import './toolbar';
|
||||
@import './component';
|
||||
@import 'vxe-table/styles/index';
|
||||
|
@@ -3,22 +3,27 @@
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #ffffff;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 5px;
|
||||
border: 1px solid #f1f1f1;
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
border-radius: 5px;
|
||||
background-color: rgb(0 0 0 / 10%);
|
||||
box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #a8a8a8;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background-color: #a8a8a8;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: #ffffff;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
.vxe-toolbar .vxe-tools--wrapper,
|
||||
.vxe-toolbar .vxe-tools--operate button:first-child {
|
||||
margin: 0;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.vxe-toolbar .vxe-tools--wrapper,
|
||||
@@ -13,11 +14,6 @@
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.vxe-toolbar .vxe-tools--wrapper,
|
||||
.vxe-toolbar .vxe-tools--operate button:first-child {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.vxe-toolbar .vxe-tools--wrapper,
|
||||
.vxe-toolbar .vxe-tools--operate .vxe-custom--wrapper {
|
||||
margin-left: 1px;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
$vxe-primary-color: rgb(9, 96, 189) !default;
|
||||
$vxe-table-row-current-background-color: rgba(9, 96, 189, 0.3);
|
||||
$vxe-table-row-hover-current-background-color: rgba(9, 96, 189, 0.2);
|
||||
$vxe-table-column-hover-background-color: rgba(9, 96, 189, 0.3);
|
||||
$vxe-table-column-current-background-color: rgba(9, 96, 189, 0.2);
|
||||
$vxe-primary-color: rgb(9 96 189) !default;
|
||||
$vxe-table-row-current-background-color: rgb(9 96 189 / 30%);
|
||||
$vxe-table-row-hover-current-background-color: rgb(9 96 189 / 20%);
|
||||
$vxe-table-column-hover-background-color: rgb(9 96 189 / 30%);
|
||||
$vxe-table-column-current-background-color: rgb(9 96 189 / 20%);
|
||||
$vxe-table-validate-error-color: #f56c6c;
|
||||
|
@@ -8,10 +8,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
span.anticon:not(.app-iconify) {
|
||||
vertical-align: 0.125em !important;
|
||||
}
|
||||
|
||||
.ant-back-top {
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
|
@@ -4,8 +4,8 @@
|
||||
.ant-input {
|
||||
&-number,
|
||||
&-number-group-wrapper {
|
||||
min-width: 110px;
|
||||
width: 100% !important;
|
||||
min-width: 110px;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
@@ -16,9 +16,9 @@ html[data-theme='dark'] {
|
||||
}
|
||||
|
||||
.ant-pagination-item-active {
|
||||
background-color: @primary-color !important;
|
||||
border: none;
|
||||
border-radius: none !important;
|
||||
background-color: @primary-color !important;
|
||||
|
||||
a {
|
||||
color: @white !important;
|
||||
@@ -32,9 +32,9 @@ html[data-theme='dark'] {
|
||||
&.mini {
|
||||
.ant-pagination-prev,
|
||||
.ant-pagination-next {
|
||||
font-size: 12px;
|
||||
color: @text-color-base;
|
||||
border: 1px solid;
|
||||
color: @text-color-base;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ant-pagination-prev:hover,
|
||||
@@ -50,9 +50,9 @@ html[data-theme='dark'] {
|
||||
.ant-pagination-next,
|
||||
.ant-pagination-item {
|
||||
margin: 0 4px !important;
|
||||
background-color: #f4f4f5 !important;
|
||||
border: none;
|
||||
border-radius: none !important;
|
||||
background-color: #f4f4f5 !important;
|
||||
|
||||
a {
|
||||
margin-top: 1px;
|
||||
@@ -65,9 +65,9 @@ html[data-theme='dark'] {
|
||||
}
|
||||
|
||||
.ant-pagination-item-active {
|
||||
background-color: @primary-color !important;
|
||||
border: none;
|
||||
border-radius: none !important;
|
||||
background-color: @primary-color !important;
|
||||
|
||||
a {
|
||||
color: @white !important;
|
||||
|
@@ -44,10 +44,6 @@
|
||||
.ant-table {
|
||||
.ant-table-content {
|
||||
.ant-table-scroll {
|
||||
.ant-table-hide-scrollbar {
|
||||
//overflow-x: auto !important;
|
||||
}
|
||||
|
||||
.ant-table-body {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
1415
src/design/dark.less
Normal file
1415
src/design/dark.less
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
@import 'ant/index.less';
|
||||
@import './theme.less';
|
||||
@import './entry.css';
|
||||
@import './dark.less';
|
||||
|
||||
input:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px white inset !important;
|
||||
@@ -24,6 +25,7 @@ body {
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
|
||||
&.color-weak {
|
||||
filter: invert(80%);
|
||||
@@ -43,3 +45,10 @@ svg,
|
||||
span {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
// 保持 和 windi 一样的全局样式,减少升级带来的影响
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user