提交 46e08753 编写于 作者: V vben

chore: add some notes

上级 815250ed
......@@ -138,6 +138,7 @@
// "vetur.format.defaultFormatter.html": "prettyhtml",
"vetur.format.defaultFormatter.ts": "prettier-tslint",
"vetur.format.defaultFormatter.js": "prettier",
"vetur.languageFeatures.codeActions": false,
// "vetur.useWorkspaceDependencies": true,
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
......
......@@ -11,7 +11,8 @@
### 🎫 Chores
- 更新 antdv 到`2.0.0-rc.1`
- 更新 antdv 到`2.0.0-rc.1`(暂时还原到 beta15,rc1 菜单卡顿太严重.)
- 添加部分注释
### 🐛 Bug Fixes
......
// 修改自
// Modified from
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
// TODO 目前还不能监听文件新增及删除 内容已经改变,缓存问题?
// TODO Currently, it is not possible to monitor file addition and deletion. The content has been changed, the cache problem?
import { join } from 'path';
import { lstatSync } from 'fs';
import glob from 'glob';
......
......@@ -4,11 +4,11 @@ import { resultSuccess } from '../_util';
const userInfo = {
name: 'Vben',
userid: '00000001',
email: 'antdesign@alipay.com',
email: 'test@gmail.com',
signature: '海纳百川,有容乃大',
introduction: '微笑着,努力着,欣赏着',
title: '交互专家',
group: '蚂蚁集团11-某某某事业群-某某平台部-某某技术部-UED',
group: '某某某事业群-某某平台部-某某技术部-UED',
tags: [
{
key: '0',
......@@ -38,7 +38,7 @@ const userInfo = {
notifyCount: 12,
unreadCount: 11,
country: 'China',
address: '厦门市 77 号',
address: 'Xiamen City 77',
phone: '0592-268888888',
};
......
......@@ -22,8 +22,8 @@
},
"dependencies": {
"@iconify/iconify": "^2.0.0-rc.2",
"@vueuse/core": "^4.0.0-beta.41",
"ant-design-vue": "^2.0.0-rc.1",
"@vueuse/core": "4.0.0-beta.41",
"ant-design-vue": "2.0.0-beta.15",
"apexcharts": "3.22.0",
"axios": "^0.21.0",
"echarts": "^4.9.0",
......@@ -35,7 +35,7 @@
"qrcode": "^1.4.4",
"vditor": "^3.6.2",
"vue": "^3.0.2",
"vue-i18n": "^9.0.0-beta.6",
"vue-i18n": "^9.0.0-beta.7",
"vue-router": "^4.0.0-rc.3",
"vuex": "^4.0.0-rc.1",
"vuex-module-decorators": "^1.0.1",
......@@ -45,10 +45,10 @@
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@iconify/json": "^1.1.258",
"@iconify/json": "^1.1.260",
"@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.4.1",
"@types/echarts": "^4.9.0",
"@types/echarts": "^4.9.1",
"@types/fs-extra": "^9.0.4",
"@types/koa-static": "^4.0.1",
"@types/lodash-es": "^4.17.3",
......@@ -56,10 +56,10 @@
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.3.5",
"@types/rollup-plugin-visualizer": "^2.6.0",
"@types/yargs": "^15.0.9",
"@types/yargs": "^15.0.10",
"@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.6.1",
"@typescript-eslint/parser": "^4.6.1",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
"@vue/compiler-sfc": "^3.0.2",
"@vuedx/typecheck": "^0.2.4-0",
"@vuedx/typescript-plugin-vue": "^0.2.4-0",
......@@ -84,7 +84,7 @@
"prettier": "^2.1.2",
"rimraf": "^3.0.2",
"rollup-plugin-visualizer": "^4.1.2",
"stylelint": "^13.7.2",
"stylelint": "^13.8.0",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0",
"stylelint-order": "^4.1.0",
......
/*
* @Author: Vben
* @Description:
*/
module.exports = {
printWidth: 100,
tabWidth: 2,
......
......@@ -33,20 +33,17 @@
</style>
<circle cx="97" cy="97" r="81" stroke-width="16" stroke="#327fd8" fill="none"></circle>
<g class="load">
<!--右半圆环-->
<linearGradient id="left-linear" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="100" y2="180">
<stop offset="0" style="stop-color: #64acff;" />
<stop offset="1" style="stop-color: #9DBFE4;" />
</linearGradient>
<path class="left-linear" d="M20,100c0-44.1,35.9-80,80-80V0C44.8,0,0,44.8,0,100s44.8,100,100,100v-20C55.9,180,20,144.1,20,100z" />
<!--左半圆环-->
<circle class="bottom" cx="100" cy="190" r="10" />
<linearGradient id="right-linear" gradientUnits="userSpaceOnUse" x1="100" y1="120" x2="100" y2="180">
<stop offset="0" style="stop-color: transparent;" />
<stop offset="1" style="stop-color: transparent;" />
</linearGradient>
<path class="right-linear" d="M100,0v20c44.1,0,80,35.9,80,80c0,44.1-35.9,80-80,80v20c55.2,0,100-44.8,100-100S155.2,0,100,0z" />
<!--左半圆环-->
<circle class="top" cx="100" cy="10" r="10" />
</g>
</svg>
<template>
<ConfigProvider :locale="zhCN" :transform-cell-text="transformCellText" v-bind="lockOn">
<ConfigProvider v-bind="lockEvent" :locale="zhCN" :transform-cell-text="transformCellText">
<router-view />
</ConfigProvider>
</template>
......@@ -13,9 +13,8 @@
import moment from 'moment';
import 'moment/dist/locale/zh-cn';
import { useConfigProvider, useInitAppConfigStore } from './useApp';
import { getConfigProvider, initAppConfigStore } from '/@/setup/Application';
import { useLockPage } from '/@/hooks/web/useLockPage';
import { useSetting } from '/@/hooks/core/useSetting';
moment.locale('zh-cn');
......@@ -23,26 +22,22 @@
name: 'App',
components: { ConfigProvider },
setup() {
// Initialize application settings
useInitAppConfigStore();
// Initialize breakpoint monitoring
// Initialize vuex internal system configuration
initAppConfigStore();
// Create a global breakpoint monitor
createBreakpointListen();
// Get system configuration
const { projectSetting } = useSetting();
// Get ConfigProvider configuration
const { transformCellText } = useConfigProvider();
const { transformCellText } = getConfigProvider();
let lockOn = {};
if (projectSetting.lockTime) {
// Monitor the mouse or keyboard time, used to recalculate the lock screen time
const { on } = useLockPage();
lockOn = on;
}
// Create a lock screen monitor
const lockEvent = useLockPage();
return {
transformCellText,
zhCN,
lockOn,
lockEvent,
};
},
});
......
......@@ -5,7 +5,7 @@ enum Api {
ACCOUNT_INFO = '/account/getAccountInfo',
}
// 获取个人中心--基础设置内容
// Get personal center-basic settings
export function accountInfoApi() {
return defHttp.request<GetAccountInfoModel>({
url: Api.ACCOUNT_INFO,
......
......@@ -7,7 +7,7 @@ enum Api {
}
/**
* @description: 根据id获取用户菜单
* @description: Get user menu based on id
*/
export function getMenuListById(params: getMenuListByIdParams) {
return defHttp.request<getMenuListByIdParamsResultModel>({
......
......@@ -11,13 +11,13 @@ export interface RouteItem {
}
/**
* @description: 获取菜单接口
* @description: Get menu interface
*/
export interface getMenuListByIdParams {
id: number | string;
}
/**
* @description: 获取菜单返回值
* @description: Get menu return value
*/
export type getMenuListByIdParamsResultModel = RouteItem[];
......@@ -7,7 +7,7 @@ enum Api {
}
/**
* @description: 上传接口
* @description: Upload interface
*/
export function uploadApi(
params: UploadFileParams,
......
import type { App } from 'vue';
import Authority from './src/index.vue';
export default Authority;
export default (app: App): void => {
app.component(Authority.name, Authority);
};
export { Authority };
<!--
* @Author: Vben
* @Description:Access control component for fine-grained access control.
-->
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, computed, unref } from 'vue';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission';
import { appStore } from '/@/store/modules/app';
import { getSlot } from '/@/utils/helper/tsxHelper';
export default defineComponent({
name: 'Authority',
props: {
// 指定角色可见
/**
* Specified role is visible
* When the permission mode is the role mode, the value value can pass the role value.
* When the permission mode is background, the value value can pass the code permission value
* @default ''
*/
value: {
type: [Number, Array, String] as PropType<RoleEnum | RoleEnum[] | string | string[]>,
default: '',
......@@ -23,7 +34,7 @@
});
/**
* 渲染角色按钮
* Render role button
*/
function renderRoleAuth() {
const { value } = props;
......@@ -34,10 +45,8 @@
return hasPermission(value) ? getSlot(slots) : null;
}
/**
* 渲染编码按钮
* 这里只判断是否包含,具体实现可以根据项目自行写逻辑
*/
// Render coding button
// Here only judge whether it is included, the specific implementation can be written according to the project logic
function renderCodeAuth() {
const { value } = props;
if (!value) {
......@@ -49,12 +58,12 @@
return () => {
const mode = unref(getModeRef);
// 基于角色渲染
// Role-based value control
if (mode === PermissionModeEnum.ROLE) {
return renderRoleAuth();
}
// 基于后台编码渲染
// Based on background role permission control
if (mode === PermissionModeEnum.BACK) {
return renderCodeAuth();
}
......
<!--
* @Author: Vben
* @Description: Arrow component with animation
-->
<template>
<span :class="getClass">
<RightOutlined />
......
......@@ -71,9 +71,6 @@
return props.absolute ? props.position : {};
});
/**
* @description: 渲染内容
*/
const renderTitle = () => {
const list = props.text;
if (isString(list)) {
......@@ -89,6 +86,7 @@
return () => {
return h(
// @ts-ignores
Tooltip,
{
title: h(
......
......@@ -12,33 +12,19 @@
import { defineComponent, computed } from 'vue';
import { Button } from 'ant-design-vue';
// import { extendSlots } from '/@/utils/helper/tsxHelper';
// import { useThrottle } from '/@/hooks/core/useThrottle';
// import { isFunction } from '/@/utils/is';
import Icon from '/@/components/Icon';
export default defineComponent({
name: 'AButton',
inheritAttrs: false,
components: { Button, Icon },
props: {
// 按钮类型
type: {
type: String as PropType<'primary' | 'default' | 'danger' | 'dashed' | 'link'>,
default: 'default',
},
// 节流防抖类型 throttle debounce
// throttle: {
// type: String as PropType<'throttle' | 'debounce'>,
// default: 'throttle',
// },
color: {
type: String as PropType<'error' | 'warning' | 'success' | ''>,
},
// // 防抖节流时间
// throttleTime: {
// type: Number as PropType<number>,
// default: 50,
// },
loading: {
type: Boolean as PropType<boolean>,
default: false,
......@@ -58,42 +44,15 @@
const getIsCircleBtn = computed(() => {
return attrs.shape === 'circle';
});
// const getListeners = computed(() => {
// const { throttle, throttleTime = 0 } = props;
// // 是否开启节流防抖
// const throttleType = throttle!.toLowerCase();
// const isDebounce = throttleType === 'debounce';
// const openThrottle = ['throttle', 'debounce'].includes(throttleType) && throttleTime > 0;
// if (!openThrottle) {
// return {
// ...attrs,
// };
// }
// const on: {
// onClick?: Fn;
// } = {};
// if (attrs.onClick && isFunction(attrs.onClick) && openThrottle) {
// const [handler] = useThrottle(attrs.onClick as any, throttleTime!, {
// debounce: isDebounce,
// immediate: false,
// });
// on.onClick = handler;
// }
// return {
// ...attrs,
// ...on,
// };
// });
const getColor = computed(() => {
const res: string[] = [];
const { color, disabled } = props;
color && res.push(`ant-btn-${color}`);
disabled && res.push('is-disabled');
return res;
return [
{
[`ant-btn-${color}`]: !!color,
[`is-disabled`]: disabled,
},
];
});
const getBindValue = computed((): any => {
......
......@@ -22,7 +22,8 @@
import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue';
import { Skeleton } from 'ant-design-vue';
import { useTimeoutFn, useIntersectionObserver } from '@vueuse/core';
import { useTimeoutFn } from '@vueuse/core';
import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
interface State {
isInit: boolean;
loading: boolean;
......
......@@ -43,23 +43,22 @@
},
name: 'CollapseContainer',
props: {
// 标题
title: {
type: String as PropType<string>,
default: '',
},
// 是否可以展开
// Can it be expanded
canExpan: {
type: Boolean as PropType<boolean>,
default: true,
},
// 标题右侧温馨提醒
// Warm reminder on the right side of the title
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
},
// 展开收缩的时候是否触发window.resize,
// 可以适应表格和表单,当表单收缩起来,表格触发resize 自适应高度
// Whether to trigger window.resize when expanding and contracting,
// Can adapt to tables and forms, when the form shrinks, the form triggers resize to adapt to the height
triggerWindowResize: {
type: Boolean as PropType<boolean>,
default: false,
......@@ -68,12 +67,12 @@
type: Boolean as PropType<boolean>,
default: false,
},
// 延时加载
// Delayed loading
lazy: {
type: Boolean as PropType<boolean>,
default: false,
},
// 延时加载时间
// Delayed loading time
lazyTime: {
type: Number as PropType<number>,
default: 0,
......@@ -82,14 +81,14 @@
setup(props) {
const showRef = ref(true);
/**
* @description: 处理开展事件
* @description: Handling development events
*/
function handleExpand() {
const hasShow = !unref(showRef);
showRef.value = hasShow;
if (props.triggerWindowResize) {
// 这里200毫秒是因为展开有动画,
// 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200);
}
}
......
......@@ -9,25 +9,23 @@ export const props = {
type: Object as PropType<Event>,
default: null,
},
// 样式
styles: {
type: Object as PropType<any>,
default: null,
},
showIcon: {
// 是否显示icon
type: Boolean as PropType<boolean>,
default: true,
},
axis: {
// 鼠标右键点击的位置
// The position of the right mouse button click
type: Object as PropType<Axis>,
default() {
return { x: 0, y: 0 };
},
},
items: {
// 最重要的列表,没有的话直接不显示
// The most important list, if not, will not be displayed
type: Array as PropType<ContextMenuItem[]>,
default() {
return [];
......
// 对vue-count-to进行改造成支持vue3版本
// Transform vue-count-to to support vue3 version
export { default as CountTo } from './src/index.vue';
......@@ -14,9 +14,8 @@ export default defineComponent({
props: descProps,
emits: ['register'],
setup(props, { attrs, slots, emit }) {
// props来自设置
const propsRef = ref<Partial<DescOptions> | null>(null);
// 自定义title组件:获得title
// Custom title component: get title
const getMergeProps = computed(() => {
return {
...props,
......@@ -34,19 +33,19 @@ export default defineComponent({
});
/**
* @description: 是否使用标题
* @description: Whether to use title
*/
const useWrapper = computed(() => {
return !!unref(getMergeProps).title;
});
/**
* @description: 获取配置Collapse
* @description: Get configuration Collapse
*/
const getCollapseOptions = computed(
(): CollapseContainerOptions => {
return {
// 默认不能展开
// Cannot be expanded by default
canExpand: false,
...unref(getProps).collapseOptions,
};
......@@ -57,7 +56,7 @@ export default defineComponent({
* @description:设置desc
*/
function setDescProps(descProps: Partial<DescOptions>): void {
// 保留上一次的setDrawerProps
// Keep the last setDrawerProps
const mergeProps = deepMerge(unref(propsRef) || {}, descProps);
propsRef.value = cloneDeep(mergeProps);
}
......@@ -68,7 +67,7 @@ export default defineComponent({
emit('register', methods);
// 防止换行
// Prevent line breaks
function renderLabel({ label, labelMinWidth, labelStyle }: DescItem) {
if (!labelStyle && !labelMinWidth) {
return label;
......@@ -101,7 +100,6 @@ export default defineComponent({
const width = contentMinWidth;
return (
// @ts-ignore
<Descriptions.Item label={renderLabel(item)} key={field} span={span}>
{() =>
contentMinWidth ? (
......@@ -131,7 +129,7 @@ export default defineComponent({
const renderContainer = () => {
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>;
// 减少dom层级
// Reduce the dom level
return props.useCollapse ? (
<CollapseContainer
title={unref(getMergeProps).title}
......
......@@ -2,7 +2,6 @@ import type { VNode } from 'vue';
import type { CollapseContainerOptions } from '/@/components/Container/index';
export interface DescItem {
// 最小宽度
labelMinWidth?: number;
contentMinWidth?: number;
......@@ -11,7 +10,7 @@ export interface DescItem {
field: string;
label: any;
// 和并列
// Merge column
span?: number;
show?: (...arg: any) => boolean;
// render
......@@ -19,10 +18,10 @@ export interface DescItem {
}
export interface DescOptions {
// 是否包含collapse组件
// Whether to include the collapse component
useCollapse?: boolean;
/**
* item配置
* item configuration
* @type DescItem
*/
schema: DescItem[];
......@@ -32,7 +31,7 @@ export interface DescOptions {
*/
data: any;
/**
* 内置的CollapseContainer组件配置
* Built-in CollapseContainer component configuration
* @type CollapseContainerOptions
*/
collapseOptions?: CollapseContainerOptions;
......
......@@ -71,7 +71,7 @@ export default defineComponent({
}
);
// 底部按钮自定义实现,
// Custom implementation of the bottom button,
const getFooterHeight = computed(() => {
const { footerHeight, showFooter }: DrawerProps = unref(getProps);
if (showFooter && footerHeight) {
......@@ -80,7 +80,7 @@ export default defineComponent({
return `0px`;
});
// 取消事件
// Cancel event
async function onClose(e: any) {
const { closeFunc } = unref(getProps);
emit('close', e);
......@@ -93,7 +93,7 @@ export default defineComponent({
}
function setDrawerProps(props: Partial<DrawerProps>): void {
// 保留上一次的setDrawerProps
// Keep the last setDrawerProps
propsRef.value = deepMerge(unref(propsRef) || {}, props);
if (Reflect.has(props, 'visible')) {
visibleRef.value = !!props.visible;
......
import type { PropType } from 'vue';
// import {DrawerProps} from './types'
export const footerProps = {
confirmLoading: Boolean as PropType<boolean>,
/**
* @description: 显示关闭按钮
* @description: Show close button
*/
showCancelBtn: {
type: Boolean as PropType<boolean>,
......@@ -15,7 +14,7 @@ export const footerProps = {
default: '关闭',
},
/**
* @description: 显示确认按钮
* @description: Show confirmation button
*/
showOkBtn: {
type: Boolean as PropType<boolean>,
......
......@@ -73,7 +73,7 @@ export interface DrawerProps extends DrawerFooterProps {
showDetailBack?: boolean;
visible?: boolean;
/**
* 内置的ScrollContainer组件配置
* Built-in ScrollContainer component configuration
* @type ScrollContainerOptions
*/
scrollOptions?: ScrollContainerOptions;
......
......@@ -22,7 +22,7 @@ import { isFunction } from '/@/utils/is';
const dataTransferRef = reactive<any>({});
/**
* @description: 适用于将drawer独立出去,外面调用
* @description: Applicable to separate drawer and call outside
*/
export function useDrawer(): UseDrawerReturnType {
if (!getCurrentInstance()) {
......
import type { PropType } from 'vue';
/**
* @description: 基础表格参数配置
*/
export const dropdownProps = {
/**
* the trigger mode which executes the drop-down action
......@@ -14,52 +12,6 @@ export const dropdownProps = {
return ['contextmenu'];
},
},
// /**
// * the dropdown menu
// * @type () => Menu
// */
// overlay: {
// type: null,
// },
// /**
// * Class name of the dropdown root element
// * @type string
// */
// overlayClassName: String,
// /**
// * Style of the dropdown root element
// * @type object
// */
// overlayStyle: Object,
// /**
// * whether the dropdown menu is visible
// * @type boolean
// */
// visible: Boolean,
// /**
// * whether the dropdown menu is disabled
// * @type boolean
// */
// disabled: Boolean,
// /**
// * to set the ontainer of the dropdown menu. The default is to create a div element in body, you can reset it to the scrolling area and make a relative reposition.
// * @default () => document.body
// * @type Function
// */
// getPopupContainer: Function,
// /**
// * placement of pop menu: bottomLeft bottomCenter bottomRight topLeft topCenter topRight
// * @default 'bottomLeft'
// * @type string
// */
// placement: String,
};
export const basicDropdownProps = Object.assign({}, dropdownProps, {
dropMenuList: {
......
......@@ -27,7 +27,7 @@
<script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks';
import type { Ref } from 'vue';
import type { Ref, WatchStopHandle } from 'vue';
import type { ValidateFields } from 'ant-design-vue/lib/form/interface';
import { defineComponent, reactive, ref, computed, unref, toRef, onMounted, watch } from 'vue';
......@@ -66,6 +66,7 @@
});
const defaultValueRef = ref<any>({});
const isInitedDefaultRef = ref(false);
const propsRef = ref<Partial<FormProps>>({});
const schemaRef = ref<Nullable<FormSchema[]>>(null);
const formElRef = ref<Nullable<FormActionType>>(null);
......@@ -164,16 +165,19 @@
}
);
watch(
const stopWatch: WatchStopHandle = watch(
() => getSchema.value,
() => {
initDefault();
(schema) => {
if (unref(isInitedDefaultRef)) {
return stopWatch();
}
if (schema && schema.length) {
initDefault();
isInitedDefaultRef.value = true;
}
}
);
/**
* @description:设置表单
*/
function setProps(formProps: Partial<FormProps>): void {
const mergeProps = deepMerge(unref(propsRef) || {}, formProps);
propsRef.value = mergeProps;
......
import { Component } from 'vue';
/**
* 组件列表,在这里注册才可以在表单使用
* Component list, register here to use it in the form
*/
import {
Input,
......
......@@ -114,7 +114,7 @@ export default function ({
) {
advanceState.hideAdvanceBtn = false;
// 大于3行默认收起
// More than 3 lines collapsed by default
} else if (!advanceState.isLoad) {
advanceState.isLoad = true;
advanceState.isAdvanced = !advanceState.isAdvanced;
......@@ -124,7 +124,7 @@ export default function ({
if (itemColSum > BASIC_COL_LEN) {
return { isAdvanced: advanceState.isAdvanced, itemColSum };
} else {
// 第一行始终显示
// The first line is always displayed
return { isAdvanced: true, itemColSum };
}
}
......
......@@ -62,7 +62,7 @@ export function useFormAction({
Object.keys(values).forEach((key) => {
const element = values[key];
if (element !== undefined && element !== null && fields.includes(key)) {
// 时间
// time type
if (itemIsDateType(key)) {
if (Array.isArray(element)) {
const arr: any[] = [];
......@@ -84,7 +84,7 @@ export function useFormAction({
// }
}
/**
* @description: 根据字段名删除
* @description: Delete based on field name
*/
function removeSchemaByFiled(fields: string | string[]): void {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
......@@ -102,7 +102,7 @@ export function useFormAction({
}
/**
* @description: 根据字段名删除
* @description: Delete based on field name
*/
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void {
if (isString(field)) {
......@@ -114,7 +114,7 @@ export function useFormAction({
}
/**
* @description: 往某个字段后面插入,如果没有插入最后一个
* @description: Insert after a certain field, if not insert the last
*/
function appendSchemaByField(schema: FormSchema, prefixField?: string) {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
......@@ -169,7 +169,7 @@ export function useFormAction({
}
/**
* @description: 是否是时间
* @description: Is it time
*/
function itemIsDateType(key: string) {
return unref(getSchema).some((item) => {
......@@ -193,7 +193,7 @@ export function useFormAction({
}
/**
* @description: 表单提交
* @description: Form submission
*/
async function handleSubmit(e?: Event): Promise<void> {
e && e.preventDefault();
......
......@@ -18,7 +18,7 @@ export function useFormValues({
getSchema,
formModel,
}: UseFormValuesContext) {
// 处理表单值
// Processing form values
function handleFormValues(values: any) {
if (!isObject(values)) {
return {};
......@@ -37,7 +37,7 @@ export function useFormValues({
if (isArray(value) && value[0]._isAMomentObject && value[1]._isAMomentObject) {
value = value.map((item) => transformDateFunc(item));
}
// 去除空格
// Remove spaces
if (isString(value)) {
value = value.trim();
}
......@@ -47,7 +47,7 @@ export function useFormValues({
}
/**
* @description: 处理时间区间参数
* @description: Processing time interval parameters
*/
function handleRangeTimeValue(values: any) {
const fieldMapToTime = unref(fieldMapToTimeRef);
......
......@@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
wrapperCol: globWrapperCol,
} = unref(propsRef) as any;
// 如果全局有设置labelWidth, 则所有item使用
// If labelWidth is set globally, all items use
if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
return { labelCol, wrapperCol };
}
......
......@@ -42,57 +42,57 @@ export type UseFormReturnType = [RegisterFn, FormActionType];
export interface FormProps {
// layout?: 'vertical' | 'inline' | 'horizontal';
// 表单值
// Form value
model?: any;
// 整个表单所有项宽度
// The width of all items in the entire form
labelWidth?: number | string;
// 重置时提交
// Submit form on reset
submitOnReset?: boolean;
// 整个表单通用Col配置
// Col configuration for the entire form
labelCol?: Partial<ColEx>;
// 整个表单通用Col配置
// Col configuration for the entire form
wrapperCol?: Partial<ColEx>;
// 通用col配置
// General col configuration
baseColProps?: Partial<ColEx>;
// 表单配置规则
// Form configuration rules
schemas?: FormSchema[];
// 用于合并到动态控制表单项的 函数values
// Function values used to merge into dynamic control form items
mergeDynamicData?: any;
// 紧凑模式,用于搜索表单
// Compact mode for search forms
compact?: boolean;
// 空白行span
// Blank line span
emptySpan?: number | Partial<ColEx>;
// 表单内部组件大小
// Internal component size of the form
size?: 'default' | 'small' | 'large';
// 是否禁用
// Whether to disable
disabled?: boolean;
// 时间区间字段映射成多个
// Time interval fields are mapped into multiple
fieldMapToTime?: FieldMapToTime;
// 自动设置placeholder
// Placeholder is set automatically
autoSetPlaceHolder?: boolean;
// 校验信息是否加入label
// Check whether the information is added to the label
rulesMessageJoinLabel?: boolean;
// 是否显示收起展开按钮
// Whether to show collapse and expand buttons
showAdvancedButton?: boolean;
// 超过指定行数自动收起
// Automatically collapse over the specified number of rows
autoAdvancedLine?: number;
// 是否显示操作按钮
// Whether to show the operation button
showActionButtonGroup?: boolean;
// 重置按钮配置
// Reset button configuration
resetButtonOptions?: Partial<ButtonProps>;
// 确认按钮配置
// Confirm button configuration
submitButtonOptions?: Partial<ButtonProps>;
// 操作列配置
// Operation column configuration
actionColOptions?: Partial<ColEx>;
// 显示重置按钮
// Show reset button
showResetButton?: boolean;
// 显示确认按钮
// Show confirmation button
showSubmitButton?: boolean;
resetFunc?: () => Promise<void>;
......@@ -101,27 +101,27 @@ export interface FormProps {
colon?: boolean;
}
export interface FormSchema {
// 字段名
// Field name
field: string;
// 内部值更改触发的事件名,默认 change
// Event name triggered by internal value change, default change
changeEvent?: string;
// v-model绑定的变量名 默认 value
// Variable name bound to v-model Default value
valueField?: string;
// 标签名
// Label name
label: string;
// 辅助文本
// Auxiliary text
subLabel?: string;
// 文本右侧帮助文本
// Help text on the right side of the text
helpMessage?: string | string[];
// BaseHelp组件props
// BaseHelp component props
helpComponentProps?: Partial<HelpComponentProps>;
// label宽度,有传的话 itemProps配置的 labelCol 和WrapperCol会失效
// Label width, if it is passed, the labelCol and WrapperCol configured by itemProps will be invalid
labelWidth?: string | number;
// 禁用调有formModel全局设置的labelWidth,自己手动设置 labelCol和wrapperCol
// Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself
disabledLabelWidth?: boolean;
// 组件
// render component
component: ComponentType;
// 组件参数
// Component parameters
componentProps?:
| ((opt: {
schema: FormSchema;
......@@ -130,35 +130,35 @@ export interface FormSchema {
formModel: any;
}) => any)
| object;
// 必填
// Required
required?: boolean;
// 校验规则
// Validation rules
rules?: Rule[];
// 校验信息是否加入label
// Check whether the information is added to the label
rulesMessageJoinLabel?: boolean;
// 参考formModelItem
// Reference formModelItem
itemProps?: Partial<FormItem>;
// formModelItem外层的col配置
// col configuration outside formModelItem
colProps?: Partial<ColEx>;
// 默认值
defaultValue?: any;
isAdvanced?: boolean;
// 配合详情组件
// Matching details components
span?: number;
ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
// 渲染form-item标签内的内容
// Render the content in the form-item tag
render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
// 渲染 col内容,需要外层包裹 form-item
// Rendering col content requires outer wrapper form-item
renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;
renderComponentContent?:
......@@ -167,10 +167,10 @@ export interface FormSchema {
| VNode[]
| string;
// 自定义slot, 在 from-item内
// Custom slot, in from-item
slot?: string;
// 自定义slot,类似renderColContent
// Custom slot, similar to renderColContent
colSlot?: string;
dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);
......@@ -179,16 +179,16 @@ export interface FormSchema {
}
export interface HelpComponentProps {
maxWidth: string;
// 是否显示序号
// Whether to display the serial number
showIndex: boolean;
// 文本列表
// Text list
text: any;
// 颜色
// colour
color: string;
// 字体大小
// font size
fontSize: string;
icon: string;
absolute: boolean;
// 定位
// Positioning
position: any;
}
......@@ -6,7 +6,8 @@ import { Menu } from 'ant-design-vue';
import SearchInput from './SearchInput.vue';
import MenuContent from './MenuContent';
import { MenuModeEnum, MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum';
import { menuStore } from '/@/store/modules/menu';
import { appStore } from '/@/store/modules/app';
......@@ -254,7 +255,7 @@ export default defineComponent({
{getSlot(slots, 'header')}
<SearchInput
class={!props.search ? 'hidden' : ''}
theme={props.theme as MenuThemeEnum}
theme={props.theme as ThemeEnum}
onChange={handleInputChange}
onClick={handleInputClick}
collapsed={getCollapsedState}
......
......@@ -11,7 +11,7 @@
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, computed } from 'vue';
import { MenuThemeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum';
// hook
import { useDebounce } from '/@/hooks/core/useDebounce';
......@@ -25,7 +25,7 @@
default: true,
},
theme: {
type: String as PropType<MenuThemeEnum>,
type: String as PropType<ThemeEnum>,
},
},
setup(props, { emit }) {
......
import type { Menu } from '/@/router/types';
import type { PropType } from 'vue';
import { MenuModeEnum, MenuTypeEnum, MenuThemeEnum } from '/@/enums/menuEnum';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum';
export const basicProps = {
items: {
type: Array as PropType<Menu[]>,
......@@ -40,7 +41,7 @@ export const basicProps = {
},
theme: {
type: String as PropType<string>,
default: MenuThemeEnum.DARK,
default: ThemeEnum.DARK,
},
showLogo: {
type: Boolean as PropType<boolean>,
......
import { ThemeEnum } from '/@/enums/appEnum';
export interface MenuState {
// 默认选中的列表
defaultSelectedKeys: string[];
......@@ -6,7 +7,7 @@ export interface MenuState {
mode: MenuModeEnum;
// 主题
theme: ComputedRef<MenuThemeEnum> | MenuThemeEnum;
theme: ComputedRef<ThemeEnum> | ThemeEnum;
// 缩进
inlineIndent?: number;
......
......@@ -208,8 +208,8 @@
padding: 16px;
.ant-form {
padding: 12px 12px 4px 12px;
margin-bottom: 12px;
padding: 20px 20px 4px 12px;
margin-bottom: 18px;
background: #fff;
border-radius: 2px;
}
......
......@@ -33,7 +33,7 @@ import {
Result,
Empty,
} from 'ant-design-vue';
import { getApp } from '/@/useApp';
import { getApp } from '/@/setup/Application';
const compList = [Icon, Button, AntButton.Group, AppFooter];
......
......@@ -16,4 +16,4 @@
@page-loading-z-index: 10000;
// left-menu
@app-menu-item-height: 46px;
@app-menu-item-height: 44px;
......@@ -15,6 +15,13 @@ export enum ThemeModeEnum {
SEMI_DARK = 'semi-dark-mode',
}
// menu theme enum
export enum ThemeEnum {
DARK = 'dark',
LIGHT = 'light',
}
/**
* 权限模式
*/
......
export const MENU_DRAG_STATE = 'MENU_DRAG_STATE';
......@@ -10,13 +10,6 @@ export enum MenuTypeEnum {
TOP_MENU = 'top-menu',
}
// menu theme enum
export enum MenuThemeEnum {
DARK = 'dark',
LIGHT = 'light',
}
// 折叠触发器位置
export enum TriggerEnum {
// 不显示
......
export enum PaginationEnum {
// Default number of pages
DEFAULT_PAGE_SIZE = 20,
}
export enum ResultEnum {
SUCCESS = 0,
ERROR = 1,
TIMEOUT = 401,
TYPE = 'success',
}
import { nextTick, onMounted, onActivated } from 'vue';
export function onMountedOrActivated(hook: Fn) {
let mounted: boolean;
onMounted(() => {
hook();
nextTick(() => {
mounted = true;
});
});
onActivated(() => {
if (mounted) {
hook();
}
});
}
import { getCurrentInstance } from 'vue';
// expose public api
export function useExpose(apis: Record<string, any>) {
const instance = getCurrentInstance();
if (instance) {
Object.assign(instance.proxy, apis);
}
}
import { toRef, Ref, reactive, customRef, SetupContext, watch, UnwrapRef } from 'vue';
export type ModelProps<U> = Readonly<
{ [props: string]: any } & {
modelValue?: U;
}
>;
export function useModel<T>(
props: ModelProps<T>,
context: SetupContext,
callback?: (val: T | undefined, internalState: { value: UnwrapRef<T | undefined> }) => any
) {
const outerModel: Ref<T | undefined> = toRef(props, 'modelValue');
const internalState = reactive({
value: props.modelValue,
});
const internalModel = customRef<UnwrapRef<T> | undefined>((track, trigger) => {
return {
get() {
track();
return internalState.value;
},
set(newVal) {
if (internalState.value === newVal) return;
internalState.value = newVal;
context.emit('update:modelValue', newVal);
trigger();
},
};
});
watch(outerModel, (val, oldVal) => {
if (val === oldVal || val === internalState.value) return;
if (callback) {
callback(val, internalState);
return;
}
internalState.value = val as UnwrapRef<T> | undefined;
});
return {
internalState,
internalModel,
};
}
import { ref, onBeforeUpdate } from 'vue';
export function useRefs() {
const refs = ref([] as Element[]);
onBeforeUpdate(() => {
refs.value = [];
});
const setRefs = (index: number) => (el: Element) => {
refs.value[index] = el;
};
return [refs, setRefs];
}
......@@ -27,7 +27,7 @@ export function throttle<T extends unknown[]>(
}
let { immediate = false } = options;
const { once = false, debounce = false } = options;
let timeoutId: ReturnType<typeof setTimeout> | undefined;
let timeoutId: Nullable<TimeoutHandle>;
// Has it been cancelled
let cancelled: boolean | null = false;
/**
......@@ -36,7 +36,7 @@ export function throttle<T extends unknown[]>(
function clearTimer() {
if (timeoutId) {
window.clearTimeout(timeoutId);
timeoutId = undefined;
timeoutId = null;
}
}
/** cancel exec */
......@@ -63,7 +63,7 @@ export function throttle<T extends unknown[]>(
const callNow = !timeoutId;
if (callNow) {
exec();
timeoutId = undefined;
timeoutId = null;
}
} else {
debounce && clearTimer();
......
import { ref, watch, Ref, SetupContext } from 'vue';
export function useToggle(internalModel: Ref<unknown>, { emit }: SetupContext) {
const isActive = ref(!!internalModel.value);
const isToggled = ref(false);
watch(internalModel, (val) => {
isActive.value = !!val;
});
watch(isActive, (value) => {
!!value !== !!internalModel.value && emit('onUpdate:modelValue', value);
});
function toggleIt() {
isToggled.value = !isToggled.value;
}
return {
isActive,
toggleIt,
isToggled,
};
}
import { Ref, watchEffect, ref } from 'vue';
interface IntersectionObserverProps {
target: Ref<Element | null | undefined>;
root?: Ref<Element | null | undefined>;
onIntersect: IntersectionObserverCallback;
rootMargin?: string;
threshold?: number;
}
export function useIntersectionObserver({
target,
root,
onIntersect,
rootMargin = '0px',
threshold = 0.1,
}: IntersectionObserverProps) {
let cleanup = () => {};
const observer: Ref<Nullable<IntersectionObserver>> = ref(null);
const stopEffect = watchEffect(() => {
cleanup();
observer.value = new IntersectionObserver(onIntersect, {
root: root ? root.value : null,
rootMargin,
threshold,
});
const current = target.value;
current && observer.value.observe(current);
cleanup = () => {
if (observer.value) {
observer.value.disconnect();
target.value && observer.value.unobserve(target.value);
}
};
});
return {
observer,
stop: () => {
cleanup();
stopEffect();
},
};
}
import { ref, Ref, unref } from 'vue';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { isServer } from '/@/utils/is';
export function useClickOutside<T extends HTMLElement>(
containerRef: Ref<T>,
onClickOutside: (e: MouseEvent | TouchEvent) => void
onClickOutside: (e: MouseEvent | TouchEvent) => void,
eventName = 'click'
) {
if (isServer) return;
const isTouchRef = ref(false);
useEventListener({
el: document,
name: 'touchend',
......@@ -13,7 +18,7 @@ export function useClickOutside<T extends HTMLElement>(
});
useEventListener({
el: document,
name: 'click',
name: eventName,
listener: handler,
options: true,
});
......
import { Ref, ref, onMounted, nextTick } from 'vue';
import { useRect } from '/@/hooks/web/useRect';
export const useHeight = (element: Element | Ref<Element>) => {
const height = ref();
onMounted(() => {
nextTick(() => {
height.value = useRect(element).height;
});
});
return height;
};
import { onUnmounted, watchEffect } from 'vue';
import { computed, onUnmounted, watchEffect } from 'vue';
import { useThrottle } from '/@/hooks/core/useThrottle';
import { appStore } from '/@/store/modules/app';
......@@ -7,10 +7,11 @@ import { userStore } from '/@/store/modules/user';
export function useLockPage() {
let timeId: ReturnType<typeof setTimeout>;
function clear() {
function clear(): void {
window.clearTimeout(timeId);
}
function resetCalcLockTimeout() {
function resetCalcLockTimeout(): void {
// not login
if (!userStore.getTokenState) {
clear();
......@@ -28,31 +29,37 @@ export function useLockPage() {
}, lockTime * 60 * 1000);
}
function lockPage() {
function lockPage(): void {
appStore.commitLockInfoState({
isLock: true,
pwd: undefined,
});
}
watchEffect(() => {
watchEffect((onClean) => {
if (userStore.getTokenState) {
resetCalcLockTimeout();
} else {
clear();
}
onClean(() => {
clear();
});
});
onUnmounted(() => {
clear();
});
const [keyupFn] = useThrottle(resetCalcLockTimeout, 2000);
return {
registerGlobOnKeyup: keyupFn,
registerGlobOnMouseMove: keyupFn,
on: {
onKeyup: keyupFn,
onMousemove: keyupFn,
},
};
return computed(() => {
const openLockPage = appStore.getProjectConfig.lockTime;
if (openLockPage) {
return { onKeyup: keyupFn, onMousemove: keyupFn };
} else {
clear();
return {};
}
});
}
import type { RouteRecordRaw } from 'vue-router';
import { appStore } from '/@/store/modules/app';
import { permissionStore } from '/@/store/modules/permission';
import { userStore } from '/@/store/modules/user';
import { useTabs } from './useTabs';
import { RoleEnum } from '/@/enums/roleEnum';
import router, { resetRouter } from '/@/router';
import { userStore } from '/@/store/modules/user';
import { isArray } from '/@/utils/is';
import { RootRoute } from '/@/router/routes';
import type { RouteRecordRaw } from 'vue-router';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum';
import { intersection } from 'lodash-es';
import { isArray } from '/@/utils/is';
// User permissions related operations
export function usePermission() {
/**
* 更换权限模式
* Change permission mode
*/
async function togglePermissionMode() {
appStore.commitProjectConfigState({
......@@ -25,6 +31,10 @@ export function usePermission() {
// location.reload();
}
/**
* Reset and regain authority resource information
* @param id
*/
async function resume(id?: string | number) {
resetRouter();
const routes = await permissionStore.buildRoutesAction(id);
......@@ -41,12 +51,12 @@ export function usePermission() {
}
/**
* 角色模式下判断是否显示
* Determine whether there is permission
*/
function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
const permMode = appStore.getProjectConfig.permissionMode;
if (PermissionModeEnum.ROLE === permMode) {
// !不传默认可见
// Visible by default
if (!value) {
return def;
}
......@@ -56,7 +66,7 @@ export function usePermission() {
return (intersection(value, userStore.getRoleListState) as RoleEnum[]).length > 0;
}
if (PermissionModeEnum.BACK === permMode) {
// !不传默认可见
// Visible by default
if (!value) {
return def;
}
......@@ -66,17 +76,18 @@ export function usePermission() {
}
return (intersection(value, allCodeList) as string[]).length > 0;
}
return true;
}
/**
* 更新角色
* Change roles
* @param roles
*/
async function changeRole(roles: RoleEnum | RoleEnum[]): Promise<void> {
if (appStore.getProjectConfig.permissionMode !== PermissionModeEnum.ROLE) {
throw new Error('请在配置中将PermissionModeEnum切换为ROLE模式在进行操作!');
throw new Error(
'Please switch PermissionModeEnum to ROLE mode in the configuration to operate!'
);
}
if (!isArray(roles)) {
roles = [roles];
......@@ -86,10 +97,10 @@ export function usePermission() {
}
/**
*
* Change menu
*/
async function changeMenu(id?: string | number) {
// 这里传入id是为测试,实际可以不用传,会自动获取登录人的id
// TODO The id passed in here is for testing. Actually, you don’t need to pass it. The id of the login person will be automatically obtained.
resume(id);
}
......
import { Ref, unref } from 'vue';
import { isWindow } from '/@/utils/is';
export const useRect = (elementRef: (Element | Window) | Ref<Element | Window | undefined>) => {
const element = unref(elementRef);
if (isWindow(element)) {
const width = element.innerWidth;
const height = element.innerHeight;
return {
top: 0,
left: 0,
right: width,
bottom: height,
width,
height,
};
}
if (element && element.getBoundingClientRect) {
return element.getBoundingClientRect();
}
return {
top: 0,
left: 0,
right: 0,
bottom: 0,
width: 0,
height: 0,
};
};
......@@ -35,7 +35,7 @@ interface SwitchOptions {
}
interface SelectConfig {
options?: SelectOptions;
options?: LabelValueOptions;
def?: any;
disabled?: boolean;
handler?: Fn;
......
import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuThemeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
import { ContentEnum, RouterTransitionEnum, ThemeEnum } from '/@/enums/appEnum';
import { TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
export enum HandlerEnum {
CHANGE_LAYOUT,
......@@ -40,11 +40,11 @@ export enum HandlerEnum {
export const themeOptions = [
{
value: MenuThemeEnum.LIGHT,
value: ThemeEnum.LIGHT,
label: '亮色',
},
{
value: MenuThemeEnum.DARK,
value: ThemeEnum.DARK,
label: '暗色',
},
];
......
......@@ -4,34 +4,40 @@ import router, { setupRouter } from '/@/router';
import { setupStore } from '/@/store';
import { setupAntd } from '/@/setup/ant-design-vue';
import { setupErrorHandle } from '/@/setup/error-handle';
import { setupDirectives } from '/@/setup/directives';
import { setupGlobDirectives } from '/@/setup/directives';
import { isDevMode, isProdMode, isUseMock } from '/@/utils/env';
import { setupProdMockServer } from '../mock/_createProductionServer';
import { setApp } from './useApp';
import { setApp } from '/@/setup/Application';
import App from './App.vue';
import { isDevMode, isProdMode, isUseMock } from '/@/utils/env';
import '/@/design/index.less';
const app = createApp(App);
// ui
// Configure component library
setupAntd(app);
// router
// Configure routing
setupRouter(app);
// store
// Configure vuex store
setupStore(app);
// Directives
setupDirectives(app);
// Register global directive
setupGlobDirectives(app);
// error-handle
// Configure global error handling
setupErrorHandle(app);
// Mount when the route is ready
router.isReady().then(() => {
app.mount('#app');
});
// The development environment takes effect
if (isDevMode()) {
app.config.performance = true;
window.__APP__ = app;
......
import type { ProjectConfig } from '/@/types/config';
import { MenuTypeEnum, MenuThemeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { primaryColor } from '../../build/config/lessModifyVars';
import { isProdMode } from '/@/utils/env';
......@@ -39,7 +39,7 @@ const setting: ProjectConfig = {
// 是否显示顶部
show: true,
// theme
theme: MenuThemeEnum.LIGHT,
theme: ThemeEnum.LIGHT,
// 开启锁屏功能
useLockPage: true,
// 显示刷新按钮
......@@ -74,7 +74,7 @@ const setting: ProjectConfig = {
// 菜单类型
type: MenuTypeEnum.SIDEBAR,
// 菜单主题
theme: MenuThemeEnum.DARK,
theme: ThemeEnum.DARK,
// 分割菜单
split: false,
// 顶部菜单布局
......
// Application related functions
/**
* Application configuration
*/
import type { ProjectConfig } from '/@/types/config';
import type { App } from 'vue';
import { computed, ref } from 'vue';
......@@ -18,7 +21,9 @@ import {
import { appStore } from '/@/store/modules/app';
// Used to share global app instances
let app: App;
export function setApp(_app: App): void {
app = _app;
}
......@@ -27,7 +32,7 @@ export function getApp(): App {
return app;
}
// TODO 主题切换
// TODO Theme switching
export function useThemeMode(mode: ThemeModeEnum) {
const modeRef = ref(mode);
const html = document.documentElement;
......@@ -43,7 +48,7 @@ export function useThemeMode(mode: ThemeModeEnum) {
}
// Initial project configuration
export function useInitAppConfigStore() {
export function initAppConfigStore() {
let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig;
if (!projCfg) {
projCfg = projectSetting;
......@@ -67,8 +72,8 @@ export function useInitAppConfigStore() {
appStore.commitProjectConfigState(projCfg);
}
// Config Provider
export function useConfigProvider() {
// antdv Config Provider
export function getConfigProvider() {
function transformCellText({ text }: { text: string }) {
if (isNull(text) || isUnDef(text)) {
return ' - ';
......
// Load on demand
// This module only introduces components globally before login
import type { App } from 'vue';
import {
......
@import (reference) '../../design/index.less';
.app-svg-loading {
position: relative;
width: auto;
&__tip {
display: block;
margin-top: 4px;
font-size: 13px;
color: #303133;
text-align: center;
}
}
import { Spin } from 'ant-design-vue';
import svgImg from '/@/assets/images/loading.svg';
import './spin.less';
Spin.setDefaultIndicator({
indicator: () => {
return (
<div class="app-svg-loading">
<div
class="app-svg-loading"
style={{
position: 'relative',
width: 'auto',
}}
>
<img src={svgImg} alt="" height="32" width="32" class="g-loading" />
</div>
);
......
/**
* Application configuration
*/
import type { ProjectConfig } from '/@/types/config';
import type { App } from 'vue';
import { computed, ref } from 'vue';
import { ThemeModeEnum } from '/@/enums/appEnum';
import { PROJ_CFG_KEY } from '/@/enums/cacheEnum';
import projectSetting from '/@/settings/projectSetting';
import { getLocal } from '/@/utils/helper/persistent';
import { isUnDef, isNull } from '/@/utils/is';
import {
updateGrayMode,
updateColorWeak,
updateHeaderBgColor,
updateSidebarBgColor,
} from '/@/setup/theme';
import { appStore } from '/@/store/modules/app';
// Used to share global app instances
let app: App;
export function setApp(_app: App): void {
app = _app;
}
export function getApp(): App {
return app;
}
// TODO Theme switching
export function useThemeMode(mode: ThemeModeEnum) {
const modeRef = ref(mode);
const html = document.documentElement;
const clsList = html.classList;
const change = () => {
clsList.contains(mode) ? clsList.remove(mode) : clsList.add(mode);
};
return {
runChangeThemeMode: change,
mode: computed(() => modeRef.value),
};
}
// Initial project configuration
export function initAppConfigStore() {
let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig;
if (!projCfg) {
projCfg = projectSetting;
}
const { colorWeak, grayMode, headerBgColor, menuBgColor } = projCfg;
try {
// if (
// themeColor !== primaryColor &&
// themeColor &&
// process.env.VUE_APP_USE_THEME_REPLACER !== 'TRUE'
// ) {
// updateTheme(themeColor);
// }
headerBgColor && updateHeaderBgColor(headerBgColor);
menuBgColor && updateSidebarBgColor(menuBgColor);
grayMode && updateGrayMode(grayMode);
colorWeak && updateColorWeak(colorWeak);
} catch (error) {
console.log(error);
}
appStore.commitProjectConfigState(projCfg);
}
// antdv Config Provider
export function getConfigProvider() {
function transformCellText({ text }: { text: string }) {
if (isNull(text) || isUnDef(text)) {
return ' - ';
}
return text;
}
return {
transformCellText,
};
}
/**
* Configure and register global directives
*/
import type { App } from 'vue';
import { setupPermissionDirective } from './permission';
export function setupDirectives(app: App) {
export function setupGlobDirectives(app: App) {
setupPermissionDirective(app);
}
/**
* Global authority directive
* Used for fine-grained control of component permissions
* @Example v-auth="RoleEnum.TEST"
*/
import type { App, Directive, DirectiveBinding } from 'vue';
import { appStore } from '/@/store/modules/app';
import type { App } from 'vue';
import { usePermission } from '/@/hooks/web/usePermission';
import { PermissionModeEnum } from '/@/enums/appEnum';
const { hasPermission } = usePermission();
......@@ -13,18 +19,28 @@ function isAuth(el: Element, binding: any) {
}
}
}
function isBackMode() {
return appStore.getProjectConfig.permissionMode === PermissionModeEnum.BACK;
}
const mounted = (el: Element, binding: DirectiveBinding<any>) => {
if (isBackMode()) return;
isAuth(el, binding);
};
const updated = (el: Element, binding: DirectiveBinding<any>) => {
if (!isBackMode()) return;
isAuth(el, binding);
};
const authDirective: Directive = {
mounted,
updated,
};
export function setupPermissionDirective(app: App) {
app.directive('auth', {
mounted(el: Element, binding) {
if (isBackMode()) return;
isAuth(el, binding);
},
updated(el: Element, binding) {
if (!isBackMode()) return;
isAuth(el, binding);
},
});
app.directive('auth', authDirective);
}
export default authDirective;
/**
* Prevent repeated clicks
* @Example v-repeat-click="()=>{}"
*/
import { on, once } from '/@/utils/domUtils';
import type { Directive, DirectiveBinding } from 'vue';
export default {
beforeMount(el: Element, binding: any) {
let interval: ReturnType<typeof setInterval> | null = null;
const repeatDirective: Directive = {
beforeMount(el: Element, binding: DirectiveBinding<any>) {
let interval: Nullable<IntervalHandle> = null;
let startTime = 0;
const handler = () => binding.value && binding.value();
const clear = () => {
const handler = (): void => binding.value && binding.value();
const clear = (): void => {
if (Date.now() - startTime < 100) {
handler();
}
......@@ -13,7 +18,7 @@ export default {
interval = null;
};
on(el, 'mousedown', (e) => {
on(el, 'mousedown', (e: MouseEvent): void => {
if ((e as any).button !== 0) return;
startTime = Date.now();
once(document as any, 'mouseup', clear);
......@@ -22,3 +27,5 @@ export default {
});
},
};
export default repeatDirective;
/**
* Used to configure the global error handling function, which can monitor vue errors, script errors, static resource errors and Promise errors
*/
import { errorStore, ErrorInfo } from '/@/store/modules/error';
import { useSetting } from '/@/hooks/core/useSetting';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { App } from 'vue';
/**
* Handling error stack information
* @param error
*/
function processStackMsg(error: Error) {
if (!error.stack) {
return '';
}
let stack = error.stack
.replace(/\n/gi, '') // 去掉换行,节省传输内容大小
.replace(/\bat\b/gi, '@') // chrome中是at,ff中是@
.split('@') // 以@分割信息
.slice(0, 9) // 最大堆栈长度(Error.stackTraceLimit = 10),所以只取前10条
.map((v) => v.replace(/^\s*|\s*$/g, '')) // 去除多余空格
.join('~') // 手动添加分隔符,便于后期展示
.replace(/\?[^:]+/gi, ''); // 去除js文件链接的多余参数(?x=1之类)
.replace(/\n/gi, '') // Remove line breaks to save the size of the transmitted content
.replace(/\bat\b/gi, '@') // At in chrome, @ in ff
.split('@') // Split information with @
.slice(0, 9) // The maximum stack length (Error.stackTraceLimit = 10), so only take the first 10
.map((v) => v.replace(/^\s*|\s*$/g, '')) // Remove extra spaces
.join('~') // Manually add separators for later display
.replace(/\?[^:]+/gi, ''); // Remove redundant parameters of js file links (?x=1 and the like)
const msg = error.toString();
if (stack.indexOf(msg) < 0) {
stack = msg + '@' + stack;
......@@ -21,6 +30,10 @@ function processStackMsg(error: Error) {
return stack;
}
/**
* get comp name
* @param vm
*/
function formatComponentName(vm: any) {
if (vm.$root === vm) {
return {
......@@ -43,6 +56,10 @@ function formatComponentName(vm: any) {
};
}
/**
* Configure Vue error handling function
*/
function vueErrorHandler(err: Error, vm: any, info: string) {
const { name, path } = formatComponentName(vm);
errorStore.commitErrorInfoState({
......@@ -56,6 +73,9 @@ function vueErrorHandler(err: Error, vm: any, info: string) {
});
}
/**
* Configure script error handling function
*/
export function scriptErrorHandler(
event: Event | string,
source?: string,
......@@ -86,6 +106,9 @@ export function scriptErrorHandler(
return true;
}
/**
* Configure Promise error handling function
*/
function registerPromiseErrorHandler() {
window.addEventListener(
'unhandledrejection',
......@@ -104,8 +127,11 @@ function registerPromiseErrorHandler() {
);
}
/**
* Configure monitoring resource loading error handling function
*/
function registerResourceErrorHandler() {
// 监控资源加载错误(img,script,css,以及jsonp)
// Monitoring resource loading error(img,script,css,and jsonp)
window.addEventListener(
'error',
function (e: Event) {
......@@ -129,19 +155,23 @@ function registerResourceErrorHandler() {
);
}
/**
* Configure global error handling
* @param app
*/
export function setupErrorHandle(app: App) {
const { projectSetting } = useSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) {
return;
}
// Vue异常监控;
if (!useErrorHandle) return;
// Vue exception monitoring;
app.config.errorHandler = vueErrorHandler;
// js错误
// script error
window.onerror = scriptErrorHandler;
// promise 异常
// promise exception
registerPromiseErrorHandler();
// 静态资源异常
// Static resource exception
registerResourceErrorHandler();
}
import { isHexColor, colorIsDark, lighten, darken } from '/@/utils/color';
import { appStore } from '/@/store/modules/app';
import { MenuThemeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum';
const HEADER_BG_COLOR_VAR = '--header-bg-color';
const HEADER_BG_HOVER_COLOR_VAR = '--header-bg-hover-color';
......@@ -22,14 +22,26 @@ function toggleClass(flag: boolean, clsName: string) {
document.body.className = flag ? `${className} ${clsName} ` : className;
}
/**
* Change the status of the project's color weakness mode
* @param gray
*/
export const updateColorWeak = (colorWeak: boolean) => {
toggleClass(colorWeak, 'color-weak');
};
/**
* Change project gray mode status
* @param gray
*/
export const updateGrayMode = (gray: boolean) => {
toggleClass(gray, 'gray-mode');
};
/**
* Change the background color of the top header
* @param color
*/
export function updateHeaderBgColor(color: string) {
if (!isHexColor(color)) return;
// bg color
......@@ -40,15 +52,20 @@ export function updateHeaderBgColor(color: string) {
setCssVar(HEADER_BG_HOVER_COLOR_VAR, hoverColor);
setCssVar(HEADER_MENU_ACTIVE_BG_COLOR_VAR, hoverColor);
// Determine the depth of the color value and automatically switch the theme
const isDark = colorIsDark(color);
appStore.commitProjectConfigState({
headerSetting: {
theme: isDark ? MenuThemeEnum.DARK : MenuThemeEnum.LIGHT,
theme: isDark ? ThemeEnum.DARK : ThemeEnum.LIGHT,
},
});
}
/**
* Change the background color of the left menu
* @param color bg color
*/
export function updateSidebarBgColor(color: string) {
if (!isHexColor(color)) return;
......@@ -58,11 +75,12 @@ export function updateSidebarBgColor(color: string) {
setCssVar(SIDER_LIGHTEN_2_BG_COLOR, lighten(color, 8));
// only #ffffff is light
// Only when the background color is #fff, the theme of the menu will be changed to light
const isLight = ['#fff', '#ffffff'].includes(color.toLowerCase());
appStore.commitProjectConfigState({
menuSetting: {
theme: isLight ? MenuThemeEnum.LIGHT : MenuThemeEnum.DARK,
theme: isLight ? ThemeEnum.LIGHT : ThemeEnum.DARK,
},
});
}
import type { App } from 'vue';
import {
createStore,
// createLogger, Plugin
} from 'vuex';
import { createStore, createLogger, Plugin } from 'vuex';
import { config } from 'vuex-module-decorators';
import { isDevMode } from '/@/utils/env';
config.rawError = true;
const isDev = isDevMode();
// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
const store = createStore({
modules: {},
// modules: {},
strict: isDev,
// plugins,
plugins,
});
export function setupStore(app: App<Element>) {
......
// 左侧菜单, 顶部菜单
import { MenuTypeEnum, MenuModeEnum, MenuThemeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
export interface MessageSetting {
title: string;
......@@ -20,7 +20,7 @@ export interface MenuSetting {
menuWidth: number;
mode: MenuModeEnum;
type: MenuTypeEnum;
theme: MenuThemeEnum;
theme: ThemeEnum;
topMenuAlign: 'start' | 'center' | 'end';
collapsedShowSearch: boolean;
trigger: TriggerEnum;
......@@ -41,7 +41,7 @@ export interface MultiTabsSetting {
export interface HeaderSetting {
fixed: boolean;
show: boolean;
theme: MenuThemeEnum;
theme: ThemeEnum;
// 显示刷新按钮
showRedo: boolean;
// 显示全屏按钮
......
/**
* @description: 输入框事件
*/
declare interface ChangeEvent extends Event {
target: HTMLInputElement;
}
interface WheelEvent {
declare interface WheelEvent {
path?: EventTarget[];
}
......@@ -6,7 +6,6 @@ declare interface PromiseFn<T = any, R = T> {
(...arg: T[]): Promise<R>;
}
// 任意对象
declare interface IObj<T = any> {
[key: string]: T;
[key: number]: T;
......@@ -34,19 +33,11 @@ declare type Indexable<T = any> = {
declare type Hash<T> = Indexable<T>;
// declare type DeepPartial<T> = {
// [P in keyof T]?: T[P] extends (infer U)[]
// ? RecursivePartial<U>[]
// : T[P] extends object
// ? RecursivePartial<T[P]>
// : T[P];
// };
declare type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
declare type SelectOptions = {
declare type LabelValueOptions = {
label: string;
value: any;
}[];
......@@ -54,3 +45,7 @@ declare type SelectOptions = {
declare type EmitType = (event: string, ...args: any[]) => void;
declare type TargetContext = '_self' | '_blank';
declare type TimeoutHandle = ReturnType<typeof setTimeout>;
declare type IntervalHandle = ReturnType<typeof setInterval>;
......@@ -2,6 +2,7 @@ import type { App } from 'vue';
declare global {
declare interface Window {
// Global vue app instance
__APP__: App<Element>;
}
}
import { userStore } from '/@/store/modules/user';
// import { permissionStore } from '@/store/modules/permission';
// import { RoleEnum } from '@/enums/roleEnum';
/**
* @description: 获取token
* @description: Get token
* @return jwt token
*/
export function getToken(): string {
return userStore.getTokenState;
......
......@@ -4,6 +4,6 @@
export function triggerWindowResize() {
const event = document.createEvent('HTMLEvents');
event.initEvent('resize', true, true);
(event as any).eventType = 'message';
(event as ChangeEvent).eventType = 'message';
window.dispatchEvent(event);
}
......@@ -50,10 +50,6 @@ export function isArray(val: unknown): val is Array<any> {
return val && Array.isArray(val);
}
export const isClient = () => {
return typeof window !== 'undefined';
};
export const isWindow = (val: any): val is Window => {
return typeof window !== 'undefined' && is(val, 'Window');
};
......@@ -64,6 +60,8 @@ export const isElement = (val: unknown): val is Element => {
export const isServer = typeof window === 'undefined';
export const isClient = typeof window !== 'undefined';
export function isImageDom(o: Element) {
return o && ['IMAGE', 'IMG'].includes(o.tagName);
}
......
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
export function warn(message: string) {
console.warn(`[${projectName} warn]:${message}`);
}
......@@ -9,9 +9,6 @@
export default defineComponent({
name: 'Welcome',
components: { House },
setup() {
return {};
},
});
</script>
<style lang="less" scoped>
......
import { FormSchema } from '/@/components/Form';
const basicOptions: SelectOptions = [
const basicOptions: LabelValueOptions = [
{
label: '付晓晓',
value: '1',
......@@ -11,7 +11,7 @@ const basicOptions: SelectOptions = [
},
];
const storeTypeOptions: SelectOptions = [
const storeTypeOptions: LabelValueOptions = [
{
label: '私密',
value: '1',
......
......@@ -58,7 +58,7 @@
import { Alert, Divider } from 'ant-design-vue';
import CurrentPermissionMode from '../CurrentPermissionMode.vue';
import { usePermission } from '/@/hooks/web/usePermission';
import Authority from '/@/components/Authority';
import { Authority } from '/@/components/Authority';
import { getPermCodeByUserId } from '/@/api/sys/user';
import { permissionStore } from '/@/store/modules/permission';
import { PermissionModeEnum } from '/@/enums/appEnum';
......
......@@ -65,7 +65,7 @@
import { userStore } from '/@/store/modules/user';
import { RoleEnum } from '/@/enums/roleEnum';
import { usePermission } from '/@/hooks/web/usePermission';
import Authority from '/@/components/Authority';
import { Authority } from '/@/components/Authority';
export default defineComponent({
components: { Alert, CurrentPermissionMode, Divider, Authority },
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册