36 Commits

Author SHA1 Message Date
xiaoma
9ad5ba18a9 1.8.0 2022-04-01 10:52:30 +08:00
Ah jung
536e16f166 Merge pull request #114 from liub1934/main
feat:表格列支持draggable配置是否能拖拽
2022-04-01 09:44:52 +08:00
寻梦
42f2256ea1 feat:表格列支持draggable配置是否能拖拽 2022-03-30 18:18:20 +08:00
Ah jung
bf0d294322 Update README.md 2022-03-28 13:57:43 +08:00
Ah jung
51f5c64755 Update README.md 2022-03-22 13:31:24 +08:00
Ah jung
b49d9e8bd2 Merge pull request #111 from wangdaoo/feat/wangdaoo
fix: 优化部分无用代码及升级ui版本 🚀
2022-03-12 18:29:21 +08:00
山人自有妙计
12e62d1179 fix: 优化部分无用代码及升级ui版本 🚀 2022-03-12 18:22:28 +08:00
Ah jung
6558e1597c Merge pull request #106 from MaybeQHL/main
fix: 修复在移动端模式下tags显示的bug  update: 移动端模式下侧边菜单item点击隐藏drawer.
2022-03-08 08:42:29 +08:00
Maybe_QHL
7bf1e1265a fix: 修复在移动端模式下tags显示的bug update: 移动端模式下侧边菜单item点击隐藏drawer. 2022-03-07 17:40:17 +08:00
Ah jung
1213de598e Merge pull request #105 from MaybeQHL/main
更新侧边导航栏移动端显示为modal显示

> ![微信截图_20220307145012](https://user-images.githubusercontent.com/34638673/156982207-e5185056-cc2a-4fd4-b7d6-53182166b1a4.png) tags 还有点bug需要你去修复一下

好,我抽空看看
2022-03-07 14:53:30 +08:00
Maybe_QHL
20c9dbbfe1 忽略代理 2022-03-07 14:31:00 +08:00
Maybe_QHL
6dab2ab35b 更新侧边导航栏移动端显示为modal显示。 2022-03-07 14:21:27 +08:00
xiaoma
a424788c45 CHANGELOG 修改 2022-02-14 15:46:13 +08:00
xiaoma
b31d5c2bd6 1.7.0 2022-02-14 15:42:17 +08:00
xiaoma
b979d9db32 1.7.0 2022-02-14 15:41:46 +08:00
xiaoma
a53c86e41b 1.7.0 2022-02-14 15:12:32 +08:00
Ah jung
b16b5c8992 Merge pull request #97 from liu1013269528/main
修复 类型“ComputedRef<{ bgColor: string; fixed: boolean; show: boolean; }…
2022-02-14 10:05:27 +08:00
liu1013269528
58dadbb95a 修复 类型“ComputedRef<{ bgColor: string; fixed: boolean; show: boolean; }>”上不存在属性“fixed”。
获取计算属性值,需要增加 value 否则返回 undefined
2022-02-14 00:19:05 +08:00
Ah jung
4ebdbc7203 Update README.md 2022-02-13 15:54:29 +08:00
Ah jung
0729e56ed4 Merge pull request #91 from devilmengcry/main
更新naive-ui到2.24.6 show-password-toggle替换成showPasswordOn="click"
2022-01-26 16:06:15 +08:00
李志萌
a50cbfa44d 更新naive-ui到2.24.6 show-password-toggle替换成showPasswordOn="click" 2022-01-26 15:58:19 +08:00
Ah jung
65d6d4d21e Update README.md 2022-01-26 10:29:45 +08:00
Ah jung
c5bb818f13 Update README.md 2022-01-19 15:32:12 +08:00
Ah jung
9e255da5d7 Update README.md 2022-01-12 11:55:15 +08:00
Ah jung
e2b5086be3 Update README.md 2022-01-12 11:53:33 +08:00
xiaoma
caaca83f78 fix #81 2022-01-07 09:12:41 +08:00
xiaoma
91de971636 降低eslint-define-config版本 2022-01-06 12:54:32 +08:00
xiaoma
5fb005d5ae 依赖还原 2022-01-06 11:26:59 +08:00
xiaoma
b42e0a2fef 还原依赖 2022-01-06 11:26:17 +08:00
xiaoma
097dda5aa1 依赖升级 2022-01-06 10:47:03 +08:00
xiaoma
16714d4bdb 降低eslint版本 2022-01-06 10:36:05 +08:00
xiaoma
a5438b4f50 降低eslint-define-config依赖 2022-01-06 10:29:43 +08:00
xiaoma
4f5bbb0673 降低eslint-define-config依赖 2022-01-06 10:25:46 +08:00
xiaoma
24cbde8b95 降低 eslint-define-config 依赖 2022-01-06 10:23:39 +08:00
xiaoma
7222398cf0 依赖升级 2022-01-06 10:14:24 +08:00
xiaoma
261e27c139 还原yarn.lock 2022-01-06 10:08:03 +08:00
37 changed files with 9424 additions and 1218 deletions

View File

@@ -15,7 +15,7 @@ VITE_DROP_CONSOLE = true
# 跨域代理,可以配置多个,请注意不要换行 # 跨域代理,可以配置多个,请注意不要换行
#VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]] #VITE_PROXY = [["/appApi","http://localhost:8001"],["/upload","http://localhost:8001/upload"]]
# VITE_PROXY=[["/api","https://naive-ui-admin"]] #VITE_PROXY=[["/api","https://naive-ui-admin"]]
# API 接口地址 # API 接口地址
VITE_GLOB_API_URL = VITE_GLOB_API_URL =

View File

@@ -1,5 +1,28 @@
# CHANGELOG # CHANGELOG
## 1.8.0 (2022-04-01)
- ### ✨ Features
- 新增 `多页签` 支持配置 `affix` 固定属性
- 新增 `usePage` Hooks
- 表格列支持 `draggable` 配置拖拽 合并 [#114](https://github.com/jekip/naive-ui-admin/pull/114)
- 依赖升级
### 🐛 Bug Fixes
- 修复 `多页签` 关闭全部缺陷
- 修复 `多页签` 跳转缺陷(记得清空多页签缓存)
## 1.7.0 (2022-02-14)
### 🐛 Bug Fixes
- 移除 `登录页面` 滑动验证组件
- 修复 `BasicUpload` 组件,回显问题
- 修复 `ts类型` 配置缺陷
- 修复 `登录页面` message 交互缺陷
- 修复 `表格编辑` 时间格式化异常 [#92](https://github.com/jekip/naive-ui-admin/issues/92)
- ### ✨ Features
- 依赖升级
## 1.6.1 (2022-01-06) ## 1.6.1 (2022-01-06)
### 🐛 Bug Fixes ### 🐛 Bug Fixes

View File

@@ -1,6 +1,3 @@
## 留步
少侠留步,早知如此绊人心,何如当初莫相识,右上角免费的 `Star` 点一点,帮我们突破一下 `2k`谢谢O(∩_∩)O
## 简介 ## 简介
[Naive Ui Admin](https://github.com/jekip/naive-ui-admin) 完全免费,且可商用,基于 [Vue3.0](https://github.com/vuejs/vue-next)、[Vite](https://github.com/vitejs/vite)、 [Naive UI](https://www.naiveui.com/)、[TypeScript](https://www.typescriptlang.org/) 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目, 相信不管是从新技术使用还是其他方面,都能帮助到你。 [Naive Ui Admin](https://github.com/jekip/naive-ui-admin) 完全免费,且可商用,基于 [Vue3.0](https://github.com/vuejs/vue-next)、[Vite](https://github.com/vitejs/vite)、 [Naive UI](https://www.naiveui.com/)、[TypeScript](https://www.typescriptlang.org/) 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件、动态菜单、权限校验、粒子化权限控制等功能,它可以帮助你快速搭建企业级中后台项目, 相信不管是从新技术使用还是其他方面,都能帮助到你。
@@ -32,16 +29,24 @@
### Antd vue ### Antd vue
千呼万唤 `Naive Admin Antd` 也迎来了第一个版本,同时具备 `Naive Ui Admin` 优点,如果您选的技术栈是 `Antd` 的话,不妨看看 新产品,如果您选的技术栈是 `Antd` 的话,不妨看看
[NaiveAdmin Antd 预览](https://antd.naiveadmin.com) [NaiveAdmin Antd 预览](https://antd.naiveadmin.com)
### Arco vue ### Arco vue
新产品,新生态,智能设计体系,连接轻盈体验,一如既往、开箱即用,欢迎前往查看。 新产品,智能设计体系,连接轻盈体验
[NaiveAdmin Arco 预览](https://arco.naiveadmin.com) [NaiveAdmin Arco 预览](https://arco.naiveadmin.com)
### Element Plus
新产品,面向设计师和开发者的组件库
[Element Plus Admin 预览](https://element.naiveadmin.com)
以上版本同时具备 `NaiveAdmin v2` 功能/组件/页面,一如既往、开箱即用,欢迎前往查看。
## 文档 ## 文档
@@ -137,11 +142,13 @@ yarn build
## 交流 ## 交流
`Naive Ui Admin` 在帮助开发者更方便地进行中大型管理系统开发,同时也提供 QQ 交流群使用问题欢迎在群内提问。 `Naive Ui Admin` 使用或者其他问题,都可以在群内讨论或提问。
- QQ 群 `328347666` ![abelianGroup](https://user-images.githubusercontent.com/19426584/160335146-c28dd205-4600-4d62-b2c6-6456034ab7b1.jpg)
## 赞助 ## 赞助
#### 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 🍹。 #### 如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 🍹。
![donate](https://jekip.github.io/docs/images/sponsor.png) ![donate](https://jekip.github.io/docs/images/sponsor.png)
[Paypal Me](https://www.paypal.com/paypalme/majunping)

View File

@@ -1,6 +1,6 @@
{ {
"name": "naive-ui-admin", "name": "naive-ui-admin",
"version": "1.6.1", "version": "1.8.0",
"author": { "author": {
"name": "Ahjung", "name": "Ahjung",
"email": "735878602@qq.com", "email": "735878602@qq.com",
@@ -27,72 +27,71 @@
"dependencies": { "dependencies": {
"@vicons/antd": "^0.10.0", "@vicons/antd": "^0.10.0",
"@vicons/ionicons5": "^0.10.0", "@vicons/ionicons5": "^0.10.0",
"@vueup/vue-quill": "^1.0.0-beta.7", "@vueup/vue-quill": "^1.0.0-beta.8",
"@vueuse/core": "^5.0.3", "@vueuse/core": "^5.3.0",
"axios": "^0.21.1", "axios": "^0.21.4",
"blueimp-md5": "^2.18.0", "blueimp-md5": "^2.19.0",
"date-fns": "^2.23.0", "date-fns": "^2.28.0",
"echarts": "^5.1.2", "echarts": "^5.3.1",
"element-resize-detector": "^1.2.3", "element-resize-detector": "^1.2.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"makeit-captcha": "^1.2.5",
"mitt": "^2.1.0", "mitt": "^2.1.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"naive-ui": "^2.23.2", "naive-ui": "^2.27.0",
"pinia": "^2.0.0-rc.4", "pinia": "^2.0.13",
"qs": "^6.10.1", "qs": "^6.10.3",
"vfonts": "^0.1.0", "vfonts": "^0.1.0",
"vue": "^3.2.16", "vue": "^3.2.31",
"vue-router": "^4.0.11", "vue-router": "^4.0.14",
"vue-types": "^4.1.0", "vue-types": "^4.1.1",
"vuedraggable": "^4.0.3" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^12.1.4", "@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4", "@commitlint/config-conventional": "^12.1.4",
"@types/lodash": "^4.14.170", "@types/lodash": "^4.14.181",
"@types/node": "^15.12.2", "@types/node": "^15.14.9",
"@typescript-eslint/eslint-plugin": "^4.31.2", "@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.31.2", "@typescript-eslint/parser": "^4.33.0",
"@vitejs/plugin-vue": "^1.9.1", "@vitejs/plugin-vue": "^1.10.2",
"@vitejs/plugin-vue-jsx": "^1.1.8", "@vitejs/plugin-vue-jsx": "^1.3.9",
"@vue/compiler-sfc": "^3.2.16", "@vue/compiler-sfc": "^3.2.31",
"@vue/eslint-config-typescript": "^7.0.0", "@vue/eslint-config-typescript": "^7.0.0",
"autoprefixer": "^10.3.1", "autoprefixer": "^10.4.4",
"commitizen": "^4.2.4", "commitizen": "^4.2.4",
"core-js": "^3.14.0", "core-js": "^3.21.1",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.0.9", "eslint-define-config": "1.0.9",
"eslint-plugin-jest": "^24.4.0", "eslint-plugin-jest": "^24.7.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-vue": "^7.18.0", "eslint-plugin-vue": "^7.20.0",
"esno": "^0.7.3", "esno": "^0.7.3",
"gh-pages": "^3.2.0", "gh-pages": "^3.2.3",
"husky": "^6.0.0", "husky": "^6.0.0",
"jest": "^27.0.6", "jest": "^27.5.1",
"less": "^4.1.1", "less": "^4.1.2",
"less-loader": "^9.0.0", "less-loader": "^9.1.0",
"lint-staged": "^11.0.0", "lint-staged": "^11.2.6",
"postcss": "^8.3.5", "postcss": "^8.4.12",
"prettier": "^2.3.1", "prettier": "^2.6.1",
"pretty-quick": "^3.1.0", "pretty-quick": "^3.1.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-order": "^4.1.0", "stylelint-order": "^4.1.0",
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.21.0",
"tailwindcss": "^2.2.7", "tailwindcss": "^2.2.19",
"typescript": "^4.4.3", "typescript": "^4.6.3",
"unplugin-vue-components": "^0.17.2", "unplugin-vue-components": "^0.17.21",
"vite": "^2.5.10", "vite": "^2.9.1",
"vite-plugin-compression": "^0.3.1", "vite-plugin-compression": "^0.3.6",
"vite-plugin-html": "^2.0.7", "vite-plugin-html": "^2.1.2",
"vite-plugin-mock": "^2.9.3", "vite-plugin-mock": "^2.9.6",
"vite-plugin-style-import": "^1.0.1", "vite-plugin-style-import": "^1.4.1",
"vue-eslint-parser": "^7.11.0" "vue-eslint-parser": "^7.11.0"
}, },
"lint-staged": { "lint-staged": {

7548
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted } from 'vue'; import { computed, onMounted, onUnmounted } from 'vue';
import { zhCN, dateZhCN, createTheme, inputDark, datePickerDark, darkTheme } from 'naive-ui'; import { zhCN, dateZhCN, darkTheme } from 'naive-ui';
import { LockScreen } from '@/components/Lockscreen'; import { LockScreen } from '@/components/Lockscreen';
import { AppProvider } from '@/components/Application'; import { AppProvider } from '@/components/Application';
import { useLockscreenStore } from '@/store/modules/lockscreen'; import { useLockscreenStore } from '@/store/modules/lockscreen';

View File

@@ -93,7 +93,7 @@
function handleSubmit() { function handleSubmit() {
subLoading.value = true; subLoading.value = true;
console.log(subLoading.value) console.log(subLoading.value);
emit('on-ok'); emit('on-ok');
} }

View File

@@ -4,7 +4,6 @@ import { ModalMethods, UseModalReturnType } from '../type';
import { getDynamicProps } from '@/utils'; import { getDynamicProps } from '@/utils';
import { tryOnUnmounted } from '@vueuse/core'; import { tryOnUnmounted } from '@vueuse/core';
export function useModal(props): UseModalReturnType { export function useModal(props): UseModalReturnType {
const modalRef = ref<Nullable<ModalMethods>>(null); const modalRef = ref<Nullable<ModalMethods>>(null);
const currentInstance = getCurrentInstance(); const currentInstance = getCurrentInstance();

View File

@@ -12,7 +12,7 @@ export interface ModalMethods {
/** /**
* 支持修改DialogOptions 參數 * 支持修改DialogOptions 參數
*/ */
export interface ModalProps extends DialogOptions { } export type ModalProps = DialogOptions;
export type RegisterFn = (ModalInstance: ModalMethods) => void; export type RegisterFn = (ModalInstance: ModalMethods) => void;

View File

@@ -11,7 +11,6 @@
<CellComponent <CellComponent
v-bind="getComponentProps" v-bind="getComponentProps"
:component="getComponent" :component="getComponent"
:style="getWrapperStyle"
:popoverVisible="getRuleVisible" :popoverVisible="getRuleVisible"
:ruleMessage="ruleMessage" :ruleMessage="ruleMessage"
:rule="getRule" :rule="getRule"
@@ -33,7 +32,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import type { CSSProperties, PropType } from 'vue'; import type { PropType } from 'vue';
import type { BasicColumn } from '../../types/table'; import type { BasicColumn } from '../../types/table';
import type { EditRecordRow } from './index'; import type { EditRecordRow } from './index';
@@ -51,7 +50,8 @@
import { set, omit } from 'lodash-es'; import { set, omit } from 'lodash-es';
import { EventEnum } from '@/components/Table/src/componentMap'; import { EventEnum } from '@/components/Table/src/componentMap';
import { milliseconds, format } from 'date-fns'; import { parseISO, format } from 'date-fns';
import { Fn, LabelValueOptions } from '/#/index';
export default defineComponent({ export default defineComponent({
name: 'EditableCell', name: 'EditableCell',
@@ -105,16 +105,28 @@
const isCheckValue = unref(getIsCheckComp); const isCheckValue = unref(getIsCheckComp);
const valueField = isCheckValue ? 'checked' : 'value'; let valueField = isCheckValue ? 'checked' : 'value';
const val = unref(currentValueRef); const val = unref(currentValueRef);
let value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; let value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
if (isString(value) && component === 'NDatePicker') { //TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
value = milliseconds(value as Duration); if (component === 'NDatePicker') {
} else if (isArray(value) && component === 'NDatePicker') { if (isString(value)) {
value = value.map((item) => milliseconds(item)); if (compProps.valueFormat) {
valueField = 'formatted-value';
} else {
value = parseISO(value as any).getTime();
}
} else if (isArray(value)) {
if (compProps.valueFormat) {
valueField = 'formatted-value';
} else {
value = value.map((item) => parseISO(item).getTime());
}
}
} }
const onEvent: any = editComponent ? EventEnum[editComponent] : undefined; const onEvent: any = editComponent ? EventEnum[editComponent] : undefined;
return { return {
@@ -146,15 +158,6 @@
return option?.label ?? value; return option?.label ?? value;
}); });
const getWrapperStyle = computed((): CSSProperties => {
// if (unref(getIsCheckComp) || unref(getRowEditable)) {
// return {};
// }
return {
width: 'calc(100% - 48px)',
};
});
const getWrapperClass = computed(() => { const getWrapperClass = computed(() => {
const { align = 'center' } = props.column; const { align = 'center' } = props.column;
return `edit-cell-align-${align}`; return `edit-cell-align-${align}`;
@@ -188,6 +191,7 @@
async function handleChange(e: any) { async function handleChange(e: any) {
const component = unref(getComponent); const component = unref(getComponent);
const compProps = props.column?.editComponentProps ?? {};
if (!e) { if (!e) {
currentValueRef.value = e; currentValueRef.value = e;
} else if (e?.target && Reflect.has(e.target, 'value')) { } else if (e?.target && Reflect.has(e.target, 'value')) {
@@ -198,10 +202,20 @@
currentValueRef.value = e; currentValueRef.value = e;
} }
//TODO 根据组件格式化值 //TODO 特殊处理 NDatePicker 可能要根据项目 规范自行调整代码
// if (component === 'NDatePicker') { if (component === 'NDatePicker') {
// currentValueRef.value = format(currentValueRef.value,'yyyy-MM-dd HH:mm:ss'); if (isNumber(currentValueRef.value)) {
// } if (compProps.valueFormat) {
currentValueRef.value = format(currentValueRef.value, compProps.valueFormat);
}
} else if (isArray(currentValueRef.value)) {
if (compProps.valueFormat) {
currentValueRef.value = currentValueRef.value.map((item) => {
format(item, compProps.valueFormat);
});
}
}
}
const onChange = props.column?.editComponentProps?.onChange; const onChange = props.column?.editComponentProps?.onChange;
if (onChange && isFunction(onChange)) onChange(...arguments); if (onChange && isFunction(onChange)) onChange(...arguments);
@@ -303,104 +317,103 @@
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) { function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
if (props.record) { if (props.record) {
/* eslint-disable */ /* eslint-disable */
isArray(props.record[cbs]) isArray(props.record[cbs])
? props.record[cbs]?.push(handle) ? props.record[cbs]?.push(handle)
: (props.record[cbs] = [handle]); : (props.record[cbs] = [handle]);
} }
}
if (props.record) {
initCbs('submitCbs', handleSubmit);
initCbs('validCbs', handleSubmiRule);
initCbs('cancelCbs', handleCancel);
if (props.column.key) {
if (!props.record.editValueRefs) props.record.editValueRefs = {};
props.record.editValueRefs[props.column.key] = currentValueRef;
}
/* eslint-disable */
props.record.onCancelEdit = () => {
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn());
};
/* eslint-disable */
props.record.onSubmitEdit = async() => {
if (isArray(props.record?.submitCbs)) {
const validFns = (props.record?.validCbs || []).map((fn) => fn());
const res = await Promise.all(validFns);
const pass = res.every((item) => !!item);
if (!pass) return;
const submitFns = props.record?.submitCbs || [];
submitFns.forEach((fn) => fn(false, false));
table.emit?.('edit-row-end');
return true;
} }
};
}
return { if (props.record) {
isEdit, initCbs('submitCbs', handleSubmit);
handleEdit, initCbs('validCbs', handleSubmiRule);
currentValueRef, initCbs('cancelCbs', handleCancel);
handleSubmit,
handleChange, if (props.column.key) {
handleCancel, if (!props.record.editValueRefs) props.record.editValueRefs = {};
elRef, props.record.editValueRefs[props.column.key] = currentValueRef;
getComponent, }
getRule, /* eslint-disable */
onClickOutside, props.record.onCancelEdit = () => {
ruleMessage, isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn());
getRuleVisible, };
getComponentProps, /* eslint-disable */
handleOptionsChange, props.record.onSubmitEdit = async () => {
getWrapperStyle, if (isArray(props.record?.submitCbs)) {
getWrapperClass, const validFns = (props.record?.validCbs || []).map((fn) => fn());
getRowEditable,
getValues, const res = await Promise.all(validFns);
handleEnter,
// getSize, const pass = res.every((item) => !!item);
};
}, if (!pass) return;
const submitFns = props.record?.submitCbs || [];
submitFns.forEach((fn) => fn(false, false));
table.emit?.('edit-row-end');
return true;
}
};
}
return {
isEdit,
handleEdit,
currentValueRef,
handleSubmit,
handleChange,
handleCancel,
elRef,
getComponent,
getRule,
onClickOutside,
ruleMessage,
getRuleVisible,
getComponentProps,
handleOptionsChange,
getWrapperClass,
getRowEditable,
getValues,
handleEnter,
// getSize,
};
},
}); });
</script> </script>
<style lang="less"> <style lang="less">
.editable-cell { .editable-cell {
&-content { &-content {
position: relative; position: relative;
overflow-wrap: break-word; overflow-wrap: break-word;
word-break: break-word; word-break: break-word;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
&-comp{ &-comp {
flex: 1; flex: 1;
}
.edit-icon {
font-size: 14px;
//position: absolute;
//top: 4px;
//right: 0;
display: none;
width: 20px;
cursor: pointer;
}
&:hover {
.edit-icon {
display: inline-block;
}
}
}
&-action {
display: flex;
align-items: center;
justify-content: center;
}
} }
.edit-icon {
font-size: 14px;
//position: absolute;
//top: 4px;
//right: 0;
display: none;
width: 20px;
cursor: pointer;
}
&:hover {
.edit-icon {
display: inline-block;
}
}
}
&-action {
display: flex;
align-items: center;
justify-content: center;
}
}
</style> </style>

View File

@@ -25,13 +25,26 @@
</template> </template>
<div class="table-toolbar-inner"> <div class="table-toolbar-inner">
<n-checkbox-group v-model:value="checkList" @update:value="onChange"> <n-checkbox-group v-model:value="checkList" @update:value="onChange">
<Draggable v-model="columnsList" animation="300" item-key="key" @end="draggableEnd"> <Draggable
v-model="columnsList"
animation="300"
item-key="key"
filter=".no-draggable"
:move="onMove"
@end="draggableEnd"
>
<template #item="{ element }"> <template #item="{ element }">
<div <div
class="table-toolbar-inner-checkbox" class="table-toolbar-inner-checkbox"
:class="{ 'table-toolbar-inner-checkbox-dark': getDarkTheme === true }" :class="{
'table-toolbar-inner-checkbox-dark': getDarkTheme === true,
'no-draggable': element.draggable === false,
}"
> >
<span class="drag-icon"> <span
class="drag-icon"
:class="{ 'drag-icon-hidden': element.draggable === false }"
>
<n-icon size="18"> <n-icon size="18">
<DragOutlined /> <DragOutlined />
</n-icon> </n-icon>
@@ -211,6 +224,11 @@
} }
} }
function onMove(e) {
if (e.draggedContext.element.draggable === false) return false;
return true;
}
//固定 //固定
function fixedColumn(item, fixed) { function fixedColumn(item, fixed) {
if (!state.checkList.includes(item.key)) return; if (!state.checkList.includes(item.key)) return;
@@ -232,6 +250,7 @@
onChange, onChange,
onCheckAll, onCheckAll,
onSelection, onSelection,
onMove,
resetColumns, resetColumns,
fixedColumn, fixedColumn,
draggableEnd, draggableEnd,
@@ -275,6 +294,10 @@
display: inline-flex; display: inline-flex;
margin-right: 8px; margin-right: 8px;
cursor: move; cursor: move;
&-hidden {
visibility: hidden;
cursor: default;
}
} }
.fixed-item { .fixed-item {

View File

@@ -14,6 +14,8 @@ export interface BasicColumn extends TableBaseColumn {
auth?: string[]; auth?: string[];
// 业务控制是否显示 // 业务控制是否显示
ifShow?: boolean | ((column: BasicColumn) => boolean); ifShow?: boolean | ((column: BasicColumn) => boolean);
// 控制是否支持拖拽,默认支持
draggable?: boolean;
} }
export interface TableActionType { export interface TableActionType {

View File

@@ -14,10 +14,10 @@
<img :src="item" /> <img :src="item" />
</div> </div>
<div class="img-box-actions"> <div class="img-box-actions">
<n-icon size="18" class="action-icon mx-2" @click="preview(item)"> <n-icon size="18" class="mx-2 action-icon" @click="preview(item)">
<EyeOutlined /> <EyeOutlined />
</n-icon> </n-icon>
<n-icon size="18" class="action-icon mx-2" @click="remove(index)"> <n-icon size="18" class="mx-2 action-icon" @click="remove(index)">
<DeleteOutlined /> <DeleteOutlined />
</n-icon> </n-icon>
</div> </div>
@@ -36,7 +36,7 @@
@before-upload="beforeUpload" @before-upload="beforeUpload"
@finish="finish" @finish="finish"
> >
<div class="flex justify-center flex-col"> <div class="flex flex-col justify-center">
<n-icon size="18" class="m-auto"> <n-icon size="18" class="m-auto">
<PlusOutlined /> <PlusOutlined />
</n-icon> </n-icon>
@@ -68,7 +68,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, toRefs, reactive, computed } from 'vue'; import { defineComponent, toRefs, reactive, computed, watch } from 'vue';
import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd'; import { EyeOutlined, DeleteOutlined, PlusOutlined } from '@vicons/antd';
import { basicProps } from './props'; import { basicProps } from './props';
import { useMessage, useDialog } from 'naive-ui'; import { useMessage, useDialog } from 'naive-ui';
@@ -106,11 +106,14 @@
}); });
//赋值默认图片显示 //赋值默认图片显示
if (props.value.length) { watch(
state.imgList = props.value.map((item) => { () => props.value,
return getImgUrl(item); () => {
}); imgList.value = props.value.map((item) => {
} return getImgUrl(item);
});
}
);
//预览 //预览
function preview(url: string) { function preview(url: string) {

View File

@@ -8,6 +8,8 @@ export function useProjectSetting() {
const getNavTheme = computed(() => projectStore.navTheme); const getNavTheme = computed(() => projectStore.navTheme);
const getIsMobile = computed(() => projectStore.isMobile);
const getHeaderSetting = computed(() => projectStore.headerSetting); const getHeaderSetting = computed(() => projectStore.headerSetting);
const getMultiTabsSetting = computed(() => projectStore.multiTabsSetting); const getMultiTabsSetting = computed(() => projectStore.multiTabsSetting);
@@ -27,6 +29,7 @@ export function useProjectSetting() {
return { return {
getNavMode, getNavMode,
getNavTheme, getNavTheme,
getIsMobile,
getHeaderSetting, getHeaderSetting,
getMultiTabsSetting, getMultiTabsSetting,
getMenuSetting, getMenuSetting,

62
src/hooks/web/usePage.ts Normal file
View File

@@ -0,0 +1,62 @@
import type { RouteLocationRaw, Router } from 'vue-router';
import { PageEnum } from '@/enums/pageEnum';
import { RedirectName } from '@/router/constant';
import { useRouter } from 'vue-router';
import { isString } from '@/utils/is';
import { unref } from 'vue';
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
function handleError(e: Error) {
console.error(e);
}
/**
* 页面切换
*/
export function useGo(_router?: Router) {
let router;
if (!_router) {
router = useRouter();
}
const { push, replace } = _router || router;
function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
if (!opt) {
return;
}
if (isString(opt)) {
isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
} else {
const o = opt as RouteLocationRaw;
isReplace ? replace(o).catch(handleError) : push(o).catch(handleError);
}
}
return go;
}
/**
* 重做当前页面
*/
export const useRedo = (_router?: Router) => {
const { push, currentRoute } = _router || useRouter();
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
function redo(): Promise<boolean> {
return new Promise((resolve) => {
if (name === RedirectName) {
resolve(false);
return;
}
if (name && Object.keys(params).length > 0) {
params['_redirect_type'] = 'name';
params['path'] = String(name);
} else {
params['_redirect_type'] = 'path';
params['path'] = fullPath;
}
push({ name: RedirectName, params, query }).then(() => resolve(true));
});
}
return redo;
};

View File

@@ -359,6 +359,7 @@
margin: 0 5px 5px 0; margin: 0 5px 5px 0;
text-align: center; text-align: center;
line-height: 14px; line-height: 14px;
.n-icon { .n-icon {
color: #fff; color: #fff;
} }

View File

@@ -125,6 +125,7 @@
} else { } else {
router.push({ name: key }); router.push({ name: key });
} }
emit('clickMenuItem' as any, key);
} }
//展开菜单 //展开菜单

View File

@@ -40,11 +40,7 @@
@contextmenu="handleContextMenu($event, element)" @contextmenu="handleContextMenu($event, element)"
> >
<span>{{ element.meta.title }}</span> <span>{{ element.meta.title }}</span>
<n-icon <n-icon size="14" @click.stop="closeTabItem(element)" v-if="!element.meta.affix">
size="14"
@click.stop="closeTabItem(element)"
v-if="element.path !== baseHome"
>
<CloseOutlined /> <CloseOutlined />
</n-icon> </n-icon>
</div> </div>
@@ -116,6 +112,7 @@
import { useDesignSetting } from '@/hooks/setting/useDesignSetting'; import { useDesignSetting } from '@/hooks/setting/useDesignSetting';
import { useProjectSettingStore } from '@/store/modules/projectSetting'; import { useProjectSettingStore } from '@/store/modules/projectSetting';
import { useThemeVars } from 'naive-ui'; import { useThemeVars } from 'naive-ui';
import { useGo } from '@/hooks/web/usePage';
export default defineComponent({ export default defineComponent({
name: 'TabsView', name: 'TabsView',
@@ -133,7 +130,7 @@
}, },
setup(props) { setup(props) {
const { getDarkTheme, getAppTheme } = useDesignSetting(); const { getDarkTheme, getAppTheme } = useDesignSetting();
const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting } = const { getNavMode, getHeaderSetting, getMenuSetting, getMultiTabsSetting, getIsMobile } =
useProjectSetting(); useProjectSetting();
const settingStore = useProjectSettingStore(); const settingStore = useProjectSettingStore();
@@ -145,6 +142,7 @@
const navScroll: any = ref(null); const navScroll: any = ref(null);
const navWrap: any = ref(null); const navWrap: any = ref(null);
const isCurrent = ref(false); const isCurrent = ref(false);
const go = useGo();
const themeVars = useThemeVars(); const themeVars = useThemeVars();
@@ -192,6 +190,13 @@
: collapsed : collapsed
? `${minMenuWidth}px` ? `${minMenuWidth}px`
: `${menuWidth}px`; : `${menuWidth}px`;
if (getIsMobile.value) {
return {
left: '0px',
width: '100%',
};
}
return { return {
left: lenNum, left: lenNum,
width: `calc(100% - ${!fixed ? '0px' : lenNum})`, width: `calc(100% - ${!fixed ? '0px' : lenNum})`,
@@ -258,8 +263,8 @@
window.pageYOffset || window.pageYOffset ||
document.body.scrollTop; // 滚动条偏移量 document.body.scrollTop; // 滚动条偏移量
state.isMultiHeaderFixed = !!( state.isMultiHeaderFixed = !!(
!getHeaderSetting.fixed && !getHeaderSetting.value.fixed &&
getMultiTabsSetting.fixed && getMultiTabsSetting.value.fixed &&
scrollTop >= 64 scrollTop >= 64
); );
} }
@@ -356,7 +361,6 @@
// 关闭全部 // 关闭全部
const closeAll = () => { const closeAll = () => {
localStorage.removeItem('routes');
tabsViewStore.closeAllTabs(); tabsViewStore.closeAllTabs();
router.replace(PageEnum.BASE_HOME); router.replace(PageEnum.BASE_HOME);
updateNavScroll(); updateNavScroll();
@@ -473,7 +477,7 @@
const { fullPath } = e; const { fullPath } = e;
if (fullPath === route.fullPath) return; if (fullPath === route.fullPath) return;
state.activeKey = fullPath; state.activeKey = fullPath;
router.push({ path: fullPath }); go(e, true);
} }
//删除tab //删除tab
@@ -499,7 +503,6 @@
navScroll, navScroll,
route, route,
tabsList, tabsList,
baseHome: PageEnum.BASE_HOME_REDIRECT,
goPage, goPage,
closeTabItem, closeTabItem,
closeLeft, closeLeft,

View File

@@ -1,7 +1,9 @@
<template> <template>
<n-layout class="layout" :position="fixedMenu" has-sider> <n-layout class="layout" :position="fixedMenu" has-sider>
<n-layout-sider <n-layout-sider
v-if="isMixMenuNoneSub && (navMode === 'vertical' || navMode === 'horizontal-mix')" v-if="
!isMobile && isMixMenuNoneSub && (navMode === 'vertical' || navMode === 'horizontal-mix')
"
show-trigger="bar" show-trigger="bar"
@collapse="collapsed = true" @collapse="collapsed = true"
:position="fixedMenu" :position="fixedMenu"
@@ -18,6 +20,16 @@
<AsideMenu v-model:collapsed="collapsed" v-model:location="getMenuLocation" /> <AsideMenu v-model:collapsed="collapsed" v-model:location="getMenuLocation" />
</n-layout-sider> </n-layout-sider>
<n-drawer
v-model:show="showSideDrawder"
:width="menuWidth"
:placement="'left'"
class="layout-side-drawer"
>
<Logo :collapsed="collapsed" />
<AsideMenu @clickMenuItem="collapsed = false" />
</n-drawer>
<n-layout :inverted="inverted"> <n-layout :inverted="inverted">
<n-layout-header :inverted="getHeaderInverted" :position="fixedHeader"> <n-layout-header :inverted="getHeaderInverted" :position="fixedHeader">
<PageHeader v-model:collapsed="collapsed" :inverted="inverted" /> <PageHeader v-model:collapsed="collapsed" :inverted="inverted" />
@@ -71,7 +83,7 @@
const { getDarkTheme } = useDesignSetting(); const { getDarkTheme } = useDesignSetting();
const { const {
getShowFooter, // getShowFooter,
getNavMode, getNavMode,
getNavTheme, getNavTheme,
getHeaderSetting, getHeaderSetting,
@@ -85,6 +97,13 @@
const collapsed = ref<boolean>(false); const collapsed = ref<boolean>(false);
const { mobileWidth, menuWidth } = unref(getMenuSetting);
const isMobile = computed<boolean>({
get: () => settingStore.getIsMobile,
set: (val) => settingStore.setIsMobile(val),
});
const fixedHeader = computed(() => { const fixedHeader = computed(() => {
const { fixed } = unref(getHeaderSetting); const { fixed } = unref(getHeaderSetting);
return fixed ? 'absolute' : 'static'; return fixed ? 'absolute' : 'static';
@@ -127,25 +146,44 @@
return collapsed.value ? minMenuWidth : menuWidth; return collapsed.value ? minMenuWidth : menuWidth;
}); });
const getChangeStyle = computed(() => { // const getChangeStyle = computed(() => {
const { minMenuWidth, menuWidth } = unref(getMenuSetting); // const { minMenuWidth, menuWidth } = unref(getMenuSetting);
return { // return {
'padding-left': collapsed.value ? `${minMenuWidth}px` : `${menuWidth}px`, // 'padding-left': collapsed.value ? `${minMenuWidth}px` : `${menuWidth}px`,
}; // };
}); // });
const getMenuLocation = computed(() => { const getMenuLocation = computed(() => {
return 'left'; return 'left';
}); });
// 控制显示或隐藏移动端侧边栏
const showSideDrawder = computed({
get: () => isMobile.value && collapsed.value,
set: (val) => (collapsed.value = val),
});
//判断是否触发移动端模式
const checkMobileMode = () => {
if (document.body.clientWidth <= mobileWidth) {
isMobile.value = true;
} else {
isMobile.value = false;
}
collapsed.value = false;
};
const watchWidth = () => { const watchWidth = () => {
const Width = document.body.clientWidth; const Width = document.body.clientWidth;
if (Width <= 950) { if (Width <= 950) {
collapsed.value = true; collapsed.value = true;
} else collapsed.value = false; } else collapsed.value = false;
checkMobileMode();
}; };
onMounted(() => { onMounted(() => {
checkMobileMode();
window.addEventListener('resize', watchWidth); window.addEventListener('resize', watchWidth);
//挂载在 window 方便与在js中使用 //挂载在 window 方便与在js中使用
window['$loading'] = useLoadingBar(); window['$loading'] = useLoadingBar();
@@ -153,6 +191,19 @@
}); });
</script> </script>
<style lang="less">
.layout-side-drawer {
background-color: rgb(0, 20, 40);
.layout-sider {
min-height: 100vh;
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
position: relative;
z-index: 13;
transition: all 0.2s ease-in-out;
}
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.layout { .layout {
display: flex; display: flex;
@@ -213,7 +264,7 @@
} }
.fluid-header { .fluid-header {
padding-top: 0px; padding-top: 0;
} }
.main-view-fix { .main-view-fix {

View File

@@ -3,8 +3,6 @@ import { createApp } from 'vue';
import App from './App.vue'; import App from './App.vue';
import router, { setupRouter } from './router'; import router, { setupRouter } from './router';
import { setupStore } from '@/store'; import { setupStore } from '@/store';
import MakeitCaptcha from 'makeit-captcha';
import 'makeit-captcha/dist/captcha.min.css';
import { setupNaive, setupDirectives } from '@/plugins'; import { setupNaive, setupDirectives } from '@/plugins';
import { AppProvider } from '@/components/Application'; import { AppProvider } from '@/components/Application';
@@ -13,8 +11,6 @@ async function bootstrap() {
const app = createApp(App); const app = createApp(App);
app.use(MakeitCaptcha);
// 注册全局常用的 naive-ui 组件 // 注册全局常用的 naive-ui 组件
setupNaive(app); setupNaive(app);

View File

@@ -34,6 +34,7 @@ const routes: Array<RouteRecordRaw> = [
meta: { meta: {
title: '主控台', title: '主控台',
permissions: ['dashboard_console'], permissions: ['dashboard_console'],
affix: true,
}, },
component: () => import('@/views/dashboard/console/console.vue'), component: () => import('@/views/dashboard/console/console.vue'),
}, },

View File

@@ -17,6 +17,15 @@ const routes: Array<RouteRecordRaw> = [
icon: renderIcon(DesktopOutline), icon: renderIcon(DesktopOutline),
}, },
children: [ children: [
{
path: 'naive-admin',
name: 'naive-admin',
meta: {
title: 'NaiveAdmin',
frameSrc: 'https://www.naiveadmin.com',
},
component: IFrame,
},
{ {
path: 'docs', path: 'docs',
name: 'frame-docs', name: 'frame-docs',

View File

@@ -3,6 +3,8 @@ const setting = {
navMode: 'vertical', navMode: 'vertical',
//导航风格 dark 暗色侧边栏 light 白色侧边栏 header-dark 暗色顶栏 //导航风格 dark 暗色侧边栏 light 白色侧边栏 header-dark 暗色顶栏
navTheme: 'dark', navTheme: 'dark',
// 是否处于移动端模式
isMobile: false,
//顶部 //顶部
headerSetting: { headerSetting: {
//背景色 //背景色
@@ -33,6 +35,8 @@ const setting = {
fixed: true, fixed: true,
//分割菜单 //分割菜单
mixMenu: false, mixMenu: false,
//触发移动端侧边栏的宽度
mobileWidth: 800,
}, },
//面包屑 //面包屑
crumbsSetting: { crumbsSetting: {

View File

@@ -6,6 +6,7 @@ import type { IheaderSetting, ImenuSetting, ImultiTabsSetting, IcrumbsSetting }
const { const {
navMode, navMode,
navTheme, navTheme,
isMobile,
headerSetting, headerSetting,
showFooter, showFooter,
menuSetting, menuSetting,
@@ -27,6 +28,7 @@ interface ProjectSettingState {
permissionMode: string; //权限模式 permissionMode: string; //权限模式
isPageAnimate: boolean; //是否开启路由动画 isPageAnimate: boolean; //是否开启路由动画
pageAnimateType: string; //路由动画类型 pageAnimateType: string; //路由动画类型
isMobile: boolean; // 是否处于移动端模式
} }
export const useProjectSettingStore = defineStore({ export const useProjectSettingStore = defineStore({
@@ -34,6 +36,7 @@ export const useProjectSettingStore = defineStore({
state: (): ProjectSettingState => ({ state: (): ProjectSettingState => ({
navMode: navMode, navMode: navMode,
navTheme, navTheme,
isMobile,
headerSetting, headerSetting,
showFooter, showFooter,
menuSetting, menuSetting,
@@ -50,6 +53,9 @@ export const useProjectSettingStore = defineStore({
getNavTheme(): string { getNavTheme(): string {
return this.navTheme; return this.navTheme;
}, },
getIsMobile(): boolean {
return this.isMobile;
},
getHeaderSetting(): object { getHeaderSetting(): object {
return this.headerSetting; return this.headerSetting;
}, },
@@ -79,6 +85,9 @@ export const useProjectSettingStore = defineStore({
setNavTheme(value: string): void { setNavTheme(value: string): void {
this.navTheme = value; this.navTheme = value;
}, },
setIsMobile(value: boolean): void {
this.isMobile = value;
},
}, },
}); });

View File

@@ -7,13 +7,23 @@ const whiteList = ['Redirect', 'login'];
export type RouteItem = Partial<RouteLocationNormalized> & { export type RouteItem = Partial<RouteLocationNormalized> & {
fullPath: string; fullPath: string;
path: string;
name: string; name: string;
hash: string;
meta: object;
params: object;
query: object;
}; };
export type ITabsViewState = { export type ITabsViewState = {
tabsList: RouteItem[]; // 标签页 tabsList: RouteItem[]; // 标签页
}; };
//保留固定路由
function retainAffixRoute(list: any[]) {
return list.filter((item) => item?.meta?.affix ?? false);
}
export const useTabsViewStore = defineStore({ export const useTabsViewStore = defineStore({
id: 'app-tabs-view', id: 'app-tabs-view',
state: (): ITabsViewState => ({ state: (): ITabsViewState => ({
@@ -55,8 +65,8 @@ export const useTabsViewStore = defineStore({
}, },
closeAllTabs() { closeAllTabs() {
// 关闭全部 // 关闭全部
this.tabsList = []; console.log(retainAffixRoute(this.tabsList));
localStorage.removeItem(TABS_ROUTES); this.tabsList = retainAffixRoute(this.tabsList);
}, },
}, },
}); });

View File

@@ -1,9 +1,12 @@
#app, body, html { #app,
body,
html {
height: 100%; height: 100%;
} }
body { body {
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, "\5FAE\8F6F\96C5\9ED1", Arial, sans-serif; font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei,
'\5FAE\8F6F\96C5\9ED1', Arial, sans-serif;
line-height: 1.5; line-height: 1.5;
color: #515a6e; color: #515a6e;
font-size: 14px; font-size: 14px;
@@ -15,7 +18,7 @@ body {
//重置样式 //重置样式
.anticon { .anticon {
svg { svg {
vertical-align: initial vertical-align: initial;
} }
} }
@@ -25,10 +28,11 @@ a {
text-decoration: none; text-decoration: none;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: color .2s ease; transition: color 0.2s ease;
} }
a:active, a:hover { a:active,
a:hover {
outline-width: 0; outline-width: 0;
} }
@@ -40,7 +44,8 @@ a:active {
color: #2b85e4; color: #2b85e4;
} }
a:active, a:hover { a:active,
a:hover {
outline: 0; outline: 0;
text-decoration: none; text-decoration: none;
} }
@@ -91,7 +96,7 @@ a:active, a:hover {
//antd 卡片样式定制 //antd 卡片样式定制
body .n-card { body .n-card {
transition: all .2s ease-in-out; transition: all 0.2s ease-in-out;
} }
body .n-icon { body .n-icon {
@@ -110,7 +115,7 @@ body .proCard {
} }
} }
body .n-modal{ body .n-modal {
border-radius: 6px; border-radius: 6px;
} }

View File

@@ -61,6 +61,7 @@ export const columns = [
editComponentProps: { editComponentProps: {
type: 'datetime', type: 'datetime',
format: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
}, },
ellipsis: false, ellipsis: false,
}, },

View File

@@ -64,6 +64,7 @@ export const columns = [
editComponentProps: { editComponentProps: {
type: 'datetime', type: 'datetime',
format: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
}, },
ellipsis: false, ellipsis: false,
}, },

View File

@@ -43,7 +43,6 @@
loading.value = true; loading.value = true;
init(); init();
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -29,7 +29,7 @@
<n-input <n-input
v-model:value="formInline.password" v-model:value="formInline.password"
type="password" type="password"
show-password-toggle showPasswordOn="click"
placeholder="请输入密码" placeholder="请输入密码"
> >
<template #prefix> <template #prefix>
@@ -39,11 +39,6 @@
</template> </template>
</n-input> </n-input>
</n-form-item> </n-form-item>
<n-form-item path="isCaptcha">
<div class="w-full">
<mi-captcha width="384" theme-color="#2d8cf0" :logo="logo" @success="onAuthCode" />
</div>
</n-form-item>
<n-form-item class="default-color"> <n-form-item class="default-color">
<div class="flex justify-between"> <div class="flex justify-between">
<div class="flex-initial"> <div class="flex-initial">
@@ -95,8 +90,8 @@
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { useMessage } from 'naive-ui'; import { useMessage } from 'naive-ui';
import { ResultEnum } from '@/enums/httpEnum'; import { ResultEnum } from '@/enums/httpEnum';
import logo from '@/assets/images/logo.png';
import { PersonOutline, LockClosedOutline, LogoGithub, LogoFacebook } from '@vicons/ionicons5'; import { PersonOutline, LockClosedOutline, LogoGithub, LogoFacebook } from '@vicons/ionicons5';
import { PageEnum } from '@/enums/pageEnum';
interface FormState { interface FormState {
username: string; username: string;
@@ -107,23 +102,17 @@
const message = useMessage(); const message = useMessage();
const loading = ref(false); const loading = ref(false);
const autoLogin = ref(true); const autoLogin = ref(true);
const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
const formInline = reactive({ const formInline = reactive({
username: 'admin', username: 'admin',
password: '123456', password: '123456',
isCaptcha: false, isCaptcha: true,
}); });
const rules = { const rules = {
username: { required: true, message: '请输入用户名', trigger: 'blur' }, username: { required: true, message: '请输入用户名', trigger: 'blur' },
password: { required: true, message: '请输入密码', trigger: 'blur' }, password: { required: true, message: '请输入密码', trigger: 'blur' },
isCaptcha: {
required: true,
type: 'boolean',
trigger: 'change',
message: '请点击按钮进行验证码校验',
validator: (_, value) => value === true,
},
}; };
const userStore = useUserStore(); const userStore = useUserStore();
@@ -144,28 +133,26 @@
password, password,
}; };
const { code, message: msg } = await userStore.login(params); try {
const { code, message: msg } = await userStore.login(params);
if (code == ResultEnum.SUCCESS) { message.destroyAll();
const toPath = decodeURIComponent((route.query?.redirect || '/') as string); if (code == ResultEnum.SUCCESS) {
message.success('登录成功!'); const toPath = decodeURIComponent((route.query?.redirect || '/') as string);
router.replace(toPath).then((_) => { message.success('登录成功,即将进入系统');
if (route.name == 'login') { if (route.name === LOGIN_NAME) {
router.replace('/'); router.replace('/');
} } else router.replace(toPath);
}); } else {
} else { message.info(msg || '登录失败');
message.info(msg || '登录失败'); }
} finally {
loading.value = false;
} }
} else { } else {
message.error('请填写完整信息,并且进行验证码校验'); message.error('请填写完整信息,并且进行验证码校验');
} }
}); });
}; };
const onAuthCode = () => {
formInline.isCaptcha = true;
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@@ -37,10 +37,17 @@
</n-card> </n-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue';
import { useThemeVars } from 'naive-ui';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { InfoCircleOutlined } from '@vicons/antd'; import { InfoCircleOutlined } from '@vicons/antd';
const router = useRouter(); const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() { function goHome() {
router.push('/'); router.push('/');
@@ -56,7 +63,7 @@
&-extra { &-extra {
padding: 24px 40px; padding: 24px 40px;
text-align: left; text-align: left;
background: #f8f8f9; background: v-bind(getTableHeaderColor);
border-radius: 4px; border-radius: 4px;
} }
} }

View File

@@ -41,10 +41,17 @@
</n-card> </n-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue';
import { useThemeVars } from 'naive-ui';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { CheckCircleOutlined } from '@vicons/antd'; import { CheckCircleOutlined } from '@vicons/antd';
const router = useRouter(); const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() { function goHome() {
router.push('/'); router.push('/');
@@ -60,7 +67,7 @@
&-extra { &-extra {
padding: 24px 40px; padding: 24px 40px;
text-align: left; text-align: left;
background: #f8f8f9; background: v-bind(getTableHeaderColor);
border-radius: 4px; border-radius: 4px;
} }
} }

View File

@@ -23,9 +23,16 @@
</n-card> </n-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useThemeVars } from 'naive-ui';
const router = useRouter(); const router = useRouter();
const themeVars = useThemeVars();
const getTableHeaderColor = computed(() => {
return themeVars.value.tableHeaderColor;
});
function goHome() { function goHome() {
router.push('/'); router.push('/');
@@ -41,7 +48,7 @@
&-extra { &-extra {
padding: 24px 40px; padding: 24px 40px;
text-align: left; text-align: left;
background: #f8f8f9; background: v-bind(getTableHeaderColor);
border-radius: 4px; border-radius: 4px;
} }
} }

View File

@@ -213,7 +213,7 @@
} }
function handleReset() { function handleReset() {
const treeItem = getTreeItem(unref(treeData), treeItemKey[0]); const treeItem = getTreeItem(unref(treeData), treeItemKey.value[0]);
Object.assign(formParams, treeItem); Object.assign(formParams, treeItem);
} }

View File

@@ -38,7 +38,7 @@
] ]
} }
}, },
"includes": [ "include": [
"src/**/*.ts", "src/**/*.ts",
"src/**/*.d.ts", "src/**/*.d.ts",
"src/**/*.tsx", "src/**/*.tsx",

1
types/config.d.ts vendored
View File

@@ -33,6 +33,7 @@ export interface ImenuSetting {
fixed: boolean; fixed: boolean;
mixMenu: boolean; mixMenu: boolean;
collapsed: boolean; collapsed: boolean;
mobileWidth: number;
} }
export interface IcrumbsSetting { export interface IcrumbsSetting {

2348
yarn.lock

File diff suppressed because it is too large Load Diff