Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b689fabfdd | ||
|
|
46dc7eb69e | ||
|
|
c1e741dad6 | ||
|
|
ab4063e75e | ||
|
|
770d39871a | ||
|
|
3cb7a7f54f |
@@ -19,5 +19,8 @@ VITE_APP_API_URL = /
|
|||||||
# 图片上传地址
|
# 图片上传地址
|
||||||
VITE_GLOB_UPLOAD_URL= /
|
VITE_GLOB_UPLOAD_URL= /
|
||||||
|
|
||||||
|
# 图片前缀地址
|
||||||
|
VITE_GLOB_IMG_URL= /
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_GLOB_API_URL_PREFIX = /api
|
VITE_GLOB_API_URL_PREFIX = /api
|
||||||
|
|||||||
@@ -16,5 +16,8 @@ VITE_APP_API_URL = /
|
|||||||
# 图片上传地址
|
# 图片上传地址
|
||||||
VITE_GLOB_UPLOAD_URL= /
|
VITE_GLOB_UPLOAD_URL= /
|
||||||
|
|
||||||
|
# 图片前缀地址
|
||||||
|
VITE_GLOB_IMG_URL= /
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_GLOB_API_URL_PREFIX = /api
|
VITE_GLOB_API_URL_PREFIX = /api
|
||||||
|
|||||||
13
CHANGELOG.md
@@ -1,3 +1,16 @@
|
|||||||
|
# 1.3 (2021-07-19)
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- 修复多标签页左右切换按钮自适应展示
|
||||||
|
- 修复登录页面出现多标签页
|
||||||
|
|
||||||
|
- ### ✨ Features
|
||||||
|
- 新增 `Upload` 组件及配置
|
||||||
|
- 新增 `VITE_GLOB_IMG_URL` 图片前缀地址,配合Upload
|
||||||
|
- 新增 `滑块验证码` 组件
|
||||||
|
- 新增 `登录页面-滑块验证码` 示例
|
||||||
|
- 持续更新更多实用组件及示例,感谢Star
|
||||||
|
|
||||||
|
|
||||||
# 1.2 (2021-07-16)
|
# 1.2 (2021-07-16)
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
- 修复面包屑显示登录页面
|
- 修复面包屑显示登录页面
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ Naive Ui Admin 是一个免费开源的中后台模版,使用了最新的`vue3
|
|||||||
|
|
||||||
### 页面组件
|
### 页面组件
|
||||||
#### ProTable
|
#### ProTable
|
||||||
- [x] 表格
|
- [x] 基础表格
|
||||||
|
- [x] 上传图片
|
||||||
|
- [x] 滑块验证码
|
||||||
- 持续开发中...
|
- 持续开发中...
|
||||||
|
|
||||||
## 在线预览
|
## 在线预览
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"element-resize-detector": "^1.2.3",
|
"element-resize-detector": "^1.2.3",
|
||||||
"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.15.5",
|
"naive-ui": "^2.15.5",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import http from '@/utils/http/axios'
|
import http from '@/utils/http/axios'
|
||||||
import { LoginParams, LoginResultModel } from './model/userModel'
|
|
||||||
|
|
||||||
export interface BasicResponseModel<T = any> {
|
export interface BasicResponseModel<T = any> {
|
||||||
code: number
|
code: number
|
||||||
@@ -28,8 +27,8 @@ export function getUserInfo() {
|
|||||||
/**
|
/**
|
||||||
* @description: 用户登录
|
* @description: 用户登录
|
||||||
*/
|
*/
|
||||||
export function login(params: LoginParams) {
|
export function login(params) {
|
||||||
return http.request<BasicResponseModel<LoginResultModel>>(
|
return http.request<BasicResponseModel>(
|
||||||
{
|
{
|
||||||
url: '/login',
|
url: '/login',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
@@ -1 +0,0 @@
|
|||||||
export { default as ProTable } from './src/ProTable.vue';
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
|
||||||
import type { BasicColumn, BasicTableProps } from '../types/table';
|
|
||||||
import { isEqual, cloneDeep } from 'lodash-es';
|
|
||||||
import { isArray, isString } from '@/utils/is';
|
|
||||||
|
|
||||||
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
|
||||||
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
|
||||||
let cacheColumns = unref(propsRef).columns;
|
|
||||||
|
|
||||||
const getColumnsRef = computed(() => {
|
|
||||||
const columns = cloneDeep(unref(columnsRef));
|
|
||||||
return columns;
|
|
||||||
})
|
|
||||||
|
|
||||||
const getPageColumns = computed(() => {
|
|
||||||
const pageColumns = unref(getColumnsRef);
|
|
||||||
const columns = cloneDeep(pageColumns);
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => unref(propsRef).columns,
|
|
||||||
(columns) => {
|
|
||||||
columnsRef.value = columns;
|
|
||||||
cacheColumns = columns;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
//设置
|
|
||||||
function setColumns(columnList: string[]) {
|
|
||||||
const columns: any[] = cloneDeep(columnList);
|
|
||||||
if (!isArray(columns)) return;
|
|
||||||
|
|
||||||
if (!columns.length) {
|
|
||||||
columnsRef.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cacheKeys = cacheColumns.map((item) => item.key);
|
|
||||||
//针对拖拽排序
|
|
||||||
if (!isString(columns[0])) {
|
|
||||||
columnsRef.value = columns;
|
|
||||||
} else {
|
|
||||||
const newColumns: any[] = []
|
|
||||||
cacheColumns.forEach(item => {
|
|
||||||
if (columnList.includes(item.key)) {
|
|
||||||
newColumns.push({ ...item })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (!isEqual(cacheKeys, columns)) {
|
|
||||||
newColumns.sort((prev, next) => {
|
|
||||||
return (
|
|
||||||
cacheKeys.indexOf(prev.key) - cacheKeys.indexOf(next.key)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
columnsRef.value = newColumns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取
|
|
||||||
function getColumns() {
|
|
||||||
let columns = toRaw(unref(getColumnsRef));
|
|
||||||
return columns.map(item => {
|
|
||||||
return { ...item, title: item.title, key: item.key, fixed: item.fixed || undefined }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取原始
|
|
||||||
function getCacheColumns(isKey?: boolean): any[] {
|
|
||||||
return isKey ? cacheColumns.map(item => item.key) : cacheColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
//更新原始数据单个字段
|
|
||||||
function setCacheColumnsField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
|
|
||||||
if (!dataIndex || !value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cacheColumns.forEach((item) => {
|
|
||||||
if (item.key === dataIndex) {
|
|
||||||
Object.assign(item, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getColumnsRef,
|
|
||||||
getCacheColumns,
|
|
||||||
setCacheColumnsField,
|
|
||||||
setColumns,
|
|
||||||
getColumns,
|
|
||||||
getPageColumns
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
import { ref, ComputedRef, unref, computed, onMounted, watchEffect, watch } from 'vue';
|
|
||||||
import type { BasicTableProps } from '../types/table';
|
|
||||||
import type { PaginationProps } from '../types/pagination';
|
|
||||||
import { isBoolean } from '@/utils/is';
|
|
||||||
import { APISETTING } from '../const';
|
|
||||||
|
|
||||||
export function useDataSource(
|
|
||||||
propsRef: ComputedRef<BasicTableProps>,
|
|
||||||
{
|
|
||||||
getPaginationInfo,
|
|
||||||
setPagination,
|
|
||||||
setLoading,
|
|
||||||
tableData
|
|
||||||
},
|
|
||||||
emit
|
|
||||||
) {
|
|
||||||
const dataSourceRef = ref([]);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
tableData.value = unref(dataSourceRef);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => unref(propsRef).dataSource,
|
|
||||||
() => {
|
|
||||||
const { dataSource }: any = unref(propsRef);
|
|
||||||
dataSource && (dataSourceRef.value = dataSource);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const getRowKey = computed(() => {
|
|
||||||
const { rowKey }:any = unref(propsRef);
|
|
||||||
return rowKey ? rowKey : () => {
|
|
||||||
return 'key'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const getDataSourceRef = computed(() => {
|
|
||||||
const dataSource = unref(dataSourceRef);
|
|
||||||
if (!dataSource || dataSource.length === 0) {
|
|
||||||
return unref(dataSourceRef);
|
|
||||||
}
|
|
||||||
return unref(dataSourceRef);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function fetch(opt?) {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const { request, pagination }: any = unref(propsRef);
|
|
||||||
|
|
||||||
//组装分页信息
|
|
||||||
const pageField = APISETTING.pageField
|
|
||||||
const sizeField = APISETTING.sizeField
|
|
||||||
const totalField = APISETTING.totalField
|
|
||||||
const listField = APISETTING.listField
|
|
||||||
|
|
||||||
let pageParams = {};
|
|
||||||
const { page = 1, pageSize = 10 } = unref(getPaginationInfo) as PaginationProps;
|
|
||||||
|
|
||||||
if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) {
|
|
||||||
pageParams = {};
|
|
||||||
} else {
|
|
||||||
pageParams[pageField] = (opt && opt[pageField]) || page;
|
|
||||||
pageParams[sizeField] = pageSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
...pageParams,
|
|
||||||
}
|
|
||||||
const res = await request(params);
|
|
||||||
|
|
||||||
const resultTotal = res[totalField] || 0
|
|
||||||
const currentPage = res[pageField]
|
|
||||||
|
|
||||||
// 如果数据异常,需获取正确的页码再次执行
|
|
||||||
if (resultTotal) {
|
|
||||||
const currentTotalPage = Math.ceil(resultTotal / pageSize);
|
|
||||||
if (page > currentTotalPage) {
|
|
||||||
setPagination({
|
|
||||||
[pageField]: currentTotalPage,
|
|
||||||
});
|
|
||||||
fetch(opt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let resultInfo = res[listField] ? res[listField] : []
|
|
||||||
dataSourceRef.value = resultInfo;
|
|
||||||
setPagination({
|
|
||||||
[pageField]: currentPage,
|
|
||||||
[totalField]: resultTotal,
|
|
||||||
});
|
|
||||||
if (opt && opt[pageField]) {
|
|
||||||
setPagination({
|
|
||||||
[pageField]: opt[pageField] || 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
emit('fetch-success', {
|
|
||||||
items: unref(resultInfo),
|
|
||||||
resultTotal
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
emit('fetch-error', error);
|
|
||||||
dataSourceRef.value = [];
|
|
||||||
// setPagination({
|
|
||||||
// pageCount: 0,
|
|
||||||
// });
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
fetch();
|
|
||||||
}, 16)
|
|
||||||
});
|
|
||||||
|
|
||||||
function setTableData(values) {
|
|
||||||
dataSourceRef.value = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDataSource() :any[] {
|
|
||||||
return getDataSourceRef.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function reload(opt?) {
|
|
||||||
await fetch(opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
fetch,
|
|
||||||
getRowKey,
|
|
||||||
getDataSourceRef,
|
|
||||||
getDataSource,
|
|
||||||
setTableData,
|
|
||||||
reload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { ref, ComputedRef, unref, computed, watch } from 'vue';
|
|
||||||
import type { BasicTableProps } from '../types/table';
|
|
||||||
|
|
||||||
export function useLoading(props: ComputedRef<BasicTableProps>) {
|
|
||||||
const loadingRef = ref(unref(props).loading);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => unref(props).loading,
|
|
||||||
(loading) => {
|
|
||||||
loadingRef.value = loading;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const getLoading = computed(() => unref(loadingRef));
|
|
||||||
|
|
||||||
function setLoading(loading: boolean) {
|
|
||||||
loadingRef.value = loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { getLoading, setLoading };
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import type { PaginationProps } from '../types/pagination';
|
|
||||||
import type { BasicTableProps } from '../types/table';
|
|
||||||
import { computed, unref, ref, ComputedRef } from 'vue';
|
|
||||||
|
|
||||||
import { isBoolean } from '@/utils/is';
|
|
||||||
import { DEFAULTPAGESIZE, PAGESIZES } from '../const';
|
|
||||||
|
|
||||||
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
|
|
||||||
const configRef = ref<PaginationProps>({});
|
|
||||||
const show = ref(true);
|
|
||||||
|
|
||||||
const getPaginationInfo = computed((): PaginationProps | boolean => {
|
|
||||||
const { pagination } = unref(refProps);
|
|
||||||
if (!unref(show) || (isBoolean(pagination) && !pagination)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
pageSize: DEFAULTPAGESIZE,
|
|
||||||
pageSizes: PAGESIZES,
|
|
||||||
showSizePicker: true,
|
|
||||||
showQuickJumper: true,
|
|
||||||
...(isBoolean(pagination) ? {} : pagination),
|
|
||||||
...unref(configRef),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function setPagination(info: Partial<PaginationProps>) {
|
|
||||||
const paginationInfo = unref(getPaginationInfo);
|
|
||||||
configRef.value = {
|
|
||||||
...(!isBoolean(paginationInfo) ? paginationInfo : {}),
|
|
||||||
...info,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPagination() {
|
|
||||||
return unref(getPaginationInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShowPagination() {
|
|
||||||
return unref(show);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setShowPagination(flag: boolean) {
|
|
||||||
show.value = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { getPagination, getPaginationInfo, setShowPagination, getShowPagination, setPagination };
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import type { PropType } from 'vue'
|
|
||||||
import { BasicColumn } from './types/table'
|
|
||||||
|
|
||||||
export const basicProps = {
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
titleTooltip: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
tableData: {
|
|
||||||
type: [Object],
|
|
||||||
default: () => {
|
|
||||||
},
|
|
||||||
},
|
|
||||||
columns: {
|
|
||||||
type: [Array] as PropType<BasicColumn[]>,
|
|
||||||
default: () => [],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
request: {
|
|
||||||
type: Function as PropType<(...arg: any[]) => Promise<any>>,
|
|
||||||
default: null,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
rowKey: {
|
|
||||||
type: [String, Function] as PropType<string | ((record) => string)>,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
pagination: {
|
|
||||||
type: [Object, Boolean],
|
|
||||||
default: () => {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showPagination: {
|
|
||||||
type: [String, Boolean],
|
|
||||||
default: 'auto'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import Pagination from 'naive-ui/lib/pagination';
|
|
||||||
import { VNodeChild } from 'vue';
|
|
||||||
|
|
||||||
export interface PaginationProps {
|
|
||||||
page?: number;
|
|
||||||
pageCount?: number,
|
|
||||||
pageSize?: number,
|
|
||||||
pageSizes?: number[],
|
|
||||||
showSizePicker?: boolean,
|
|
||||||
showQuickJumper?: boolean,
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import type {
|
|
||||||
TableBaseColumn,
|
|
||||||
} from 'naive-ui/lib/data-table/src/interface';
|
|
||||||
|
|
||||||
export interface BasicColumn extends TableBaseColumn {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TableActionType {
|
|
||||||
reload: (opt) => Promise<void>;
|
|
||||||
emit?: any;
|
|
||||||
getColumns: (opt) => BasicColumn[];
|
|
||||||
setColumns: (columns: BasicColumn[] | string[]) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BasicTableProps<T = any> {
|
|
||||||
title?: string,
|
|
||||||
dataSource: Function,
|
|
||||||
columns: any[],
|
|
||||||
pagination: object,
|
|
||||||
showPagination: boolean
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
ProTable 重封装组件说明
|
BasicTable 重封装组件说明
|
||||||
====
|
====
|
||||||
|
|
||||||
封装说明
|
封装说明
|
||||||
@@ -6,7 +6,7 @@ ProTable 重封装组件说明
|
|||||||
|
|
||||||
> 基础的使用方式与 API 与 [官方版(data-table)](https://www.naiveui.com/zh-CN/os-theme/components/data-table#tree) 本一致,在其基础上,封装了加载数据的方法。
|
> 基础的使用方式与 API 与 [官方版(data-table)](https://www.naiveui.com/zh-CN/os-theme/components/data-table#tree) 本一致,在其基础上,封装了加载数据的方法。
|
||||||
>
|
>
|
||||||
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 ProTable 组件传递绑定 `:api="Promise"` 对象即可
|
> 你无需在你是用表格的页面进行分页逻辑处理,仅需向 BasicTable 组件传递绑定 `:api="Promise"` 对象即可
|
||||||
>
|
>
|
||||||
> 例子1
|
> 例子1
|
||||||
----
|
----
|
||||||
@@ -15,7 +15,7 @@ ProTable 重封装组件说明
|
|||||||
```vue
|
```vue
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ProTable
|
<BasicTable
|
||||||
title="表格列表"
|
title="表格列表"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:api="loadDataTable"
|
:api="loadDataTable"
|
||||||
@@ -25,12 +25,12 @@ ProTable 重封装组件说明
|
|||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<n-button type="primary">添加会员</n-button>
|
<n-button type="primary">添加会员</n-button>
|
||||||
</template>
|
</template>
|
||||||
</ProTable>
|
</BasicTable>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { ProTable } from '@/components/ProTable'
|
import { BasicTable } from '@/components/Table'
|
||||||
import { getTableList } from '@/api/table/list'
|
import { getTableList } from '@/api/table/list'
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -51,7 +51,7 @@ const columns = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { ProTable },
|
components: { BasicTable },
|
||||||
setup() {
|
setup() {
|
||||||
const loadDataTable = async (params) => {
|
const loadDataTable = async (params) => {
|
||||||
const data = await getTableList(params);
|
const data = await getTableList(params);
|
||||||
@@ -68,7 +68,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
API
|
API
|
||||||
----
|
----
|
||||||
ProTable 在 NaiveUi 的 data-table 上进行了一层封装,支持了一些预设,并且封装了一些行为。这里只列出与 data-table 不同的 api。
|
BasicTable 在 NaiveUi 的 data-table 上进行了一层封装,支持了一些预设,并且封装了一些行为。这里只列出与 data-table 不同的 api。
|
||||||
|
|
||||||
> request:Promise 参考上面例子写法
|
> request:Promise 参考上面例子写法
|
||||||
> ref:可绑定ref 调用组件内部方法(data-table本身的方法和参数)
|
> ref:可绑定ref 调用组件内部方法(data-table本身的方法和参数)
|
||||||
1
src/components/Table/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as BasicTable } from './src/Table.vue';
|
||||||
@@ -281,7 +281,8 @@ export default defineComponent({
|
|||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color:var(--text-color);
|
color: var(--text-color);
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,8 @@
|
|||||||
<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" @end="draggableEnd">
|
||||||
<template #item="{element, index}">
|
<template #item="{element, index}">
|
||||||
<div class="table-toolbar-inner-checkbox" :class="{'table-toolbar-inner-checkbox-dark':getDarkTheme === true}">
|
<div class="table-toolbar-inner-checkbox"
|
||||||
|
:class="{'table-toolbar-inner-checkbox-dark':getDarkTheme === true}">
|
||||||
<span class="drag-icon">
|
<span class="drag-icon">
|
||||||
<n-icon size="18">
|
<n-icon size="18">
|
||||||
<DragOutlined/>
|
<DragOutlined/>
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-icon size="18" :color="element.fixed === 'left' ? '#2080f0':undefined"
|
<n-icon size="18" :color="element.fixed === 'left' ? '#2080f0':undefined"
|
||||||
class="cursor-pointer" @click="fixedColumn(element,'left')">
|
class="cursor-pointer" @click="fixedColumn(element,'left')">
|
||||||
<VerticalRightOutlined />
|
<VerticalRightOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
<span>固定到左侧</span>
|
<span>固定到左侧</span>
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<n-icon size="18" :color="element.fixed === 'right' ? '#2080f0':undefined"
|
<n-icon size="18" :color="element.fixed === 'right' ? '#2080f0':undefined"
|
||||||
class="cursor-pointer" @click="fixedColumn(element,'right')">
|
class="cursor-pointer" @click="fixedColumn(element,'right')">
|
||||||
<VerticalLeftOutlined />
|
<VerticalLeftOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
<span>固定到右侧</span>
|
<span>固定到右侧</span>
|
||||||
@@ -131,7 +132,7 @@ export default defineComponent({
|
|||||||
function getColumns() {
|
function getColumns() {
|
||||||
let newRet = []
|
let newRet = []
|
||||||
table.getColumns().forEach(item => {
|
table.getColumns().forEach(item => {
|
||||||
newRet.push({...item })
|
newRet.push({ ...item })
|
||||||
})
|
})
|
||||||
return newRet
|
return newRet
|
||||||
}
|
}
|
||||||
@@ -140,11 +141,11 @@ export default defineComponent({
|
|||||||
function resetColumns() {
|
function resetColumns() {
|
||||||
state.checkList = [...state.defaultCheckList]
|
state.checkList = [...state.defaultCheckList]
|
||||||
state.checkAll = true;
|
state.checkAll = true;
|
||||||
let cacheColumnsKeys:any[] = table.getCacheColumns()
|
let cacheColumnsKeys: any[] = table.getCacheColumns()
|
||||||
let newColumns = cacheColumnsKeys.map(item => {
|
let newColumns = cacheColumnsKeys.map(item => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
fixed:undefined
|
fixed: undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setColumns(newColumns);
|
setColumns(newColumns);
|
||||||
@@ -184,18 +185,18 @@ export default defineComponent({
|
|||||||
|
|
||||||
//固定
|
//固定
|
||||||
function fixedColumn(item, fixed) {
|
function fixedColumn(item, fixed) {
|
||||||
console.log('item:',item)
|
console.log('item:', item)
|
||||||
if (!state.checkList.includes(item.key)) return;
|
if (!state.checkList.includes(item.key)) return;
|
||||||
let columns = getColumns();
|
let columns = getColumns();
|
||||||
const isFixed = item.fixed === fixed ? undefined : fixed
|
const isFixed = item.fixed === fixed ? undefined : fixed
|
||||||
let index = columns.findIndex(res => res.key === item.key)
|
let index = columns.findIndex(res => res.key === item.key)
|
||||||
console.log('index:',index)
|
console.log('index:', index)
|
||||||
if(index !== -1){
|
if (index !== -1) {
|
||||||
columns[index].fixed = isFixed;
|
columns[index].fixed = isFixed;
|
||||||
}
|
}
|
||||||
table.setCacheColumnsField(item.key, { fixed: isFixed })
|
table.setCacheColumnsField(item.key, { fixed: isFixed })
|
||||||
columnsList.value[index].fixed = isFixed
|
columnsList.value[index].fixed = isFixed
|
||||||
console.log('columnsList:',columnsList.value)
|
console.log('columnsList:', columnsList.value)
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,15 +218,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.table-toolbar {
|
.table-toolbar {
|
||||||
&-inner-popover-title{
|
&-inner-popover-title {
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-right {
|
&-right {
|
||||||
&-icon {
|
&-icon {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color:var(--text-color);
|
color: var(--text-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
@@ -238,14 +241,17 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #e6f7ff;
|
background: #e6f7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.drag-icon {
|
.drag-icon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fixed-item {
|
.fixed-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -261,14 +267,16 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-checkbox-dark{
|
|
||||||
|
&-checkbox-dark {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: hsla(0, 0%, 100%, .08);
|
background: hsla(0, 0%, 100%, .08);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toolbar-popover{
|
|
||||||
.n-popover__content{
|
.toolbar-popover {
|
||||||
|
.n-popover__content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
95
src/components/Table/src/hooks/useColumns.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { ref, Ref, ComputedRef, unref, computed, watch, toRaw } from 'vue';
|
||||||
|
import type { BasicColumn, BasicTableProps } from '../types/table';
|
||||||
|
import { isEqual, cloneDeep } from 'lodash-es';
|
||||||
|
import { isArray, isString } from '@/utils/is';
|
||||||
|
|
||||||
|
export function useColumns(propsRef: ComputedRef<BasicTableProps>) {
|
||||||
|
const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
|
||||||
|
let cacheColumns = unref(propsRef).columns;
|
||||||
|
|
||||||
|
const getColumnsRef = computed(() => {
|
||||||
|
const columns = cloneDeep(unref(columnsRef));
|
||||||
|
return columns;
|
||||||
|
})
|
||||||
|
|
||||||
|
const getPageColumns = computed(() => {
|
||||||
|
const pageColumns = unref(getColumnsRef);
|
||||||
|
const columns = cloneDeep(pageColumns);
|
||||||
|
return columns
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => unref(propsRef).columns,
|
||||||
|
(columns) => {
|
||||||
|
columnsRef.value = columns;
|
||||||
|
cacheColumns = columns;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//设置
|
||||||
|
function setColumns(columnList: string[]) {
|
||||||
|
const columns: any[] = cloneDeep(columnList);
|
||||||
|
if (!isArray(columns)) return;
|
||||||
|
|
||||||
|
if (!columns.length) {
|
||||||
|
columnsRef.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const cacheKeys = cacheColumns.map((item) => item.key);
|
||||||
|
//针对拖拽排序
|
||||||
|
if (!isString(columns[0])) {
|
||||||
|
columnsRef.value = columns;
|
||||||
|
} else {
|
||||||
|
const newColumns: any[] = []
|
||||||
|
cacheColumns.forEach(item => {
|
||||||
|
if (columnList.includes(item.key)) {
|
||||||
|
newColumns.push({ ...item })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!isEqual(cacheKeys, columns)) {
|
||||||
|
newColumns.sort((prev, next) => {
|
||||||
|
return (
|
||||||
|
cacheKeys.indexOf(prev.key) - cacheKeys.indexOf(next.key)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
columnsRef.value = newColumns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取
|
||||||
|
function getColumns() {
|
||||||
|
let columns = toRaw(unref(getColumnsRef));
|
||||||
|
return columns.map(item => {
|
||||||
|
return { ...item, title: item.title, key: item.key, fixed: item.fixed || undefined }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取原始
|
||||||
|
function getCacheColumns(isKey?: boolean): any[] {
|
||||||
|
return isKey ? cacheColumns.map(item => item.key) : cacheColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
//更新原始数据单个字段
|
||||||
|
function setCacheColumnsField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
|
||||||
|
if (!dataIndex || !value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheColumns.forEach((item) => {
|
||||||
|
if (item.key === dataIndex) {
|
||||||
|
Object.assign(item, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getColumnsRef,
|
||||||
|
getCacheColumns,
|
||||||
|
setCacheColumnsField,
|
||||||
|
setColumns,
|
||||||
|
getColumns,
|
||||||
|
getPageColumns
|
||||||
|
};
|
||||||
|
}
|
||||||
141
src/components/Table/src/hooks/useDataSource.ts
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import { ref, ComputedRef, unref, computed, onMounted, watchEffect, watch } from 'vue';
|
||||||
|
import type { BasicTableProps } from '../types/table';
|
||||||
|
import type { PaginationProps } from '../types/pagination';
|
||||||
|
import { isBoolean } from '@/utils/is';
|
||||||
|
import { APISETTING } from '../const';
|
||||||
|
|
||||||
|
export function useDataSource(
|
||||||
|
propsRef: ComputedRef<BasicTableProps>,
|
||||||
|
{
|
||||||
|
getPaginationInfo,
|
||||||
|
setPagination,
|
||||||
|
setLoading,
|
||||||
|
tableData
|
||||||
|
},
|
||||||
|
emit
|
||||||
|
) {
|
||||||
|
const dataSourceRef = ref([]);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
tableData.value = unref(dataSourceRef);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => unref(propsRef).dataSource,
|
||||||
|
() => {
|
||||||
|
const { dataSource }: any = unref(propsRef);
|
||||||
|
dataSource && (dataSourceRef.value = dataSource);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getRowKey = computed(() => {
|
||||||
|
const { rowKey }: any = unref(propsRef);
|
||||||
|
return rowKey ? rowKey : () => {
|
||||||
|
return 'key'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const getDataSourceRef = computed(() => {
|
||||||
|
const dataSource = unref(dataSourceRef);
|
||||||
|
if (!dataSource || dataSource.length === 0) {
|
||||||
|
return unref(dataSourceRef);
|
||||||
|
}
|
||||||
|
return unref(dataSourceRef);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetch(opt?) {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { request, pagination }: any = unref(propsRef);
|
||||||
|
|
||||||
|
//组装分页信息
|
||||||
|
const pageField = APISETTING.pageField
|
||||||
|
const sizeField = APISETTING.sizeField
|
||||||
|
const totalField = APISETTING.totalField
|
||||||
|
const listField = APISETTING.listField
|
||||||
|
|
||||||
|
let pageParams = {};
|
||||||
|
const { page = 1, pageSize = 10 } = unref(getPaginationInfo) as PaginationProps;
|
||||||
|
|
||||||
|
if ((isBoolean(pagination) && !pagination) || isBoolean(getPaginationInfo)) {
|
||||||
|
pageParams = {};
|
||||||
|
} else {
|
||||||
|
pageParams[pageField] = (opt && opt[pageField]) || page;
|
||||||
|
pageParams[sizeField] = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
...pageParams,
|
||||||
|
}
|
||||||
|
const res = await request(params);
|
||||||
|
|
||||||
|
const resultTotal = res[totalField] || 0
|
||||||
|
const currentPage = res[pageField]
|
||||||
|
|
||||||
|
// 如果数据异常,需获取正确的页码再次执行
|
||||||
|
if (resultTotal) {
|
||||||
|
const currentTotalPage = Math.ceil(resultTotal / pageSize);
|
||||||
|
if (page > currentTotalPage) {
|
||||||
|
setPagination({
|
||||||
|
[pageField]: currentTotalPage,
|
||||||
|
});
|
||||||
|
fetch(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let resultInfo = res[listField] ? res[listField] : []
|
||||||
|
dataSourceRef.value = resultInfo;
|
||||||
|
setPagination({
|
||||||
|
[pageField]: currentPage,
|
||||||
|
[totalField]: resultTotal,
|
||||||
|
});
|
||||||
|
if (opt && opt[pageField]) {
|
||||||
|
setPagination({
|
||||||
|
[pageField]: opt[pageField] || 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
emit('fetch-success', {
|
||||||
|
items: unref(resultInfo),
|
||||||
|
resultTotal
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
emit('fetch-error', error);
|
||||||
|
dataSourceRef.value = [];
|
||||||
|
// setPagination({
|
||||||
|
// pageCount: 0,
|
||||||
|
// });
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
fetch();
|
||||||
|
}, 16)
|
||||||
|
});
|
||||||
|
|
||||||
|
function setTableData(values) {
|
||||||
|
dataSourceRef.value = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataSource(): any[] {
|
||||||
|
return getDataSourceRef.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload(opt?) {
|
||||||
|
await fetch(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fetch,
|
||||||
|
getRowKey,
|
||||||
|
getDataSourceRef,
|
||||||
|
getDataSource,
|
||||||
|
setTableData,
|
||||||
|
reload
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/components/Table/src/hooks/useLoading.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ref, ComputedRef, unref, computed, watch } from 'vue';
|
||||||
|
import type { BasicTableProps } from '../types/table';
|
||||||
|
|
||||||
|
export function useLoading(props: ComputedRef<BasicTableProps>) {
|
||||||
|
const loadingRef = ref(unref(props).loading);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => unref(props).loading,
|
||||||
|
(loading) => {
|
||||||
|
loadingRef.value = loading;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getLoading = computed(() => unref(loadingRef));
|
||||||
|
|
||||||
|
function setLoading(loading: boolean) {
|
||||||
|
loadingRef.value = loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { getLoading, setLoading };
|
||||||
|
}
|
||||||
48
src/components/Table/src/hooks/usePagination.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import type { PaginationProps } from '../types/pagination';
|
||||||
|
import type { BasicTableProps } from '../types/table';
|
||||||
|
import { computed, unref, ref, ComputedRef } from 'vue';
|
||||||
|
|
||||||
|
import { isBoolean } from '@/utils/is';
|
||||||
|
import { DEFAULTPAGESIZE, PAGESIZES } from '../const';
|
||||||
|
|
||||||
|
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
|
||||||
|
const configRef = ref<PaginationProps>({});
|
||||||
|
const show = ref(true);
|
||||||
|
|
||||||
|
const getPaginationInfo = computed((): PaginationProps | boolean => {
|
||||||
|
const { pagination } = unref(refProps);
|
||||||
|
if (!unref(show) || (isBoolean(pagination) && !pagination)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
pageSize: DEFAULTPAGESIZE,
|
||||||
|
pageSizes: PAGESIZES,
|
||||||
|
showSizePicker: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
...(isBoolean(pagination) ? {} : pagination),
|
||||||
|
...unref(configRef),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function setPagination(info: Partial<PaginationProps>) {
|
||||||
|
const paginationInfo = unref(getPaginationInfo);
|
||||||
|
configRef.value = {
|
||||||
|
...(!isBoolean(paginationInfo) ? paginationInfo : {}),
|
||||||
|
...info,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPagination() {
|
||||||
|
return unref(getPaginationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShowPagination() {
|
||||||
|
return unref(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setShowPagination(flag: boolean) {
|
||||||
|
show.value = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { getPagination, getPaginationInfo, setShowPagination, getShowPagination, setPagination };
|
||||||
|
}
|
||||||
45
src/components/Table/src/props.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { BasicColumn } from './types/table'
|
||||||
|
|
||||||
|
export const basicProps = {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
titleTooltip: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: 'medium',
|
||||||
|
},
|
||||||
|
tableData: {
|
||||||
|
type: [Object],
|
||||||
|
default: () => {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
type: [Array] as PropType<BasicColumn[]>,
|
||||||
|
default: () => [],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
type: Function as PropType<(...arg: any[]) => Promise<any>>,
|
||||||
|
default: null,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
rowKey: {
|
||||||
|
type: [String, Function] as PropType<string | ((record) => string)>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
type: [Object, Boolean],
|
||||||
|
default: () => {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showPagination: {
|
||||||
|
type: [String, Boolean],
|
||||||
|
default: 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/components/Table/src/types/pagination.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import Pagination from 'naive-ui/lib/pagination';
|
||||||
|
import { VNodeChild } from 'vue';
|
||||||
|
|
||||||
|
export interface PaginationProps {
|
||||||
|
page?: number;
|
||||||
|
pageCount?: number,
|
||||||
|
pageSize?: number,
|
||||||
|
pageSizes?: number[],
|
||||||
|
showSizePicker?: boolean,
|
||||||
|
showQuickJumper?: boolean,
|
||||||
|
}
|
||||||
22
src/components/Table/src/types/table.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type {
|
||||||
|
TableBaseColumn,
|
||||||
|
} from 'naive-ui/lib/data-table/src/interface';
|
||||||
|
|
||||||
|
export interface BasicColumn extends TableBaseColumn {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableActionType {
|
||||||
|
reload: (opt) => Promise<void>;
|
||||||
|
emit?: any;
|
||||||
|
getColumns: (opt) => BasicColumn[];
|
||||||
|
setColumns: (columns: BasicColumn[] | string[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BasicTableProps<T = any> {
|
||||||
|
title?: string,
|
||||||
|
dataSource: Function,
|
||||||
|
columns: any[],
|
||||||
|
pagination: object,
|
||||||
|
showPagination: boolean
|
||||||
|
}
|
||||||
1
src/components/Upload/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as BasicUpload } from './src/BasicUpload.vue';
|
||||||
313
src/components/Upload/src/BasicUpload.vue
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="upload">
|
||||||
|
<div class="upload-card">
|
||||||
|
|
||||||
|
<!--图片列表-->
|
||||||
|
<div class="upload-card-item" :style="getCSSProperties" v-for="(item,index) in imgList">
|
||||||
|
<div class="upload-card-item-info">
|
||||||
|
<div class="img-box">
|
||||||
|
<img :src="item"/>
|
||||||
|
</div>
|
||||||
|
<div class="img-box-actions">
|
||||||
|
<n-icon size="18" class="action-icon mx-2" @click="preview(item)">
|
||||||
|
<EyeOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
<n-icon size="18" class="action-icon mx-2" @click="remove(index)">
|
||||||
|
<DeleteOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--上传图片-->
|
||||||
|
<div class="upload-card-item upload-card-item-select-picture" :style="getCSSProperties"
|
||||||
|
v-if="imgList.length < maxNumber">
|
||||||
|
<n-upload
|
||||||
|
v-bind="$props"
|
||||||
|
:file-list-style="{ display:'none'}"
|
||||||
|
@before-upload="beforeUpload"
|
||||||
|
@finish="finish"
|
||||||
|
>
|
||||||
|
<div class="flex justify-center flex-col">
|
||||||
|
<n-icon size="18" class="m-auto">
|
||||||
|
<PlusOutlined/>
|
||||||
|
</n-icon>
|
||||||
|
<span class="upload-title">上传图片</span>
|
||||||
|
</div>
|
||||||
|
</n-upload>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--上传图片-->
|
||||||
|
<n-space>
|
||||||
|
<n-alert title="提示" type="info" v-if="helpText" class="flex w-full">
|
||||||
|
{{ helpText }}
|
||||||
|
</n-alert>
|
||||||
|
</n-space>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--预览图片-->
|
||||||
|
<n-modal
|
||||||
|
v-model:show="showModal"
|
||||||
|
preset="card"
|
||||||
|
title="预览"
|
||||||
|
:bordered="false"
|
||||||
|
:style="{width: '520px'}"
|
||||||
|
>
|
||||||
|
<img :src="previewUrl"/>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, toRefs, reactive, computed } from "vue";
|
||||||
|
import { EyeOutlined, DeleteOutlined, PlusOutlined } from "@vicons/antd";
|
||||||
|
import { NUpload } from 'naive-ui'
|
||||||
|
import { basicProps } from "./props";
|
||||||
|
import { useMessage, useDialog } from 'naive-ui'
|
||||||
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
|
import componentSetting from '@/settings/componentSetting'
|
||||||
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
import { isString } from '@/utils/is'
|
||||||
|
|
||||||
|
const globSetting = useGlobSetting()
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'BasicUpload',
|
||||||
|
|
||||||
|
components: { EyeOutlined, DeleteOutlined, PlusOutlined },
|
||||||
|
props: {
|
||||||
|
...NUpload.props, // 这里继承原 UI 组件的 props
|
||||||
|
...basicProps
|
||||||
|
},
|
||||||
|
emits: ['uploadChange', 'delete'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { value, width, height } = props
|
||||||
|
|
||||||
|
const getCSSProperties = computed(() => {
|
||||||
|
return {
|
||||||
|
width: `${ width }px`,
|
||||||
|
height: `${ height }px`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
showModal: false,
|
||||||
|
previewUrl: '',
|
||||||
|
originalImgList: [],
|
||||||
|
imgList: []
|
||||||
|
})
|
||||||
|
|
||||||
|
//赋值默认图片显示
|
||||||
|
if (value.length) {
|
||||||
|
state.imgList = value.map(item => {
|
||||||
|
return getImgUrl(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//预览
|
||||||
|
function preview(url: string) {
|
||||||
|
state.showModal = true
|
||||||
|
state.previewUrl = url
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除
|
||||||
|
function remove(index: number) {
|
||||||
|
dialog.info({
|
||||||
|
title: '提示',
|
||||||
|
content: '你确定要删除吗?',
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
state.imgList.splice(index, 1)
|
||||||
|
state.originalImgList.splice(index, 1)
|
||||||
|
emit('uploadChange', state.originalImgList)
|
||||||
|
emit('delete', state.originalImgList)
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//组装完整图片地址
|
||||||
|
function getImgUrl(url: string) {
|
||||||
|
const { imgUrl } = globSetting
|
||||||
|
return (/(^http|https:\/\/)/g).test(url) ? url : `${ imgUrl }${ url }`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkFileType(fileType: string) {
|
||||||
|
return componentSetting.upload.fileType.includes(fileType)
|
||||||
|
}
|
||||||
|
|
||||||
|
//上传之前
|
||||||
|
function beforeUpload({ file, fileList }) {
|
||||||
|
const fileInfo = file.file;
|
||||||
|
const { maxSize, accept, maxNumber } = props;
|
||||||
|
const acceptRef = (isString(accept) && accept.split(',')) || [];
|
||||||
|
|
||||||
|
// 设置最大值,则判断
|
||||||
|
if (maxSize && fileInfo.size / 1024 / 1024 >= maxSize) {
|
||||||
|
message.error(`上传文件最大值不能超过${ maxSize }M`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置类型,则判断
|
||||||
|
const fileType = componentSetting.upload.fileType
|
||||||
|
if (acceptRef.length > 0 && !checkFileType(fileInfo.type)) {
|
||||||
|
message.error(`只能上传文件类型为${ fileType.join(',') }`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//上传结束
|
||||||
|
function finish({ event: Event }) {
|
||||||
|
const res = eval('(' + Event.target.response + ')');
|
||||||
|
const infoField = componentSetting.upload.apiSetting.infoField
|
||||||
|
const { code } = res
|
||||||
|
const message = (res.msg || res.message) || '上传失败'
|
||||||
|
const result = res[infoField]
|
||||||
|
//成功
|
||||||
|
if (code === ResultEnum.SUCCESS) {
|
||||||
|
let imgUrl: string = getImgUrl(result.photo)
|
||||||
|
state.imgList.push(imgUrl)
|
||||||
|
state.originalImgList.push(result.photo)
|
||||||
|
emit('uploadChange', state.originalImgList)
|
||||||
|
} else message.error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
finish,
|
||||||
|
preview,
|
||||||
|
remove,
|
||||||
|
beforeUpload,
|
||||||
|
getCSSProperties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
|
||||||
|
.upload {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin: 0 8px 8px 0;
|
||||||
|
position: relative;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: 0 0;
|
||||||
|
|
||||||
|
&-info::before {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-info {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.img-box-actions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, .5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all .3s;
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-box {
|
||||||
|
position: relative;
|
||||||
|
//padding: 8px;
|
||||||
|
//border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-box-actions {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
white-space: nowrap;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all .3s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-icon {
|
||||||
|
color: rgba(255, 255, 255, .85);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item-select-picture {
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fafafa;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.upload-title {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item:hover {
|
||||||
|
background: 0 0;
|
||||||
|
|
||||||
|
.upload-card-item-info::before {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
src/components/Upload/src/props.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { NUpload } from 'naive-ui'
|
||||||
|
|
||||||
|
export const basicProps = {
|
||||||
|
...NUpload.props,
|
||||||
|
accept: {
|
||||||
|
type: String,
|
||||||
|
default: '.jpg,.png,.jpeg,.svg,.gif',
|
||||||
|
},
|
||||||
|
helpText: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
maxSize: {
|
||||||
|
type: Number as PropType<number>,
|
||||||
|
default: 2
|
||||||
|
},
|
||||||
|
maxNumber: {
|
||||||
|
type: Number as PropType<number>,
|
||||||
|
default: Infinity
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Array as PropType<string[]>,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number as PropType<number>,
|
||||||
|
default: 104
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number as PropType<number>,
|
||||||
|
default: 104 //建议不小于这个尺寸 太小页面可能显示有异常
|
||||||
|
},
|
||||||
|
}
|
||||||
7
src/components/Upload/src/type/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export interface BasicProps<T = any> {
|
||||||
|
title?: string,
|
||||||
|
dataSource: Function,
|
||||||
|
columns: any[],
|
||||||
|
pagination: object,
|
||||||
|
showPagination: boolean
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
export enum PageEnum {
|
export enum PageEnum {
|
||||||
// 登录
|
// 登录
|
||||||
BASE_LOGIN = '/login',
|
BASE_LOGIN = '/login',
|
||||||
|
BASE_LOGIN_NAME = 'Login',
|
||||||
//重定向
|
//重定向
|
||||||
REDIRECT = '/Redirect',
|
REDIRECT = '/redirect',
|
||||||
|
REDIRECT_NAME = 'Redirect',
|
||||||
// 首页
|
// 首页
|
||||||
BASE_HOME = '/dashboard',
|
BASE_HOME = '/dashboard',
|
||||||
// 错误
|
// 错误
|
||||||
ERROR_PAGE = '/exception',
|
ERROR_PAGE_NAME = 'ErrorPage',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
|||||||
VITE_GLOB_APP_SHORT_NAME,
|
VITE_GLOB_APP_SHORT_NAME,
|
||||||
VITE_GLOB_API_URL_PREFIX,
|
VITE_GLOB_API_URL_PREFIX,
|
||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PERMISSION_MODE,
|
VITE_GLOB_PROD_MOCK,
|
||||||
VITE_GLOB_PROD_MOCK
|
VITE_GLOB_IMG_URL
|
||||||
} = getAppEnvConfig();
|
} = getAppEnvConfig();
|
||||||
|
|
||||||
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||||
@@ -27,8 +27,9 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
|
|||||||
shortName: VITE_GLOB_APP_SHORT_NAME,
|
shortName: VITE_GLOB_APP_SHORT_NAME,
|
||||||
urlPrefix: VITE_GLOB_API_URL_PREFIX,
|
urlPrefix: VITE_GLOB_API_URL_PREFIX,
|
||||||
uploadUrl: VITE_GLOB_UPLOAD_URL,
|
uploadUrl: VITE_GLOB_UPLOAD_URL,
|
||||||
permissionMode: VITE_GLOB_PERMISSION_MODE,
|
prodMock: VITE_GLOB_PROD_MOCK,
|
||||||
prodMock: VITE_GLOB_PROD_MOCK
|
imgUrl: VITE_GLOB_IMG_URL
|
||||||
|
|
||||||
};
|
};
|
||||||
return glob as Readonly<GlobConfig>;
|
return glob as Readonly<GlobConfig>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="copyright">
|
<div class="copyright">
|
||||||
naive-ui-admin 1.2 · Made by Ah jung
|
naive-ui-admin 1.3 · Made by Ah jung
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,9 @@
|
|||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<n-avatar>
|
<n-avatar>
|
||||||
{{ username }}
|
{{ username }}
|
||||||
<template #icon><UserOutlined/></template>
|
<template #icon>
|
||||||
|
<UserOutlined/>
|
||||||
|
</template>
|
||||||
</n-avatar>
|
</n-avatar>
|
||||||
</div>
|
</div>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
@@ -188,7 +190,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
const doLogout = () => {
|
const doLogout = () => {
|
||||||
dialog.warning({
|
dialog.info({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '您确定要退出登录吗',
|
content: '您确定要退出登录吗',
|
||||||
positiveText: '确定',
|
positiveText: '确定',
|
||||||
@@ -269,7 +271,7 @@ export default defineComponent({
|
|||||||
const avatarSelect = (key) => {
|
const avatarSelect = (key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 1:
|
case 1:
|
||||||
router.push({name:'Setting'})
|
router.push({ name: 'Setting' })
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
doLogout()
|
doLogout()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="~@/assets/images/logo.png" alt=""/>
|
<img src="~@/assets/images/logo.png" alt=""/>
|
||||||
<h2 v-show="!collapsed" class="title">NaiveUiAdmin</h2>
|
<h2 v-show="!collapsed" class="title"> NaiveUiAdmin</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
width: auto;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ export default defineComponent({
|
|||||||
watch(
|
watch(
|
||||||
() => currentRoute.fullPath,
|
() => currentRoute.fullPath,
|
||||||
() => {
|
() => {
|
||||||
|
const matched = currentRoute.matched
|
||||||
|
const getOpenKeys = matched && matched.length ? [matched[0]?.name] : []
|
||||||
|
state.openKeys = getOpenKeys
|
||||||
state.selectedKeys = currentRoute.name
|
state.selectedKeys = currentRoute.name
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -89,6 +92,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
//展开菜单
|
//展开菜单
|
||||||
function menuExpanded(openKeys: string[]) {
|
function menuExpanded(openKeys: string[]) {
|
||||||
|
console.log(openKeys)
|
||||||
if (!openKeys) return
|
if (!openKeys) return
|
||||||
const latestOpenKey = openKeys.pop();
|
const latestOpenKey = openKeys.pop();
|
||||||
state.openKeys = latestOpenKey ? [latestOpenKey] : []
|
state.openKeys = latestOpenKey ? [latestOpenKey] : []
|
||||||
|
|||||||
@@ -52,7 +52,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, computed, ref, toRefs, toRaw, unref, provide, watch, onMounted, nextTick } from 'vue'
|
import {
|
||||||
|
defineComponent,
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
ref,
|
||||||
|
toRefs,
|
||||||
|
toRaw,
|
||||||
|
unref,
|
||||||
|
provide,
|
||||||
|
watch,
|
||||||
|
onMounted,
|
||||||
|
nextTick
|
||||||
|
} from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { storage } from '@/utils/Storage'
|
import { storage } from '@/utils/Storage'
|
||||||
import { TABS_ROUTES } from '@/store/mutation-types'
|
import { TABS_ROUTES } from '@/store/mutation-types'
|
||||||
@@ -203,14 +215,15 @@ export default defineComponent({
|
|||||||
|
|
||||||
// 标签页列表
|
// 标签页列表
|
||||||
const tabsList: any = computed(() => tabsViewStore.tabsList)
|
const tabsList: any = computed(() => tabsViewStore.tabsList)
|
||||||
const whiteList = [PageEnum.REDIRECT, PageEnum.BASE_LOGIN]
|
const whiteList: string[] = [PageEnum.BASE_LOGIN_NAME, PageEnum.REDIRECT_NAME, PageEnum.ERROR_PAGE_NAME]
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.fullPath,
|
() => route.fullPath,
|
||||||
(to) => {
|
(to) => {
|
||||||
if (whiteList.includes(route.name as string) || ['ErrorPage'].includes(route.name as string)) return
|
if (whiteList.includes(route.name as string)) return
|
||||||
state.activeKey = to
|
state.activeKey = to
|
||||||
tabsViewStore.addTabs(getSimpleRoute(route))
|
tabsViewStore.addTabs(getSimpleRoute(route))
|
||||||
|
updateNavScroll()
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
@@ -233,6 +246,7 @@ export default defineComponent({
|
|||||||
state.activeKey = currentRoute.fullPath
|
state.activeKey = currentRoute.fullPath
|
||||||
router.push(currentRoute)
|
router.push(currentRoute)
|
||||||
}
|
}
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新页面
|
// 刷新页面
|
||||||
@@ -251,6 +265,7 @@ export default defineComponent({
|
|||||||
tabsViewStore.closeLeftTabs(route)
|
tabsViewStore.closeLeftTabs(route)
|
||||||
state.activeKey = route.fullPath
|
state.activeKey = route.fullPath
|
||||||
router.replace(route.fullPath)
|
router.replace(route.fullPath)
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭右侧
|
// 关闭右侧
|
||||||
@@ -258,6 +273,7 @@ export default defineComponent({
|
|||||||
tabsViewStore.closeRightTabs(route)
|
tabsViewStore.closeRightTabs(route)
|
||||||
state.activeKey = route.fullPath
|
state.activeKey = route.fullPath
|
||||||
router.replace(route.fullPath)
|
router.replace(route.fullPath)
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭其他
|
// 关闭其他
|
||||||
@@ -265,6 +281,7 @@ export default defineComponent({
|
|||||||
tabsViewStore.closeOtherTabs(route)
|
tabsViewStore.closeOtherTabs(route)
|
||||||
state.activeKey = route.fullPath
|
state.activeKey = route.fullPath
|
||||||
router.replace(route.fullPath)
|
router.replace(route.fullPath)
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭全部
|
// 关闭全部
|
||||||
@@ -272,6 +289,7 @@ export default defineComponent({
|
|||||||
localStorage.removeItem('routes')
|
localStorage.removeItem('routes')
|
||||||
tabsViewStore.closeAllTabs()
|
tabsViewStore.closeAllTabs()
|
||||||
router.replace('/')
|
router.replace('/')
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
//tab 操作
|
//tab 操作
|
||||||
@@ -294,6 +312,7 @@ export default defineComponent({
|
|||||||
closeAll()
|
closeAll()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
updateNavScroll()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentScrollOffset() {
|
function getCurrentScrollOffset() {
|
||||||
@@ -315,7 +334,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function scrollNext() {
|
function scrollNext() {
|
||||||
const navWidth = navRef.value.offsetWidth
|
const navWidth = navRef.value.scrollWidth
|
||||||
const containerWidth = navScroll.value.offsetWidth
|
const containerWidth = navScroll.value.offsetWidth
|
||||||
const currentOffset = getCurrentScrollOffset()
|
const currentOffset = getCurrentScrollOffset()
|
||||||
if (navWidth - currentOffset <= containerWidth) return
|
if (navWidth - currentOffset <= containerWidth) return
|
||||||
@@ -329,8 +348,9 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateNavScroll() {
|
function updateNavScroll() {
|
||||||
const navWidth = navRef.value.offsetWidth
|
if (!navRef.value) return
|
||||||
const containerWidth = navScroll.value.offsetWidth
|
let navWidth = navRef.value.scrollWidth
|
||||||
|
let containerWidth = navScroll.value.offsetWidth
|
||||||
const currentOffset = getCurrentScrollOffset()
|
const currentOffset = getCurrentScrollOffset()
|
||||||
if (containerWidth < navWidth) {
|
if (containerWidth < navWidth) {
|
||||||
state.scrollable = true
|
state.scrollable = true
|
||||||
@@ -383,10 +403,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
onElementResize()
|
||||||
|
})
|
||||||
|
|
||||||
|
function onElementResize() {
|
||||||
let observer
|
let observer
|
||||||
observer = elementResizeDetectorMaker()
|
observer = elementResizeDetectorMaker()
|
||||||
observer.listenTo(navWrap.value, handleResize)
|
observer.listenTo(navWrap.value, handleResize)
|
||||||
})
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
@@ -419,7 +443,7 @@ export default defineComponent({
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.tabs-view {
|
.tabs-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 6px 0px;
|
padding: 6px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
@@ -519,7 +543,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.active-item{
|
|
||||||
|
.active-item {
|
||||||
color: #2d8cf0
|
color: #2d8cf0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,6 +589,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tabs-view-fixed-header {
|
.tabs-view-fixed-header {
|
||||||
top: 0px;
|
top: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ 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, setupGlobalMethods, setupCustomComponents } from '@/plugins'
|
import { setupNaive, setupDirectives, setupGlobalMethods, setupCustomComponents } from '@/plugins'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
app.use(MakeitCaptcha)
|
||||||
|
|
||||||
// 注册全局常用的 naive-ui 组件
|
// 注册全局常用的 naive-ui 组件
|
||||||
setupNaive(app)
|
setupNaive(app)
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ import {
|
|||||||
NTable,
|
NTable,
|
||||||
NInputNumber,
|
NInputNumber,
|
||||||
NLoadingBarProvider,
|
NLoadingBarProvider,
|
||||||
NModal
|
NModal,
|
||||||
|
NUpload
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
|
||||||
const naive = create({
|
const naive = create({
|
||||||
@@ -122,7 +123,8 @@ const naive = create({
|
|||||||
NTable,
|
NTable,
|
||||||
NInputNumber,
|
NInputNumber,
|
||||||
NLoadingBarProvider,
|
NLoadingBarProvider,
|
||||||
NModal
|
NModal,
|
||||||
|
NUpload
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import { RouteRecordRaw } from 'vue-router'
|
|||||||
* @param parent
|
* @param parent
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export const routerGenerator = (routerMap, parent?):any[] => {
|
export const routerGenerator = (routerMap, parent?): any[] => {
|
||||||
return routerMap.map(item => {
|
return routerMap.map(item => {
|
||||||
const currentRouter:any = {
|
const currentRouter: any = {
|
||||||
// 路由地址 动态拼接生成如 /dashboard/workplace
|
// 路由地址 动态拼接生成如 /dashboard/workplace
|
||||||
path: `${ parent && parent.path || '' }/${ item.path }`,
|
path: `${ parent && parent.path || '' }/${ item.path }`,
|
||||||
// 路由名称,建议唯一
|
// 路由名称,建议唯一
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: Layout,
|
component: Layout,
|
||||||
meta: {
|
meta: {
|
||||||
title: '组件示例',
|
title: '组件示例',
|
||||||
icon: renderIcon(WalletOutlined ),
|
icon: renderIcon(WalletOutlined),
|
||||||
sort: 8
|
sort: 8
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
@@ -36,6 +36,15 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
title: '表格',
|
title: '表格',
|
||||||
},
|
},
|
||||||
component: () => import('@/views/comp/table/list.vue')
|
component: () => import('@/views/comp/table/list.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
path: 'upload',
|
||||||
|
name: `${ routeName }_upload`,
|
||||||
|
meta: {
|
||||||
|
title: '上传',
|
||||||
|
},
|
||||||
|
component: () => import('@/views/comp/upload/index.vue')
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { toRefs } from 'vue'
|
|
||||||
import { isNavigationFailure, Router } from 'vue-router'
|
import { isNavigationFailure, Router } from 'vue-router'
|
||||||
import { useUserStoreWidthOut } from '@/store/modules/user'
|
import { useUserStoreWidthOut } from '@/store/modules/user'
|
||||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute'
|
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute'
|
||||||
import NProgress from 'nprogress' // progress bar
|
import NProgress from 'nprogress' // progress bar
|
||||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||||
import { storage } from '@/utils/Storage'
|
import { storage } from '@/utils/Storage'
|
||||||
import { debounce } from '@/utils/lodashChunk'
|
|
||||||
import { PageEnum } from '@/enums/pageEnum'
|
import { PageEnum } from '@/enums/pageEnum'
|
||||||
|
|
||||||
|
|
||||||
@@ -15,39 +13,6 @@ const LOGIN_PATH = PageEnum.BASE_LOGIN
|
|||||||
|
|
||||||
const whitePathList = [LOGIN_PATH] // no redirect whitelist
|
const whitePathList = [LOGIN_PATH] // no redirect whitelist
|
||||||
|
|
||||||
// 是否需要从后端获取菜单
|
|
||||||
const isGetMenus = debounce(
|
|
||||||
({ to, from, next, hasRoute, router }) => {
|
|
||||||
const userStore = useUserStoreWidthOut();
|
|
||||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
|
||||||
userStore.GetInfo().then(res => {
|
|
||||||
asyncRouteStore.generateRoutes(res).then(() => {
|
|
||||||
// 根据roles权限生成可访问的路由表
|
|
||||||
// 动态添加可访问路由表
|
|
||||||
asyncRouteStore.getRouters().forEach((item) => {
|
|
||||||
router.addRoute(item)
|
|
||||||
});
|
|
||||||
// if (whitePathList.includes(to.name as string)) return
|
|
||||||
if (!hasRoute) {
|
|
||||||
// 请求带有 redirect 重定向时,登录自动重定向到该地址
|
|
||||||
const redirect = decodeURIComponent((from.query.redirect || '') as string)
|
|
||||||
if (to.path === redirect) {
|
|
||||||
next({ ...to, replace: true })
|
|
||||||
} else {
|
|
||||||
// 跳转到目的路由
|
|
||||||
next({ ...to, replace: true })
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
}).catch(() => next({ path: defaultRoutePath }))
|
|
||||||
})
|
|
||||||
},
|
|
||||||
1800,
|
|
||||||
{ leading: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
export function createRouterGuards(router: Router) {
|
export function createRouterGuards(router: Router) {
|
||||||
const userStore = useUserStoreWidthOut();
|
const userStore = useUserStoreWidthOut();
|
||||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||||
@@ -65,7 +30,6 @@ export function createRouterGuards(router: Router) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const token = storage.get(ACCESS_TOKEN)
|
const token = storage.get(ACCESS_TOKEN)
|
||||||
const roles = storage.get('roles')
|
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
|
||||||
@@ -111,7 +75,7 @@ export function createRouterGuards(router: Router) {
|
|||||||
NProgress.done()
|
NProgress.done()
|
||||||
})
|
})
|
||||||
|
|
||||||
router.afterEach((to, from, failure) => {
|
router.afterEach((to, _, failure) => {
|
||||||
document.title = (to?.meta?.title as string) || document.title
|
document.title = (to?.meta?.title as string) || document.title
|
||||||
if (isNavigationFailure(failure)) {
|
if (isNavigationFailure(failure)) {
|
||||||
//console.log('failed navigation', failure)
|
//console.log('failed navigation', failure)
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
|
import type { RouteRecordRaw, RouteMeta } from 'vue-router';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
import { RoleEnum } from '@/enums/roleEnum'
|
import { RoleEnum } from '@/enums/roleEnum'
|
||||||
|
|
||||||
|
export type Component<T extends any = any> =
|
||||||
|
| ReturnType<typeof defineComponent>
|
||||||
|
| (() => Promise<typeof import('*.vue')>)
|
||||||
|
| (() => Promise<T>);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
||||||
|
name: string;
|
||||||
|
meta: RouteMeta;
|
||||||
|
component?: Component | string;
|
||||||
|
components?: Component;
|
||||||
|
children?: AppRouteRecordRaw[];
|
||||||
|
props?: Recordable;
|
||||||
|
fullPath?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Meta {
|
export interface Meta {
|
||||||
// 名称
|
// 名称
|
||||||
title: string
|
title: string
|
||||||
|
|||||||
@@ -14,5 +14,18 @@ export default {
|
|||||||
defaultPageSize: 10,
|
defaultPageSize: 10,
|
||||||
//可切换每页数量集合
|
//可切换每页数量集合
|
||||||
pageSizes: [10, 20, 30, 40, 50],
|
pageSizes: [10, 20, 30, 40, 50],
|
||||||
|
},
|
||||||
|
upload: {
|
||||||
|
//考虑接口规范不同
|
||||||
|
apiSetting: {
|
||||||
|
// 集合字段名
|
||||||
|
infoField: 'data',
|
||||||
|
// 图片地址字段名
|
||||||
|
imgField: 'photo'
|
||||||
|
},
|
||||||
|
//最大上传图片大小
|
||||||
|
maxSize: 2,
|
||||||
|
//图片上传类型
|
||||||
|
fileType: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/svg+xml'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ import { toRaw, unref } from 'vue'
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { RouteRecordRaw } from 'vue-router'
|
import { RouteRecordRaw } from 'vue-router'
|
||||||
import { store } from '@/store'
|
import { store } from '@/store'
|
||||||
import { createStorage } from '@/utils/Storage'
|
|
||||||
import { asyncRoutes, constantRouter } from '@/router/index'
|
import { asyncRoutes, constantRouter } from '@/router/index'
|
||||||
import { generatorDynamicRouter } from '@/router/generator-routers'
|
import { generatorDynamicRouter } from '@/router/generator-routers'
|
||||||
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
|
||||||
|
|
||||||
const Storage = createStorage({ storage: localStorage })
|
|
||||||
|
|
||||||
|
|
||||||
interface TreeHelperConfig {
|
interface TreeHelperConfig {
|
||||||
id: string;
|
id: string;
|
||||||
children: string;
|
children: string;
|
||||||
@@ -64,7 +60,7 @@ export const useAsyncRouteStore = defineStore({
|
|||||||
isDynamicAddedRoute: false,
|
isDynamicAddedRoute: false,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getMenus(state: AsyncRouteState): RouteRecordRaw[] {
|
getMenus(): RouteRecordRaw[] {
|
||||||
return this.menus
|
return this.menus
|
||||||
},
|
},
|
||||||
getIsDynamicAddedRoute(): boolean {
|
getIsDynamicAddedRoute(): boolean {
|
||||||
|
|||||||
@@ -94,8 +94,8 @@ body .n-card {
|
|||||||
transition: all .2s ease-in-out;
|
transition: all .2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
body .n-icon{
|
body .n-icon {
|
||||||
vertical-align:middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
body .proCard {
|
body .proCard {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { GlobEnvConfig } from '#/config';
|
import type { GlobEnvConfig } from '/#/config';
|
||||||
|
|
||||||
import { warn } from '@/utils/log';
|
import { warn } from '@/utils/log';
|
||||||
import pkg from '../../package.json';
|
import pkg from '../../package.json';
|
||||||
@@ -28,7 +28,8 @@ export function getAppEnvConfig() {
|
|||||||
VITE_GLOB_APP_SHORT_NAME,
|
VITE_GLOB_APP_SHORT_NAME,
|
||||||
VITE_GLOB_API_URL_PREFIX,
|
VITE_GLOB_API_URL_PREFIX,
|
||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PROD_MOCK
|
VITE_GLOB_PROD_MOCK,
|
||||||
|
VITE_GLOB_IMG_URL
|
||||||
} = ENV;
|
} = ENV;
|
||||||
|
|
||||||
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
|
||||||
@@ -43,7 +44,8 @@ export function getAppEnvConfig() {
|
|||||||
VITE_GLOB_APP_SHORT_NAME,
|
VITE_GLOB_APP_SHORT_NAME,
|
||||||
VITE_GLOB_API_URL_PREFIX,
|
VITE_GLOB_API_URL_PREFIX,
|
||||||
VITE_GLOB_UPLOAD_URL,
|
VITE_GLOB_UPLOAD_URL,
|
||||||
VITE_GLOB_PROD_MOCK
|
VITE_GLOB_PROD_MOCK,
|
||||||
|
VITE_GLOB_IMG_URL
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ import { useUserStoreWidthOut } from '@/store/modules/user'
|
|||||||
|
|
||||||
const globSetting = useGlobSetting()
|
const globSetting = useGlobSetting()
|
||||||
const urlPrefix = globSetting.urlPrefix || '';
|
const urlPrefix = globSetting.urlPrefix || '';
|
||||||
|
// @ts-ignore
|
||||||
const Message = window.$message
|
const { $message: Message, $dialog: Modal } = window
|
||||||
const Modal = window.$dialog
|
|
||||||
|
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { storage } from '@/utils/Storage'
|
import { storage } from '@/utils/Storage'
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export interface RequestOptions {
|
|||||||
apiUrl?: string
|
apiUrl?: string
|
||||||
// 错误消息提示类型
|
// 错误消息提示类型
|
||||||
errorMessageMode?: 'none' | 'modal'
|
errorMessageMode?: 'none' | 'modal'
|
||||||
|
// 是否添加时间戳
|
||||||
|
joinTime?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Result<T = any> {
|
export interface Result<T = any> {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import type { App, Plugin } from 'vue';
|
import type { App, Plugin } from 'vue';
|
||||||
import { NIcon } from 'naive-ui'
|
import { NIcon } from 'naive-ui'
|
||||||
|
import { PageEnum } from "@/enums/pageEnum";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* render 图标
|
* render 图标
|
||||||
@@ -12,9 +13,9 @@ export function renderIcon(icon) {
|
|||||||
/**
|
/**
|
||||||
* 递归组装菜单格式
|
* 递归组装菜单格式
|
||||||
*/
|
*/
|
||||||
export function generatorMenu(routerMap: Array<any>, parent?: object) {
|
export function generatorMenu(routerMap: Array<any>) {
|
||||||
return routerMap.filter(item => {
|
return routerMap.filter(item => {
|
||||||
return item.meta.hidden != true && !['/:path(.*)*', '/', '/redirect', '/login'].includes(item.path)
|
return item.meta.hidden != true && !['/:path(.*)*', '/', PageEnum.REDIRECT, PageEnum.BASE_LOGIN].includes(item.path)
|
||||||
}).map(item => {
|
}).map(item => {
|
||||||
const currentMenu = {
|
const currentMenu = {
|
||||||
...item,
|
...item,
|
||||||
@@ -25,7 +26,7 @@ export function generatorMenu(routerMap: Array<any>, parent?: object) {
|
|||||||
// 是否有子菜单,并递归处理
|
// 是否有子菜单,并递归处理
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
// Recursion
|
// Recursion
|
||||||
currentMenu.children = generatorMenu(item.children, currentMenu)
|
currentMenu.children = generatorMenu(item.children)
|
||||||
}
|
}
|
||||||
return currentMenu
|
return currentMenu
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const columns = [
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width:150,
|
width: 150,
|
||||||
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
||||||
render() {
|
render() {
|
||||||
return [
|
return [
|
||||||
@@ -50,8 +50,8 @@ export const columns = [
|
|||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
size: 'small',
|
size: 'small',
|
||||||
type:'error',
|
type: 'error',
|
||||||
style:'margin-right:10px',
|
style: 'margin-right:10px',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-card :bordered="false" class="proCard">
|
<n-card :bordered="false" class="proCard">
|
||||||
<ProTable
|
<BasicTable
|
||||||
title="表格列表"
|
title="表格列表"
|
||||||
titleTooltip="这是一个提示"
|
titleTooltip="这是一个提示"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@@ -12,19 +12,19 @@
|
|||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
</template>
|
</template>
|
||||||
</ProTable>
|
</BasicTable>
|
||||||
</n-card>
|
</n-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onMounted, reactive, toRefs, ref, h } from 'vue'
|
import { defineComponent, onMounted, reactive, toRefs, ref, h } from 'vue'
|
||||||
import { NTag, NButton, useMessage } from 'naive-ui'
|
import { NTag, NButton, useMessage } from 'naive-ui'
|
||||||
import { ProTable } from '@/components/ProTable'
|
import { BasicTable } from '@/components/Table'
|
||||||
import { getTableList } from '@/api/table/list'
|
import { getTableList } from '@/api/table/list'
|
||||||
import { columns } from './columns'
|
import { columns } from './columns'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { ProTable },
|
components: { BasicTable },
|
||||||
setup() {
|
setup() {
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const actionRef = ref()
|
const actionRef = ref()
|
||||||
|
|||||||
128
src/views/comp/upload/index.vue
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="n-layout-page-header">
|
||||||
|
<n-card :bordered="false" title="上传图片">
|
||||||
|
上传图片,用于向用户收集图片信息
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
<n-card :bordered="false" class="proCard mt-4">
|
||||||
|
<n-grid cols="2 s:1 m:3 l:3 xl:3 2xl:3" responsive="screen">
|
||||||
|
<n-grid-item offset="0 s:0 m:1 l:1 xl:1 2xl:1">
|
||||||
|
<n-form
|
||||||
|
:label-width="80"
|
||||||
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
label-placement="left"
|
||||||
|
ref="formRef"
|
||||||
|
class="py-8"
|
||||||
|
>
|
||||||
|
<n-form-item label="预约姓名" path="name">
|
||||||
|
<n-input v-model:value="formValue.name" placeholder="输入姓名"/>
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="预约号码" path="mobile">
|
||||||
|
<n-input placeholder="电话号码" v-model:value="formValue.mobile"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="病例图片" path="images">
|
||||||
|
<BasicUpload
|
||||||
|
:action="`${uploadUrl}/v1.0/files`"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:data="{ type: 0 }"
|
||||||
|
name="files"
|
||||||
|
:width="100"
|
||||||
|
:height="100"
|
||||||
|
@uploadChange="uploadChange"
|
||||||
|
v-model:value="formValue.images"
|
||||||
|
helpText="单个文件不超过2MB,最多只能上传10个文件"/>
|
||||||
|
</n-form-item>
|
||||||
|
<div style="margin-left:80px">
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" @click="formSubmit">提交预约</n-button>
|
||||||
|
<n-button @click="resetForm">重置</n-button>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, unref, reactive, toRefs, } from 'vue'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { BasicUpload } from '@/components/Upload'
|
||||||
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
|
const globSetting = useGlobSetting()
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入预约姓名',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
remark: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入预约备注',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
required: true,
|
||||||
|
type: 'array',
|
||||||
|
message: '请上传病例图片',
|
||||||
|
trigger: 'change'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicUpload },
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
const { uploadUrl } = globSetting
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formValue: {
|
||||||
|
name: '',
|
||||||
|
mobile: '',
|
||||||
|
//图片列表 通常查看和编辑使用 绝对路径 | 相对路径都可以
|
||||||
|
images: ['https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'],
|
||||||
|
},
|
||||||
|
uploadHeaders: {
|
||||||
|
platform: "miniPrograms",
|
||||||
|
timestamp: new Date().getTime(),
|
||||||
|
token: 'Q6fFCuhc1vkKn5JNFWaCLf6gRAc5n0LQHd08dSnG4qo=',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function formSubmit() {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('验证成功')
|
||||||
|
} else {
|
||||||
|
message.error('验证失败,请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
formRef.value.restoreValidation()
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadChange(list: string[]) {
|
||||||
|
state.formValue.images = unref(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
formRef,
|
||||||
|
uploadUrl,
|
||||||
|
rules,
|
||||||
|
formSubmit,
|
||||||
|
resetForm,
|
||||||
|
uploadChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -18,7 +18,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
return {
|
return {
|
||||||
goHome(){
|
goHome() {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,14 @@ export default defineComponent({
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 50px 0;
|
padding: 50px 0;
|
||||||
.text-center{
|
|
||||||
h1{ color: #666;padding: 20px 0}
|
.text-center {
|
||||||
|
h1 {
|
||||||
|
color: #666;
|
||||||
|
padding: 20px 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
return {
|
return {
|
||||||
goHome(){
|
goHome() {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,14 @@ export default defineComponent({
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 50px 0;
|
padding: 50px 0;
|
||||||
.text-center{
|
|
||||||
h1{ color: #666;padding: 20px 0}
|
.text-center {
|
||||||
|
h1 {
|
||||||
|
color: #666;
|
||||||
|
padding: 20px 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
return {
|
return {
|
||||||
goHome(){
|
goHome() {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,14 @@ export default defineComponent({
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 50px 0;
|
padding: 50px 0;
|
||||||
.text-center{
|
|
||||||
h1{ color: #666;padding: 20px 0}
|
.text-center {
|
||||||
|
h1 {
|
||||||
|
color: #666;
|
||||||
|
padding: 20px 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|||||||
@@ -55,6 +55,18 @@
|
|||||||
placeholder="请输入预约备注"
|
placeholder="请输入预约备注"
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
<n-form-item label="图片" path="img">
|
||||||
|
<BasicUpload
|
||||||
|
:action="`${uploadUrl}/v1.0/files`"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:data="{ type: 0 }"
|
||||||
|
name="files"
|
||||||
|
:width="100"
|
||||||
|
:height="100"
|
||||||
|
@uploadChange="uploadChange"
|
||||||
|
v-model:value="uploadList"
|
||||||
|
helpText="单个文件不超过20MB,最多只能上传10个文件"/>
|
||||||
|
</n-form-item>
|
||||||
<div style="margin-left:80px">
|
<div style="margin-left:80px">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button type="primary" @click="formSubmit">提交预约</n-button>
|
<n-button type="primary" @click="formSubmit">提交预约</n-button>
|
||||||
@@ -69,8 +81,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from 'vue'
|
import { defineComponent, ref, reactive, toRefs, } from 'vue'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { BasicUpload } from '@/components/Upload'
|
||||||
|
import { useGlobSetting } from '@/hooks/setting'
|
||||||
|
|
||||||
|
const globSetting = useGlobSetting()
|
||||||
|
|
||||||
const matterList = [
|
const matterList = [
|
||||||
{
|
{
|
||||||
@@ -102,23 +118,7 @@ const doctorList = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export default defineComponent({
|
const rules = {
|
||||||
setup() {
|
|
||||||
const formRef = ref(null)
|
|
||||||
const message = useMessage()
|
|
||||||
|
|
||||||
return {
|
|
||||||
formRef,
|
|
||||||
formValue: ref({
|
|
||||||
name: '',
|
|
||||||
mobile: '',
|
|
||||||
remark: '',
|
|
||||||
sex: 1,
|
|
||||||
matter: null,
|
|
||||||
doctor: null,
|
|
||||||
datetime: [],
|
|
||||||
}),
|
|
||||||
rules: {
|
|
||||||
name: {
|
name: {
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入预约姓名',
|
message: '请输入预约姓名',
|
||||||
@@ -146,10 +146,37 @@ export default defineComponent({
|
|||||||
message: '请选择预约医生',
|
message: '请选择预约医生',
|
||||||
trigger: 'change'
|
trigger: 'change'
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicUpload },
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
const { uploadUrl } = globSetting
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formValue: {
|
||||||
|
name: '',
|
||||||
|
mobile: '',
|
||||||
|
remark: '',
|
||||||
|
sex: 1,
|
||||||
|
matter: null,
|
||||||
|
doctor: null,
|
||||||
|
datetime: [],
|
||||||
},
|
},
|
||||||
doctorList,
|
//图片列表 通常查看和编辑使用 绝对路径 | 相对路径都可以
|
||||||
matterList,
|
uploadList: [
|
||||||
formSubmit(e) {
|
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
|
||||||
|
],
|
||||||
|
uploadHeaders: {
|
||||||
|
platform: "miniPrograms",
|
||||||
|
timestamp: new Date().getTime(),
|
||||||
|
token: 'Q6fFCuhc1vkKn5JNFWaCLf6gRAc5n0LQHd08dSnG4qo=',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function formSubmit() {
|
||||||
formRef.value.validate((errors) => {
|
formRef.value.validate((errors) => {
|
||||||
if (!errors) {
|
if (!errors) {
|
||||||
message.success('验证成功')
|
message.success('验证成功')
|
||||||
@@ -157,10 +184,26 @@ export default defineComponent({
|
|||||||
message.error('验证失败,请填写完整信息')
|
message.error('验证失败,请填写完整信息')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
resetForm() {
|
|
||||||
|
function resetForm() {
|
||||||
formRef.value.restoreValidation()
|
formRef.value.restoreValidation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function uploadChange(list: string[]) {
|
||||||
|
console.log(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
formRef,
|
||||||
|
uploadUrl,
|
||||||
|
rules,
|
||||||
|
doctorList,
|
||||||
|
matterList,
|
||||||
|
formSubmit,
|
||||||
|
resetForm,
|
||||||
|
uploadChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,22 +7,32 @@
|
|||||||
</div>
|
</div>
|
||||||
<n-card :bordered="false" title="基本信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
<n-card :bordered="false" title="基本信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
||||||
<n-descriptions label-placement="left" class="py-2">
|
<n-descriptions label-placement="left" class="py-2">
|
||||||
<n-descriptions-item><template #label>收款人姓名</template>啊俊</n-descriptions-item>
|
<n-descriptions-item>
|
||||||
|
<template #label>收款人姓名</template>
|
||||||
|
啊俊
|
||||||
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="收款账户">NaiveUiAdmin@qq.com</n-descriptions-item>
|
<n-descriptions-item label="收款账户">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||||
<n-descriptions-item label="付款类型">支付宝</n-descriptions-item>
|
<n-descriptions-item label="付款类型">支付宝</n-descriptions-item>
|
||||||
<n-descriptions-item label="付款账户">NaiveUiAdmin@163.com</n-descriptions-item>
|
<n-descriptions-item label="付款账户">NaiveUiAdmin@163.com</n-descriptions-item>
|
||||||
<n-descriptions-item label="转账金额">¥1980.00</n-descriptions-item>
|
<n-descriptions-item label="转账金额">¥1980.00</n-descriptions-item>
|
||||||
<n-descriptions-item label="状态"><n-tag type="success"> 已到账 </n-tag></n-descriptions-item>
|
<n-descriptions-item label="状态">
|
||||||
|
<n-tag type="success"> 已到账</n-tag>
|
||||||
|
</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card :bordered="false" title="其它信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
<n-card :bordered="false" title="其它信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
||||||
<n-descriptions label-placement="left" class="py-2">
|
<n-descriptions label-placement="left" class="py-2">
|
||||||
<n-descriptions-item><template #label>城市</template>深圳</n-descriptions-item>
|
<n-descriptions-item>
|
||||||
|
<template #label>城市</template>
|
||||||
|
深圳
|
||||||
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="性别">男</n-descriptions-item>
|
<n-descriptions-item label="性别">男</n-descriptions-item>
|
||||||
<n-descriptions-item label="邮箱">NaiveUiAdmin@qq.com</n-descriptions-item>
|
<n-descriptions-item label="邮箱">NaiveUiAdmin@qq.com</n-descriptions-item>
|
||||||
<n-descriptions-item label="地址">广东省深圳市南山区</n-descriptions-item>
|
<n-descriptions-item label="地址">广东省深圳市南山区</n-descriptions-item>
|
||||||
<n-descriptions-item label="生日">1991-06-04</n-descriptions-item>
|
<n-descriptions-item label="生日">1991-06-04</n-descriptions-item>
|
||||||
<n-descriptions-item label="认证"><n-tag type="success"> 已认证 </n-tag></n-descriptions-item>
|
<n-descriptions-item label="认证">
|
||||||
|
<n-tag type="success"> 已认证</n-tag>
|
||||||
|
</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card :bordered="false" title="表格信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
<n-card :bordered="false" title="表格信息" class="proCard mt-4" size="small" :segmented="{content: 'hard'}">
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const columns = [
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width:150,
|
width: 150,
|
||||||
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
//简单写一下例子,不建议这么写,过段时间,这里封二次封装
|
||||||
render() {
|
render() {
|
||||||
return [
|
return [
|
||||||
@@ -50,8 +50,8 @@ export const columns = [
|
|||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
size: 'small',
|
size: 'small',
|
||||||
type:'error',
|
type: 'error',
|
||||||
style:'margin-right:10px',
|
style: 'margin-right:10px',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-card :bordered="false" class="proCard">
|
<n-card :bordered="false" class="proCard">
|
||||||
<ProTable
|
<BasicTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="loadDataTable"
|
:request="loadDataTable"
|
||||||
:row-key="row => row.id"
|
:row-key="row => row.id"
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
<n-button type="primary" @click="reloadTable">刷新数据</n-button>
|
||||||
</template>
|
</template>
|
||||||
</ProTable>
|
</BasicTable>
|
||||||
|
|
||||||
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" title="新建">
|
<n-modal v-model:show="showModal" :show-icon="false" preset="dialog" title="新建">
|
||||||
<n-form
|
<n-form
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, toRefs, ref, h } from 'vue'
|
import { defineComponent, reactive, toRefs, ref, h } from 'vue'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
import { ProTable } from '@/components/ProTable'
|
import { BasicTable } from '@/components/Table'
|
||||||
import { getTableList } from '@/api/table/list'
|
import { getTableList } from '@/api/table/list'
|
||||||
import { columns } from './columns'
|
import { columns } from './columns'
|
||||||
import { PlusOutlined } from '@vicons/antd'
|
import { PlusOutlined } from '@vicons/antd'
|
||||||
@@ -82,7 +82,7 @@ const rules = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { ProTable, PlusOutlined },
|
components: { BasicTable, PlusOutlined },
|
||||||
setup() {
|
setup() {
|
||||||
const formRef: any = ref(null)
|
const formRef: any = ref(null)
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|||||||
@@ -28,6 +28,18 @@
|
|||||||
</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"
|
||||||
|
></mi-captcha>
|
||||||
|
</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">
|
||||||
@@ -80,6 +92,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
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'
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
username: string
|
username: string
|
||||||
@@ -96,12 +109,18 @@ export default defineComponent({
|
|||||||
autoLogin: true,
|
autoLogin: true,
|
||||||
formInline: {
|
formInline: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: '123456'
|
password: '123456',
|
||||||
|
isCaptcha: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
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();
|
||||||
|
|
||||||
@@ -135,15 +154,22 @@ export default defineComponent({
|
|||||||
message.info(msg || '登录失败')
|
message.info(msg || '登录失败')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message.error('请填写完整信息')
|
message.error('请填写完整信息,并且进行验证码校验')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAuthCode() {
|
||||||
|
state.formInline.isCaptcha = true
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(state),
|
...toRefs(state),
|
||||||
formRef,
|
formRef,
|
||||||
rules,
|
rules,
|
||||||
handleSubmit
|
logo,
|
||||||
|
handleSubmit,
|
||||||
|
onAuthCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -167,14 +193,9 @@ export default defineComponent({
|
|||||||
padding: 32px 0;
|
padding: 32px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&-logo {
|
|
||||||
height: 75px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-desc {
|
&-desc {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #808695;
|
color: #808695;
|
||||||
margin-top: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
<n-space align="center">
|
<n-space align="center">
|
||||||
<n-icon size="20" color="#f0a020">
|
<n-icon size="20" color="#f0a020">
|
||||||
<InfoCircleOutlined />
|
<InfoCircleOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<span>认证照片不够清晰</span>
|
<span>认证照片不够清晰</span>
|
||||||
<n-button type="info" text>立即修改</n-button>
|
<n-button type="info" text>立即修改</n-button>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-icon size="20" color="#f0a020">
|
<n-icon size="20" color="#f0a020">
|
||||||
<InfoCircleOutlined />
|
<InfoCircleOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<span>备注包含敏感字符,并且不能包含政治相关</span>
|
<span>备注包含敏感字符,并且不能包含政治相关</span>
|
||||||
<n-button type="info" text>立即修改</n-button>
|
<n-button type="info" text>立即修改</n-button>
|
||||||
@@ -42,7 +42,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { InfoCircleOutlined } from '@vicons/antd'
|
import { InfoCircleOutlined } from '@vicons/antd'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components:{ InfoCircleOutlined },
|
components: { InfoCircleOutlined },
|
||||||
setup() {
|
setup() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
return {
|
return {
|
||||||
@@ -59,6 +59,7 @@ export default defineComponent({
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
|
||||||
&-extra {
|
&-extra {
|
||||||
padding: 24px 40px;
|
padding: 24px 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
<n-space align="center">
|
<n-space align="center">
|
||||||
<n-icon size="20" color="#18a058">
|
<n-icon size="20" color="#18a058">
|
||||||
<CheckCircleOutlined />
|
<CheckCircleOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<span>转入支付宝账户(189****5426):¥1980元</span>
|
<span>转入支付宝账户(189****5426):¥1980元</span>
|
||||||
<n-button type="info" text>立即撤回</n-button>
|
<n-button type="info" text>立即撤回</n-button>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<p class="mt-3">
|
<p class="mt-3">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-icon size="20" color="#18a058">
|
<n-icon size="20" color="#18a058">
|
||||||
<CheckCircleOutlined />
|
<CheckCircleOutlined/>
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<span>转入支付宝账户(187****5426):¥2980元</span>
|
<span>转入支付宝账户(187****5426):¥2980元</span>
|
||||||
<n-button type="info" text>立即撤回</n-button>
|
<n-button type="info" text>立即撤回</n-button>
|
||||||
@@ -42,7 +42,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { CheckCircleOutlined } from '@vicons/antd'
|
import { CheckCircleOutlined } from '@vicons/antd'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components:{ CheckCircleOutlined },
|
components: { CheckCircleOutlined },
|
||||||
setup() {
|
setup() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
return {
|
return {
|
||||||
@@ -59,6 +59,7 @@ export default defineComponent({
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
|
||||||
&-extra {
|
&-extra {
|
||||||
padding: 24px 40px;
|
padding: 24px 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export default defineComponent({
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
|
||||||
&-extra {
|
&-extra {
|
||||||
padding: 24px 40px;
|
padding: 24px 40px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
88
src/views/setting/account/account.vue
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n-grid :x-gap="24">
|
||||||
|
<n-grid-item span="6">
|
||||||
|
<n-card :bordered="false" size="small" class="proCard">
|
||||||
|
<n-thing
|
||||||
|
class="thing-cell"
|
||||||
|
v-for="item in typeTabList" :key="item.key"
|
||||||
|
:class="{'thing-cell-on':type===item.key}"
|
||||||
|
@click="switchType(item)"
|
||||||
|
>
|
||||||
|
<template #header>{{ item.name }}</template>
|
||||||
|
<template #description>{{ item.desc }}</template>
|
||||||
|
</n-thing>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item span="18">
|
||||||
|
<n-card :bordered="false" size="small" :title="typeTitle" class="proCard">
|
||||||
|
<BasicSetting v-if="type === 1"></BasicSetting>
|
||||||
|
<SafetySetting v-if="type === 2"></SafetySetting>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs } from 'vue'
|
||||||
|
import BasicSetting from "./BasicSetting.vue"
|
||||||
|
import SafetySetting from "./SafetySetting.vue"
|
||||||
|
|
||||||
|
|
||||||
|
const typeTabList = [
|
||||||
|
{
|
||||||
|
name: '基本设置',
|
||||||
|
desc: '个人账户信息设置',
|
||||||
|
key: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '安全设置',
|
||||||
|
desc: '密码,邮箱等设置',
|
||||||
|
key: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicSetting, SafetySetting },
|
||||||
|
setup() {
|
||||||
|
const state = reactive({
|
||||||
|
type: 1,
|
||||||
|
typeTitle: '基本设置'
|
||||||
|
})
|
||||||
|
|
||||||
|
function switchType(e) {
|
||||||
|
state.type = e.key
|
||||||
|
state.typeTitle = e.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
switchType,
|
||||||
|
typeTabList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.thing-cell {
|
||||||
|
margin: 0 -16px 10px;
|
||||||
|
padding: 5px 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f3f3f3;
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thing-cell-on {
|
||||||
|
background: #f0faff;
|
||||||
|
color: #2d8cf0;
|
||||||
|
|
||||||
|
::v-deep(.n-thing-main .n-thing-header .n-thing-header__title) {
|
||||||
|
color: #2d8cf0
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0faff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
138
src/views/setting/system/BasicSetting.vue
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form
|
||||||
|
:label-width="80"
|
||||||
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
>
|
||||||
|
<n-form-item label="网站名称" path="name">
|
||||||
|
<n-input v-model:value="formValue.name" placeholder="请输入网站名称"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="备案编号" path="icpCode">
|
||||||
|
<n-input placeholder="请输入备案编号" v-model:value="formValue.icpCode"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
|
||||||
|
<n-form-item label="联系电话" path="mobile">
|
||||||
|
<n-input placeholder="请输入联系电话" v-model:value="formValue.mobile"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="联系地址" path="address">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formValue.address"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入联系地址"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="登录验证码" path="loginCode">
|
||||||
|
<n-radio-group v-model:value="formValue.loginCode" name="loginCode">
|
||||||
|
<n-space>
|
||||||
|
<n-radio :value="1">开启</n-radio>
|
||||||
|
<n-radio :value="0">关闭</n-radio>
|
||||||
|
</n-space>
|
||||||
|
</n-radio-group>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="网站开启访问" path="systemOpen">
|
||||||
|
<n-switch size="large" v-model:value="formValue.systemOpen" @update:value="systemOpenChange"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="网站关闭提示" path="closeText">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formValue.closeText"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入网站关闭提示"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" @click="formSubmit">更新基本信息</n-button>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, ref, toRefs } from 'vue'
|
||||||
|
import { useDialog, useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入网站名称',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入联系电话',
|
||||||
|
trigger: 'input'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const formRef = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formValue: {
|
||||||
|
name: '',
|
||||||
|
mobile: '',
|
||||||
|
icpCode: '',
|
||||||
|
address: '',
|
||||||
|
loginCode: 0,
|
||||||
|
closeText: '网站维护中,暂时无法访问!本网站正在进行系统维护和技术升级,网站暂时无法访问,敬请谅解!',
|
||||||
|
systemOpen: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function systemOpenChange(value) {
|
||||||
|
if (!value) {
|
||||||
|
dialog.warning({
|
||||||
|
title: '提示',
|
||||||
|
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!',
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
message.success('操作成功')
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {
|
||||||
|
state.formValue.systemOpen = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formSubmit(e) {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('验证成功')
|
||||||
|
} else {
|
||||||
|
message.error('验证失败,请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
formRef.value.restoreValidation()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
formRef,
|
||||||
|
...toRefs(state),
|
||||||
|
rules,
|
||||||
|
formSubmit,
|
||||||
|
resetForm,
|
||||||
|
systemOpenChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
85
src/views/setting/system/EmailSetting.vue
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form
|
||||||
|
:label-width="120"
|
||||||
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
>
|
||||||
|
|
||||||
|
<n-form-item label="发件人邮箱" path="originator">
|
||||||
|
<n-input v-model:value="formValue.originator" placeholder="请输入发件人邮箱"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="SMTP服务器地址">
|
||||||
|
<n-input placeholder="请输入SMTP服务器地址"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="SMTP服务器端口">
|
||||||
|
<n-input placeholder="请输入SMTP服务器端口"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="SMTP用户名">
|
||||||
|
<n-input placeholder="请输入SMTP用户名"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="SMTP密码">
|
||||||
|
<n-input type="password" placeholder="请输入SMTP密码"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="邮件测试">
|
||||||
|
<n-button>邮件测试</n-button>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" @click="formSubmit">更新邮件信息</n-button>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, ref, toRefs } from 'vue'
|
||||||
|
import { useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
originator: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入发件人邮箱',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formValue: {
|
||||||
|
originator: '',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function formSubmit() {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('验证成功')
|
||||||
|
} else {
|
||||||
|
message.error('验证失败,请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
formRef,
|
||||||
|
...toRefs(state),
|
||||||
|
rules,
|
||||||
|
formSubmit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
200
src/views/setting/system/RevealSetting.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<n-grid cols="2 s:2 m:2 l:3 xl:3 2xl:3" responsive="screen">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form
|
||||||
|
:label-width="120"
|
||||||
|
:model="formValue"
|
||||||
|
:rules="rules"
|
||||||
|
ref="formRef"
|
||||||
|
>
|
||||||
|
<n-form-item label="商品图片(大)">
|
||||||
|
<n-space align="center">
|
||||||
|
<span>宽度:</span>
|
||||||
|
<n-input v-model:value="formValue.bigWidth" style="width:80px" placeholder="宽度像素"/>
|
||||||
|
<span>高度:</span>
|
||||||
|
<n-input v-model:value="formValue.bigHeight" style="width:80px" placeholder="高度像素"/>
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="商品图片(小)">
|
||||||
|
<n-space align="center">
|
||||||
|
<span>宽度:</span>
|
||||||
|
<n-input v-model:value="formValue.smallWidth" style="width:80px" placeholder="宽度像素"/>
|
||||||
|
<span>高度:</span>
|
||||||
|
<n-input v-model:value="formValue.smallHeight" style="width:80px" placeholder="高度像素"/>
|
||||||
|
</n-space>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="水印透明度" path="watermarkClarity">
|
||||||
|
<n-input-number v-model:value="formValue.watermarkClarity" :show-button="false" placeholder="请输入水印透明度"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="水印图片" path="watermarkClarity">
|
||||||
|
<n-upload action="http://www.mocky.io/v2/5e4bafc63100007100d8b70f">
|
||||||
|
<n-button>上传文件</n-button>
|
||||||
|
</n-upload>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="水印位置" path="watermarkPlace">
|
||||||
|
<n-select placeholder="请选择价格精确方式"
|
||||||
|
:options="watermarkPlaceList"
|
||||||
|
v-model:value="formValue.watermarkPlace"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="价格精确位数" path="pricePreciseNum">
|
||||||
|
<n-select placeholder="请选择价格精确位数"
|
||||||
|
:options="pricePreciseNumList"
|
||||||
|
v-model:value="formValue.pricePreciseNum"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="价格精确方式" path="pricePrecise">
|
||||||
|
<n-select placeholder="请选择价格精确方式"
|
||||||
|
:options="pricePreciseList"
|
||||||
|
v-model:value="formValue.pricePrecise"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<n-form-item label="前台显示市场价" path="isMarketPrice">
|
||||||
|
<n-switch size="large" v-model:value="formValue.isMarketPrice"/>
|
||||||
|
</n-form-item>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<n-space>
|
||||||
|
<n-button type="primary" @click="formSubmit">更新显示信息</n-button>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</n-form>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, ref, toRefs } from 'vue'
|
||||||
|
import { useDialog, useMessage } from 'naive-ui'
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入网站名称',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
required: true,
|
||||||
|
message: '请输入联系电话',
|
||||||
|
trigger: 'input'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const watermarkPlaceList = [
|
||||||
|
{
|
||||||
|
label: '左上',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '右上',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '居中',
|
||||||
|
value: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '右下',
|
||||||
|
value: 4
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const pricePreciseNumList = [
|
||||||
|
{
|
||||||
|
label: '2位',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '3位',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '4位',
|
||||||
|
value: 3
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const pricePreciseList = [
|
||||||
|
{
|
||||||
|
label: '四舍五入',
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '向上取整',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '向下取整',
|
||||||
|
value: 3
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const formRef: any = ref(null)
|
||||||
|
const message = useMessage()
|
||||||
|
const dialog = useDialog()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
formValue: {
|
||||||
|
bigWidth: '',
|
||||||
|
bigHeight: '',
|
||||||
|
smallWidth: '',
|
||||||
|
smallHeight: '',
|
||||||
|
watermarkClarity: null,
|
||||||
|
pricePrecise: 1,
|
||||||
|
isMarketPrice: true,
|
||||||
|
pricePreciseNum: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function systemOpenChange(value) {
|
||||||
|
if (!value) {
|
||||||
|
dialog.warning({
|
||||||
|
title: '提示',
|
||||||
|
content: '您确定要关闭系统访问吗?该操作立马生效,请慎重操作!',
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
message.success('操作成功')
|
||||||
|
},
|
||||||
|
onNegativeClick: () => {
|
||||||
|
state.formValue.systemOpen = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formSubmit(e) {
|
||||||
|
formRef.value.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
message.success('验证成功')
|
||||||
|
} else {
|
||||||
|
message.error('验证失败,请填写完整信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
formRef.value.restoreValidation()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
formRef,
|
||||||
|
...toRefs(state),
|
||||||
|
pricePreciseList,
|
||||||
|
watermarkPlaceList,
|
||||||
|
pricePreciseNumList,
|
||||||
|
rules,
|
||||||
|
formSubmit,
|
||||||
|
resetForm,
|
||||||
|
systemOpenChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
96
src/views/setting/system/system.vue
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n-grid :x-gap="24">
|
||||||
|
<n-grid-item span="6">
|
||||||
|
<n-card :bordered="false" size="small" class="proCard">
|
||||||
|
<n-thing
|
||||||
|
class="thing-cell"
|
||||||
|
v-for="item in typeTabList" :key="item.key"
|
||||||
|
:class="{'thing-cell-on':type===item.key}"
|
||||||
|
@click="switchType(item)"
|
||||||
|
>
|
||||||
|
<template #header>{{ item.name }}</template>
|
||||||
|
<template #description>{{ item.desc }}</template>
|
||||||
|
</n-thing>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item span="18">
|
||||||
|
<n-card :bordered="false" size="small" :title="typeTitle" class="proCard">
|
||||||
|
<BasicSetting v-if="type === 1"></BasicSetting>
|
||||||
|
<RevealSetting v-if="type === 2"></RevealSetting>
|
||||||
|
<EmailSetting v-if="type === 3"></EmailSetting>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, reactive, toRefs } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import BasicSetting from "./BasicSetting.vue"
|
||||||
|
import RevealSetting from "./RevealSetting.vue"
|
||||||
|
import EmailSetting from "./EmailSetting.vue"
|
||||||
|
|
||||||
|
const typeTabList = [
|
||||||
|
{
|
||||||
|
name: '基本设置',
|
||||||
|
desc: '系统常规设置',
|
||||||
|
key: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '显示设置',
|
||||||
|
desc: '系统显示设置',
|
||||||
|
key: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '邮件设置',
|
||||||
|
desc: '系统邮件设置',
|
||||||
|
key: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BasicSetting, RevealSetting, EmailSetting },
|
||||||
|
setup() {
|
||||||
|
const router = useRouter()
|
||||||
|
const state = reactive({
|
||||||
|
type: 1,
|
||||||
|
typeTitle: '基本设置'
|
||||||
|
})
|
||||||
|
|
||||||
|
function switchType(e) {
|
||||||
|
state.type = e.key
|
||||||
|
state.typeTitle = e.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
switchType,
|
||||||
|
typeTabList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.thing-cell {
|
||||||
|
margin: 0 -16px 10px;
|
||||||
|
padding: 5px 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f3f3f3;
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thing-cell-on {
|
||||||
|
background: #f0faff;
|
||||||
|
color: #2d8cf0;
|
||||||
|
|
||||||
|
::v-deep(.n-thing-main .n-thing-header .n-thing-header__title) {
|
||||||
|
color: #2d8cf0
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0faff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -31,9 +31,8 @@
|
|||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": ["src/*"],
|
||||||
"src/*"
|
"/#/*": ["types/*"]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
51
types/config.d.ts
vendored
@@ -1,10 +1,45 @@
|
|||||||
export interface ProjectSettingState {
|
export interface ProjectSettingState {
|
||||||
navMode: string,//导航模式
|
//导航模式
|
||||||
navTheme: string,//导航风格
|
navMode: string;
|
||||||
headerSetting: object,//顶部设置
|
//导航风格
|
||||||
showFooter: boolean, //页脚
|
navTheme: string;
|
||||||
menuSetting: object, //多标签
|
//顶部设置
|
||||||
multiTabsSetting: object,//多标签
|
headerSetting: object;
|
||||||
crumbsSetting: object,//面包屑
|
//页脚
|
||||||
permissionMode: string//权限模式
|
showFooter: boolean;
|
||||||
|
//菜单设置
|
||||||
|
menuSetting: object;
|
||||||
|
//多标签
|
||||||
|
multiTabsSetting: object;
|
||||||
|
//面包屑
|
||||||
|
crumbsSetting: object;
|
||||||
|
//权限模式
|
||||||
|
permissionMode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlobConfig {
|
||||||
|
title: string;
|
||||||
|
apiUrl: string;
|
||||||
|
shortName: string;
|
||||||
|
urlPrefix?: string;
|
||||||
|
uploadUrl?: string;
|
||||||
|
prodMock: boolean;
|
||||||
|
imgUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlobEnvConfig {
|
||||||
|
// 标题
|
||||||
|
VITE_GLOB_APP_TITLE: string;
|
||||||
|
// 接口地址
|
||||||
|
VITE_GLOB_API_URL: string;
|
||||||
|
// 接口前缀
|
||||||
|
VITE_GLOB_API_URL_PREFIX?: string;
|
||||||
|
// Project abbreviation
|
||||||
|
VITE_GLOB_APP_SHORT_NAME: string;
|
||||||
|
// 图片上传地址
|
||||||
|
VITE_GLOB_UPLOAD_URL?: string;
|
||||||
|
//图片前缀地址
|
||||||
|
VITE_GLOB_IMG_URL?: string;
|
||||||
|
//生产环境开启mock
|
||||||
|
VITE_GLOB_PROD_MOCK: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
2
types/global.d.ts
vendored
@@ -64,6 +64,8 @@ declare global {
|
|||||||
VITE_GLOB_APP_TITLE: string;
|
VITE_GLOB_APP_TITLE: string;
|
||||||
VITE_GLOB_APP_SHORT_NAME: string;
|
VITE_GLOB_APP_SHORT_NAME: string;
|
||||||
VITE_DROP_CONSOLE: boolean;
|
VITE_DROP_CONSOLE: boolean;
|
||||||
|
VITE_GLOB_PROD_MOCK: boolean;
|
||||||
|
VITE_GLOB_IMG_URL: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function parseInt(s: string | number, radix?: number): number;
|
declare function parseInt(s: string | number, radix?: number): number;
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
|||||||
const root = process.cwd();
|
const root = process.cwd();
|
||||||
const env = loadEnv(mode, root);
|
const env = loadEnv(mode, root);
|
||||||
const viteEnv = wrapperEnv(env);
|
const viteEnv = wrapperEnv(env);
|
||||||
const { VITE_BASE_URL, VITE_DROP_CONSOLE, VITE_PORT, VITE_GLOB_PROD_MOCK } = viteEnv
|
const { VITE_PUBLIC_PATH, VITE_DROP_CONSOLE, VITE_PORT, VITE_GLOB_PROD_MOCK } = viteEnv
|
||||||
const prodMock = VITE_GLOB_PROD_MOCK
|
const prodMock = VITE_GLOB_PROD_MOCK
|
||||||
const isBuild = command === 'build';
|
const isBuild = command === 'build';
|
||||||
return {
|
return {
|
||||||
base: VITE_BASE_URL,
|
base: VITE_PUBLIC_PATH,
|
||||||
esbuild: {},
|
esbuild: {},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: [
|
||||||
|
|||||||
133
yarn.lock
@@ -2,6 +2,29 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@ant-design/colors@^5.0.0":
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-5.1.1.tgz?cache=0&sync_timestamp=1612935637470&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Fcolors%2Fdownload%2F%40ant-design%2Fcolors-5.1.1.tgz#800b2186b1e27e66432e67d03ed96af3e21d8940"
|
||||||
|
integrity sha1-gAshhrHifmZDLmfQPtlq8+IdiUA=
|
||||||
|
dependencies:
|
||||||
|
"@ctrl/tinycolor" "^3.3.1"
|
||||||
|
|
||||||
|
"@ant-design/icons-svg@^4.0.0":
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.nlark.com/@ant-design/icons-svg/download/@ant-design/icons-svg-4.1.0.tgz#480b025f4b20ef7fe8f47d4a4846e4fee84ea06c"
|
||||||
|
integrity sha1-SAsCX0sg73/o9H1KSEbk/uhOoGw=
|
||||||
|
|
||||||
|
"@ant-design/icons-vue@^5.1.9":
|
||||||
|
version "5.1.9"
|
||||||
|
resolved "https://registry.npm.taobao.org/@ant-design/icons-vue/download/@ant-design/icons-vue-5.1.9.tgz#8d741a3290be61af7c71618c308cc1a946c4e434"
|
||||||
|
integrity sha1-jXQaMpC+Ya98cWGMMIzBqUbE5DQ=
|
||||||
|
dependencies:
|
||||||
|
"@ant-design/colors" "^5.0.0"
|
||||||
|
"@ant-design/icons-svg" "^4.0.0"
|
||||||
|
"@babel/runtime" "^7.10.4"
|
||||||
|
"@types/lodash" "^4.14.165"
|
||||||
|
lodash "^4.17.15"
|
||||||
|
|
||||||
"@babel/code-frame@7.12.11":
|
"@babel/code-frame@7.12.11":
|
||||||
version "7.12.11"
|
version "7.12.11"
|
||||||
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||||
@@ -230,6 +253,13 @@
|
|||||||
"@babel/helper-plugin-utils" "^7.14.5"
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
"@babel/plugin-syntax-typescript" "^7.14.5"
|
"@babel/plugin-syntax-typescript" "^7.14.5"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.10.4":
|
||||||
|
version "7.14.6"
|
||||||
|
resolved "https://registry.nlark.com/@babel/runtime/download/@babel/runtime-7.14.6.tgz?cache=0&sync_timestamp=1623708247115&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
|
||||||
|
integrity sha1-U1IDvAiS78fexgvcJ7Ls9uQJBi0=
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.0.0", "@babel/template@^7.14.5":
|
"@babel/template@^7.0.0", "@babel/template@^7.14.5":
|
||||||
version "7.14.5"
|
version "7.14.5"
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
|
resolved "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
|
||||||
@@ -411,6 +441,11 @@
|
|||||||
resolved "https://registry.nlark.com/@css-render/vue3-ssr/download/@css-render/vue3-ssr-0.15.4.tgz#ae29bc76f7eb40830d0ecd8086ce3726a1c6e8e3"
|
resolved "https://registry.nlark.com/@css-render/vue3-ssr/download/@css-render/vue3-ssr-0.15.4.tgz#ae29bc76f7eb40830d0ecd8086ce3726a1c6e8e3"
|
||||||
integrity sha1-rim8dvfrQIMNDs2Ahs43JqHG6OM=
|
integrity sha1-rim8dvfrQIMNDs2Ahs43JqHG6OM=
|
||||||
|
|
||||||
|
"@ctrl/tinycolor@^3.3.1":
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.nlark.com/@ctrl/tinycolor/download/@ctrl/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f"
|
||||||
|
integrity sha1-w8WuVDyJfKqcKmhjC+01W+X5mQ8=
|
||||||
|
|
||||||
"@emotion/hash@~0.8.0":
|
"@emotion/hash@~0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.npm.taobao.org/@emotion/hash/download/@emotion/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
resolved "https://registry.npm.taobao.org/@emotion/hash/download/@emotion/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||||
@@ -563,6 +598,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
|
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6"
|
||||||
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
|
integrity sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.165":
|
||||||
|
version "4.14.171"
|
||||||
|
resolved "https://registry.nlark.com/@types/lodash/download/@types/lodash-4.14.171.tgz?cache=0&sync_timestamp=1625609589008&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Flodash%2Fdownload%2F%40types%2Flodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
|
||||||
|
integrity sha1-8Bs6X+NJnjS2IsNipGpgn9sjVzs=
|
||||||
|
|
||||||
"@types/mdast@^3.0.0":
|
"@types/mdast@^3.0.0":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
|
resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
|
||||||
@@ -778,6 +818,17 @@
|
|||||||
estree-walker "^2.0.1"
|
estree-walker "^2.0.1"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@vue/compiler-core@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/compiler-core/download/@vue/compiler-core-3.1.5.tgz#298f905b6065d6d81ff63756f98c60876b393c87"
|
||||||
|
integrity sha1-KY+QW2Bl1tgf9jdW+Yxgh2s5PIc=
|
||||||
|
dependencies:
|
||||||
|
"@babel/parser" "^7.12.0"
|
||||||
|
"@babel/types" "^7.12.0"
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
estree-walker "^2.0.1"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
"@vue/compiler-dom@3.1.1":
|
"@vue/compiler-dom@3.1.1":
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz#ef60d856ac2ede5b2ad5c72a7a68122895e3d652"
|
resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.1.1.tgz#ef60d856ac2ede5b2ad5c72a7a68122895e3d652"
|
||||||
@@ -794,6 +845,14 @@
|
|||||||
"@vue/compiler-core" "3.1.2"
|
"@vue/compiler-core" "3.1.2"
|
||||||
"@vue/shared" "3.1.2"
|
"@vue/shared" "3.1.2"
|
||||||
|
|
||||||
|
"@vue/compiler-dom@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/compiler-dom/download/@vue/compiler-dom-3.1.5.tgz#cbb97020c62a5faa3fbc2a97916bd98041ac9856"
|
||||||
|
integrity sha1-y7lwIMYqX6o/vCqXkWvZgEGsmFY=
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-core" "3.1.5"
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.1.1":
|
"@vue/compiler-sfc@3.1.1":
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz#d4e4507c013d0b219f0b106b317ec5bb1cde3398"
|
resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.1.1.tgz#d4e4507c013d0b219f0b106b317ec5bb1cde3398"
|
||||||
@@ -843,6 +902,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@vue/shared" "3.1.2"
|
"@vue/shared" "3.1.2"
|
||||||
|
|
||||||
|
"@vue/reactivity@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/reactivity/download/@vue/reactivity-3.1.5.tgz#dbec4d9557f7c8f25c2635db1e23a78a729eb991"
|
||||||
|
integrity sha1-2+xNlVf3yPJcJjXbHiOninKeuZE=
|
||||||
|
dependencies:
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
|
||||||
"@vue/runtime-core@3.1.2":
|
"@vue/runtime-core@3.1.2":
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.nlark.com/@vue/runtime-core/download/@vue/runtime-core-3.1.2.tgz#f4dbc503cfc9a02ab5f1ebe002c3322512064a54"
|
resolved "https://registry.nlark.com/@vue/runtime-core/download/@vue/runtime-core-3.1.2.tgz#f4dbc503cfc9a02ab5f1ebe002c3322512064a54"
|
||||||
@@ -851,6 +917,14 @@
|
|||||||
"@vue/reactivity" "3.1.2"
|
"@vue/reactivity" "3.1.2"
|
||||||
"@vue/shared" "3.1.2"
|
"@vue/shared" "3.1.2"
|
||||||
|
|
||||||
|
"@vue/runtime-core@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/runtime-core/download/@vue/runtime-core-3.1.5.tgz#a545b7f146092929cb5e833e85439150f17ac87b"
|
||||||
|
integrity sha1-pUW38UYJKSnLXoM+hUORUPF6yHs=
|
||||||
|
dependencies:
|
||||||
|
"@vue/reactivity" "3.1.5"
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
|
||||||
"@vue/runtime-dom@3.1.2":
|
"@vue/runtime-dom@3.1.2":
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.nlark.com/@vue/runtime-dom/download/@vue/runtime-dom-3.1.2.tgz#0fd8724f14bc7ba64b6c954d874a8d8a4fcb5fe9"
|
resolved "https://registry.nlark.com/@vue/runtime-dom/download/@vue/runtime-dom-3.1.2.tgz#0fd8724f14bc7ba64b6c954d874a8d8a4fcb5fe9"
|
||||||
@@ -860,6 +934,15 @@
|
|||||||
"@vue/shared" "3.1.2"
|
"@vue/shared" "3.1.2"
|
||||||
csstype "^2.6.8"
|
csstype "^2.6.8"
|
||||||
|
|
||||||
|
"@vue/runtime-dom@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/runtime-dom/download/@vue/runtime-dom-3.1.5.tgz#4fa28947d408aa368fa17ea0edc1beb9af1472a1"
|
||||||
|
integrity sha1-T6KJR9QIqjaPoX6g7cG+ua8UcqE=
|
||||||
|
dependencies:
|
||||||
|
"@vue/runtime-core" "3.1.5"
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
csstype "^2.6.8"
|
||||||
|
|
||||||
"@vue/shared@3.1.1":
|
"@vue/shared@3.1.1":
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c"
|
resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c"
|
||||||
@@ -870,6 +953,11 @@
|
|||||||
resolved "https://registry.nlark.com/@vue/shared/download/@vue/shared-3.1.2.tgz#1069c0bc7d6f4bd15ccf3a5f3be29450aca368f9"
|
resolved "https://registry.nlark.com/@vue/shared/download/@vue/shared-3.1.2.tgz#1069c0bc7d6f4bd15ccf3a5f3be29450aca368f9"
|
||||||
integrity sha1-EGnAvH1vS9FczzpfO+KUUKyjaPk=
|
integrity sha1-EGnAvH1vS9FczzpfO+KUUKyjaPk=
|
||||||
|
|
||||||
|
"@vue/shared@3.1.5":
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/@vue/shared/download/@vue/shared-3.1.5.tgz#74ee3aad995d0a3996a6bb9533d4d280514ede03"
|
||||||
|
integrity sha1-dO46rZldCjmWpruVM9TSgFFO3gM=
|
||||||
|
|
||||||
"@vueuse/core@^5.0.3":
|
"@vueuse/core@^5.0.3":
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
resolved "https://registry.nlark.com/@vueuse/core/download/@vueuse/core-5.0.3.tgz#8f3170e2a51ae62fb1725c84d4cc02a7552aad0b"
|
resolved "https://registry.nlark.com/@vueuse/core/download/@vueuse/core-5.0.3.tgz#8f3170e2a51ae62fb1725c84d4cc02a7552aad0b"
|
||||||
@@ -2961,6 +3049,11 @@ is-plain-obj@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||||
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
||||||
|
|
||||||
|
is-plain-object@3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.nlark.com/is-plain-object/download/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
|
||||||
|
integrity sha1-Zi2S0kwKpDAkB7DUXSHyJRyF+Fs=
|
||||||
|
|
||||||
is-plain-object@5.0.0:
|
is-plain-object@5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.nlark.com/is-plain-object/download/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
resolved "https://registry.nlark.com/is-plain-object/download/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||||
@@ -3351,6 +3444,25 @@ make-dir@^3.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
semver "^6.0.0"
|
semver "^6.0.0"
|
||||||
|
|
||||||
|
makeit-captcha@^1.2.5:
|
||||||
|
version "1.2.5"
|
||||||
|
resolved "https://registry.nlark.com/makeit-captcha/download/makeit-captcha-1.2.5.tgz#f5055edbff35d5e1a0dccede7953f24f6aabc20c"
|
||||||
|
integrity sha1-9QVe2/811eGg3M7eeVPyT2qrwgw=
|
||||||
|
dependencies:
|
||||||
|
"@ant-design/icons-vue" "^5.1.9"
|
||||||
|
axios "^0.21.1"
|
||||||
|
makeit-tooltip "^1.1.2"
|
||||||
|
vue "^3.0.4"
|
||||||
|
vue-types "^3.0.1"
|
||||||
|
|
||||||
|
makeit-tooltip@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.nlark.com/makeit-tooltip/download/makeit-tooltip-1.1.2.tgz#b265f8586c2b1ef9bab4e6d141cbaccb8eba73bc"
|
||||||
|
integrity sha1-smX4WGwrHvm6tObRQcusy466c7w=
|
||||||
|
dependencies:
|
||||||
|
vue "^3.0.4"
|
||||||
|
vue-types "^3.0.1"
|
||||||
|
|
||||||
map-obj@^1.0.0:
|
map-obj@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
|
resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
|
||||||
@@ -4274,6 +4386,11 @@ reduce-css-calc@^2.1.8:
|
|||||||
css-unit-converter "^1.1.1"
|
css-unit-converter "^1.1.1"
|
||||||
postcss-value-parser "^3.3.0"
|
postcss-value-parser "^3.3.0"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.4:
|
||||||
|
version "0.13.7"
|
||||||
|
resolved "https://registry.nlark.com/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
||||||
|
integrity sha1-ysLazIoepnX+qrrriugziYrkb1U=
|
||||||
|
|
||||||
regexpp@^3.1.0:
|
regexpp@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
|
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
|
||||||
@@ -5272,6 +5389,13 @@ vue-router@^4.0.10:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@vue/devtools-api" "^6.0.0-beta.14"
|
"@vue/devtools-api" "^6.0.0-beta.14"
|
||||||
|
|
||||||
|
vue-types@^3.0.1:
|
||||||
|
version "3.0.2"
|
||||||
|
resolved "https://registry.nlark.com/vue-types/download/vue-types-3.0.2.tgz#ec16e05d412c038262fc1efa4ceb9647e7fb601d"
|
||||||
|
integrity sha1-7BbgXUEsA4Ji/B76TOuWR+f7YB0=
|
||||||
|
dependencies:
|
||||||
|
is-plain-object "3.0.1"
|
||||||
|
|
||||||
vue-types@^4.0.0:
|
vue-types@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.nlark.com/vue-types/download/vue-types-4.0.0.tgz#da13ccca0f979d3cfd076ce3a2b7050a9627ed5a"
|
resolved "https://registry.nlark.com/vue-types/download/vue-types-4.0.0.tgz#da13ccca0f979d3cfd076ce3a2b7050a9627ed5a"
|
||||||
@@ -5279,6 +5403,15 @@ vue-types@^4.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-plain-object "5.0.0"
|
is-plain-object "5.0.0"
|
||||||
|
|
||||||
|
vue@^3.0.4:
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.nlark.com/vue/download/vue-3.1.5.tgz#12879b11d0685ee4478c8869551799630a52f9fe"
|
||||||
|
integrity sha1-EoebEdBoXuRHjIhpVReZYwpS+f4=
|
||||||
|
dependencies:
|
||||||
|
"@vue/compiler-dom" "3.1.5"
|
||||||
|
"@vue/runtime-dom" "3.1.5"
|
||||||
|
"@vue/shared" "3.1.5"
|
||||||
|
|
||||||
vue@^3.1.2:
|
vue@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.nlark.com/vue/download/vue-3.1.2.tgz#647f8e3949a3d600771dca25d50225dc3e594c64"
|
resolved "https://registry.nlark.com/vue/download/vue-3.1.2.tgz#647f8e3949a3d600771dca25d50225dc3e594c64"
|
||||||
|
|||||||