feat(全局): 路由切换取消未完成请求&icon 更新&ms-tree 树组件&ms-split-box 分割组件&ms-empty 全局空状态&部分样式调整

This commit is contained in:
baiqi 2023-09-04 11:59:50 +08:00 committed by fit2-zhao
parent d6d1724f45
commit 5f6bf18411
44 changed files with 726 additions and 104 deletions

View File

@ -37,7 +37,7 @@
"dependencies": {
"@7polo/kity": "2.0.8",
"@7polo/kityminder-core": "1.4.53",
"@arco-design/web-vue": "^2.49.2",
"@arco-design/web-vue": "^2.51.0",
"@arco-themes/vue-ms-theme-default": "^0.0.28",
"@form-create/arco-design": "^3.1.22",
"@types/color": "^3.0.3",

View File

@ -1,25 +1,29 @@
<template>
<a-config-provider :locale="locale">
<router-view />
<global-setting />
<template #empty>
<MsEmpty />
</template>
<!-- <global-setting /> -->
</a-config-provider>
</template>
<script lang="ts" setup>
import { computed, onBeforeMount, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
import GlobalSetting from '@/components/pure/global-setting/index.vue';
// import GlobalSetting from '@/components/pure/global-setting/index.vue';
import useLocale from '@/locale/useLocale';
import { saveBaseInfo } from '@/api/modules/setting/config';
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
import { useUserStore } from '@/store';
import useAppStore from '@/store/modules/app';
import useLicenseStore from '@/store/modules/setting/license';
import { watchStyle, watchTheme, setFavicon } from '@/utils/theme';
import { saveBaseInfo } from '@/api/modules/setting/config';
import { GetPlatformIconUrl } from '@/api/requrls/setting/config';
import { useUserStore } from '@/store';
import { useRouter } from 'vue-router';
import { getLocalStorage, setLocalStorage } from '@/utils/local-storage';
import { watchStyle, watchTheme, setFavicon } from '@/utils/theme';
import { WorkbenchRouteEnum } from './enums/routeEnum';
import MsEmpty from '@/components/pure/ms-empty/index.vue';
const appStore = useAppStore();
const userStore = useUserStore();

View File

@ -42,13 +42,9 @@ export class MSAxios {
const axiosCanceler = new AxiosCanceler();
// 请求拦截器
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
this.axiosInstance.interceptors.request.use((config: CreateAxiosOptions) => {
// 如果ignoreCancelToken为true则不添加到pending中
const {
// @ts-ignore
headers: { ignoreCancelToken },
} = config;
const ignoreCancelToken = config.requestOptions?.ignoreCancelToken;
const ignoreCancel =
ignoreCancelToken !== undefined ? ignoreCancelToken : this.options.requestOptions?.ignoreCancelToken;
@ -107,7 +103,7 @@ export class MSAxios {
headers: {
'Content-type': ContentTypeEnum.FORM_DATA,
// @ts-ignore
'ignoreCancelToken': true, // 文件上传请求不需要添加到pending中
'ignoreCancelToken': true, // 文件上传请求不需要添加到pending中,以免路由切换导致文件上传请求被取消
},
})
.then((res: AxiosResponse<Result>) => {

View File

@ -199,8 +199,8 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
errorMessageMode: 'message',
// 是否加入时间戳
joinTime: true,
// 忽略重复请求
ignoreCancelToken: true,
// 忽略取消请求的token
ignoreCancelToken: false,
// 是否携带token
withToken: true,
},

View File

@ -3,7 +3,7 @@ import { getLicenseUrl, addLicenseUrl } from '@/api/requrls/setting/authorizedMa
import type { LicenseInfo } from '@/models/setting/authorizedManagement';
// 获取当前信息
export function getLicenseInfo() {
return MSR.get<LicenseInfo>({ url: getLicenseUrl });
return MSR.get<LicenseInfo>({ url: getLicenseUrl }, { ignoreCancelToken: true });
}
// 添加License
export function addLicense(data: string) {

View File

@ -39,7 +39,7 @@ export function testEmail(data: TestEmailParams) {
// 保存基础信息
export function saveBaseInfo(data: SaveInfoParams) {
return MSR.post({ url: SaveBaseInfoUrl, data });
return MSR.post({ url: SaveBaseInfoUrl, data }, { ignoreCancelToken: true });
}
// 获取基础信息
@ -64,7 +64,7 @@ export function savePageConfig(data: SavePageConfigParams) {
// 获取界面配置
export function getPageConfig() {
return MSR.get<PageConfigReturns>({ url: GetPageConfigUrl });
return MSR.get<PageConfigReturns>({ url: GetPageConfigUrl }, { ignoreCancelToken: true });
}
// 获取认证源列表

View File

@ -4,7 +4,7 @@ import { GetVersionUrl } from '@/api/requrls/system';
// 获取系统版本
export function getSystemVersion() {
return MSR.get<string>({ url: GetVersionUrl });
return MSR.get<string>({ url: GetVersionUrl }, { ignoreCancelToken: true });
}
export default { getSystemVersion };

View File

@ -8,7 +8,7 @@ export function login(data: LoginData) {
}
export function isLogin() {
return MSR.get<LoginRes>({ url: isLoginUrl });
return MSR.get<LoginRes>({ url: isLoginUrl }, { ignoreCancelToken: true });
}
export function logout() {

View File

@ -1,7 +1,7 @@
@font-face {
font-family: iconfont; /* Project id 3462279 */
src: url('iconfont.woff2?t=1693190889145') format('woff2'), url('iconfont.woff?t=1693190889145') format('woff'),
url('iconfont.ttf?t=1693190889145') format('truetype'), url('iconfont.svg?t=1693190889145#iconfont') format('svg');
src: url('iconfont.woff2?t=1693381191489') format('woff2'), url('iconfont.woff?t=1693381191489') format('woff'),
url('iconfont.ttf?t=1693381191489') format('truetype'), url('iconfont.svg?t=1693381191489#iconfont') format('svg');
}
.iconfont {
font-size: 16px;
@ -10,6 +10,15 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-icon_folder_collapse1::before {
content: '\e756';
}
.icon-icon_folder_filled1::before {
content: '\e755';
}
.icon-icon_copy_filled::before {
content: '\e754';
}
.icon-a-icon_file-jar_colorful_ash::before {
content: '\e753';
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,27 @@
"css_prefix_text": "icon-",
"description": "DE、MS项目icon管理",
"glyphs": [
{
"icon_id": "37159110",
"name": "icon_folder_collapse",
"font_class": "icon_folder_collapse1",
"unicode": "e756",
"unicode_decimal": 59222
},
{
"icon_id": "37157216",
"name": "icon_folder_filled",
"font_class": "icon_folder_filled1",
"unicode": "e755",
"unicode_decimal": 59221
},
{
"icon_id": "37147506",
"name": "icon_copy_filled",
"font_class": "icon_copy_filled",
"unicode": "e754",
"unicode_decimal": 59220
},
{
"icon_id": "37085019",
"name": "icon_file- jar_colorful_ash",

View File

@ -14,6 +14,12 @@
/>
<missing-glyph />
<glyph glyph-name="icon_folder_collapse1" unicode="&#59222;" d="M42.666667 725.333333c0 47.146667 40.021333 85.333333 89.386666 85.333334h268.202667c13.226667 0 25.856-5.632 34.346667-15.36L532.906667 682.666667h358.997333C941.312 682.666667 981.333333 644.48 981.333333 597.333333v-554.666666c0-47.146667-40.021333-85.333333-89.386666-85.333334H132.053333C82.688-42.666667 42.666667-4.48 42.666667 42.666667V725.333333z m336.64 0H132.053333v-682.666666h759.893334V597.333333H512c-13.226667 0-25.856 5.632-34.346667 15.36L379.306667 725.333333z m190.464-268.501333c17.493333 16.64 45.781333 16.64 63.232 0l111.786666-106.666667a41.301333 41.301333 0 0 0 0-60.330666l-111.786666-106.666667c-17.493333-16.64-45.738667-16.64-63.232 0a41.301333 41.301333 0 0 0 0 60.330667l80.170666 76.501333-80.170666 76.501333a41.301333 41.301333 0 0 0 0 60.330667z m-115.541334-60.330667a41.301333 41.301333 0 0 1 0 60.330667 46.208 46.208 0 0 1-63.232 0l-111.786666-106.666667a41.301333 41.301333 0 0 1 0-60.330666l111.786666-106.666667c17.493333-16.64 45.738667-16.64 63.232 0a41.301333 41.301333 0 0 1 0 60.330667L374.058667 320l80.170666 76.501333z" horiz-adv-x="1024" />
<glyph glyph-name="icon_folder_filled1" unicode="&#59221;" d="M42.666667 725.333333a42.666667 42.666667 0 0 0 42.666666 42.666667h357.632a42.666667 42.666667 0 0 0 38.144-23.594667L512 682.666667h426.666667a42.666667 42.666667 0 0 0 42.666666-42.666667v-597.333333a42.666667 42.666667 0 0 0-42.666666-42.666667H85.333333a42.666667 42.666667 0 0 0-42.666666 42.666667V725.333333z m106.666666-128a21.333333 21.333333 0 0 1-21.333333-21.333333v-42.666667a21.333333 21.333333 0 0 1 21.333333-21.333333h725.333334a21.333333 21.333333 0 0 1 21.333333 21.333333v42.666667a21.333333 21.333333 0 0 1-21.333333 21.333333h-725.333334z" horiz-adv-x="1024" />
<glyph glyph-name="icon_copy_filled" unicode="&#59220;" d="M725.333333 597.333333v-602.069333c0-20.949333-17.834667-37.930667-39.808-37.930667H167.808C145.834667-42.666667 128-25.685333 128-4.736V602.026667C128 623.018667 145.834667 640 167.808 640H682.666667a42.666667 42.666667 0 0 0 42.666666-42.666667z m158.165334 200.832A42.538667 42.538667 0 0 0 896 768v-533.333333a21.333333 21.333333 0 0 0-21.333333-21.333334h-42.666667a21.333333 21.333333 0 0 0-21.333333 21.333334V725.333333H405.333333a21.333333 21.333333 0 0 0-21.333333 21.333334v42.666666a21.333333 21.333333 0 0 0 21.333333 21.333334H853.333333c11.776 0 22.442667-4.778667 30.165334-12.501334z" horiz-adv-x="1024" />
<glyph glyph-name="a-icon_file-jar_colorful_ash" unicode="&#59219;" d="M648.533333 853.333333a42.666667 42.666667 0 0 0 33.322667-16l170.666667-213.333333 2.730666-3.925333 1.152-1.877334a40.021333 40.021333 0 0 0 4.053334-9.941333 41.258667 41.258667 0 0 0 1.109333-5.973333L861.866667 597.333333v-85.333333h42.666666a85.333333 85.333333 0 0 0 85.333334-85.333333v-298.666667a85.333333 85.333333 0 0 0-85.333334-85.333333h-42.666666v-85.333334a42.666667 42.666667 0 0 0-42.666667-42.666666h-597.333333a42.666667 42.666667 0 0 0-42.666667 42.666666v85.333334h-42.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v298.666667a85.333333 85.333333 0 0 0 85.333333 85.333333h42.666667V810.666667a42.666667 42.666667 0 0 0 42.666667 42.666666h426.666666z m128-810.666666h-512v-42.666667h512v42.666667z m128 384h-768v-298.666667h768v298.666667z m-556.8-42.24v-116.181334c0-24.405333-2.176-43.008-6.528-55.722666-4.309333-12.757333-13.013333-23.509333-26.112-32.384-13.056-8.832-29.781333-13.226667-50.218666-13.226667-21.589333 0-38.314667 2.858667-50.176 8.704a63.104 63.104 0 0 0-27.477334 25.6c-6.485333 11.221333-10.282667 25.130667-11.434666 41.642667l63.146666 8.618666c0.085333-9.429333 0.896-16.469333 2.474667-21.034666a22.869333 22.869333 0 0 1 7.850667-11.093334c2.56-1.834667 6.144-2.730667 10.794666-2.730666 7.381333 0 12.8 2.730667 16.256 8.234666 3.456 5.504 5.205333 14.762667 5.205334 27.733334v131.84h66.176z m174.848 0L602.88 170.666667h-69.12l-10.496 35.285333H448l-10.368-35.285333H370.176l80.341333 213.76h72.064z m212.48 0c20.394667 0 36.010667-1.706667 46.805334-5.205334 10.794667-3.498667 19.498667-9.984 26.112-19.498666 6.570667-9.472 9.898667-20.992 9.898666-34.602667 0-11.861333-2.56-22.101333-7.594666-30.72a59.306667 59.306667 0 0 0-20.864-20.906667c-5.632-3.413333-13.354667-6.229333-23.168-8.490666 7.893333-2.602667 13.610667-5.248 17.194666-7.850667 2.432-1.749333 5.973333-5.504 10.581334-11.221333 4.608-5.76 7.68-10.154667 9.258666-13.269334L835.242667 170.666667h-74.666667l-35.285333 65.322666c-4.48 8.448-8.490667 13.952-11.946667 16.469334a27.733333 27.733333 0 0 1-16.213333 4.992h-5.845334V170.666667h-66.346666v213.76h110.08z m-249.258666-55.381334l-23.466667-76.842666h47.104l-23.637333 76.8z m234.496 12.245334h-29.013334v-43.477334h27.861334c2.986667 0 8.832 0.981333 17.493333 2.944a17.493333 17.493333 0 0 1 10.709333 6.698667 19.754667 19.754667 0 0 1 4.181334 12.373333c0 6.912-2.218667 12.245333-6.570667 15.914667-4.394667 3.712-12.586667 5.546667-24.661333 5.546667zM563.2 768h-298.666667v-256h512V554.666667h-170.666666a42.666667 42.666667 0 0 0-42.666667 42.666666V768z m85.333333-25.6V640h81.92L648.533333 742.4z" horiz-adv-x="1024" />
<glyph glyph-name="icon_git" unicode="&#59218;" d="M974.506667 398.592L526.805333 847.829333c-7.338667 7.338667-20.565333 7.338667-29.354666 0l-123.306667-123.306666 114.474667-114.517334a65.706667 65.706667 0 0 0 29.397333 5.845334 74.24 74.24 0 0 0 74.837333-74.837334c0-10.282667-1.450667-20.565333-5.845333-29.354666l91.008-91.050667a65.706667 65.706667 0 0 0 29.354667 5.888 74.24 74.24 0 0 0 74.88-74.88 74.24 74.24 0 0 0-74.88-74.88 74.24 74.24 0 0 0-74.88 74.88c0 10.24 1.493333 20.565333 5.888 29.354667l-91.008 91.008h-1.493334v-173.226667a75.52 75.52 0 0 0 46.976-70.442667 74.24 74.24 0 0 0-74.837333-74.88 74.24 74.24 0 0 0-74.88 74.88 75.52 75.52 0 0 0 46.933333 70.442667v170.325333a75.52 75.52 0 0 0-46.933333 70.442667c0 10.282667 1.450667 20.565333 5.845333 29.354667L334.506667 684.885333l-286.293334-286.293333c-7.338667-7.338667-7.338667-20.565333 0-29.354667l449.237334-449.237333c7.338667-7.338667 20.565333-7.338667 29.354666 0l449.237334 449.237333a20.906667 20.906667 0 0 1-1.450667 29.354667z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 353 KiB

View File

@ -130,11 +130,16 @@
}
/** 气泡确认框 **/
.ms-pop-confirm {
.ms-pop-confirm--hidden-cancel {
.arco-btn-secondary {
@apply hidden;
}
}
.ms-pop-confirm--hidden-icon {
.arco-popconfirm-icon {
@apply !hidden;
}
}
/** 按钮 **/
.arco-btn-primary {
@ -180,6 +185,11 @@
color: var(--color-text-brand);
}
}
.arco-input-clear-btn {
.arco-icon-close {
font-size: 14px;
}
}
.arco-input-wrapper,
.arco-textarea-wrapper,
.arco-input-tag,
@ -200,7 +210,7 @@
.arco-input-tag {
.arco-icon {
font-size: 14px;
color: var(--color-text-4);
color: var(--color-text-brand);
}
}
.arco-select-view-inner,
@ -209,6 +219,11 @@
background-color: var(--color-text-n8) !important;
}
}
.arco-select-view-icon svg,
.arco-select-view-clear-btn svg {
font-size: 14px;
color: var(--color-text-brand);
}
.arco-input-error,
.arco-select-view-error,
.arco-input-tag-error {
@ -250,6 +265,11 @@
}
}
}
/** form-item **/
.arco-form-item-content-flex {
@apply flex-wrap;
}
.arco-form-item-status-success .arco-input-wrapper:not(.arco-input-disabled).arco-input-focus {
border: 1px solid rgb(var(--primary-5)) !important;
background: none;
@ -258,11 +278,6 @@
.arco-form-item-status-success .arco-input-wrapper:not(.arco-input-disabled) {
border-color: var(--color-text-input-border);
}
/** form-item **/
.arco-form-item-content-flex {
@apply flex-wrap;
}
.arco-form-item-message {
width: 100%;
}
@ -629,18 +644,6 @@
color: var(--color-text-brand);
}
}
/** 标签样式 **/
.arco-tag {
.arco-icon {
font-size: 14px !important;
color: var(--color-text-4) !important;
&:hover {
font-size: 14px;
color: var(--color-text-1) !important;
}
}
.arco-icon-hover.arco-tag-icon-hover::before {
background: none !important;
}
.arco-picker-input-active input {
border-radius: var(--border-radius-mini);
}

View File

@ -1,4 +1,7 @@
@import url('./arco-reset.less');
:root {
--border-radius-mini: 2px;
}
@tailwind base;
@tailwind components;

View File

@ -1,5 +1,4 @@
/** 主题变量覆盖 **/
@border-radius-mini: 2px;
@border-radius-small: 4px;
@border-radius-medium: 6px;
@border-radius-large: 12px;

View File

@ -261,6 +261,9 @@
margin: 2px 6px;
border-radius: 4px;
}
.arco-cascader-option-active {
background-color: rgb(var(--primary-1));
}
}
.arco-cascader-panel-column:first-child {
.arco-checkbox {

View File

@ -0,0 +1,376 @@
<template>
<a-tree
v-bind="props"
ref="treeRef"
v-model:expanded-keys="expandedKeys"
:data="treeData"
class="ms-tree"
@drop="onDrop"
@select="select"
>
<template v-if="$slots['title']" #title="_props">
<slot name="title" v-bind="_props"></slot>
</template>
<template v-if="$slots['extra']" #extra="_props">
<div
:class="[
'ms-tree-node-extra',
innerFocusNodeKey === _props[props.fieldNames.key] ? 'ms-tree-node-extra--focus' : '',
]"
>
<div
class="ml-[-4px] flex h-[32px] items-center rounded-[var(--border-radius-small)] bg-[rgb(var(--primary-1))]"
>
<slot name="extra" v-bind="_props"></slot>
<MsTableMoreAction
v-if="props.nodeMoreActions"
:list="props.nodeMoreActions"
trigger="click"
@select="handleNodeMoreSelect($event, _props)"
@close="moreActionsClose"
>
<MsButton
type="text"
size="mini"
class="ms-tree-node-extra__more"
@click="innerFocusNodeKey = _props[props.fieldNames.key]"
>
<MsIcon type="icon-icon_more_outlined" size="14" class="text-[var(--color-text-4)]" />
</MsButton>
</MsTableMoreAction>
</div>
</div>
</template>
</a-tree>
</template>
<script setup lang="ts">
import { onBeforeMount, ref, h, watch, Ref, watchEffect } from 'vue';
import { debounce } from 'lodash-es';
import { mapTree } from '@/utils/index';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import type { MsTreeNodeData, MsTreeFieldNames, MsTreeSelectedData } from './types';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
const props = withDefaults(
defineProps<{
data: MsTreeNodeData[];
keyword?: string; //
searchDebounce?: number; // ms
draggable?: boolean; //
blockNode?: boolean; //
showLine?: boolean; // 线
defaultExpandAll?: boolean; //
selectable?: boolean; //
fieldNames?: MsTreeFieldNames; //
focusNodeKey?: string | number; // key
nodeMoreActions?: ActionsItem[]; //
expandAll?: boolean; // /true false
}>(),
{
searchDebounce: 300,
defaultExpandAll: false,
selectable: true,
fieldNames: () => ({
key: 'key',
title: 'title',
children: 'children',
isLeaf: 'isLeaf',
}),
}
);
const emit = defineEmits<{
(e: 'select', selectedKeys: Array<string | number>, node: MsTreeNodeData): void;
(
e: 'dragOver',
tree: MsTreeNodeData[],
dragNode: MsTreeNodeData, //
dropNode: MsTreeNodeData, //
dropPosition: number // -1 1 0
): void;
(e: 'moreActionSelect', item: ActionsItem, node: MsTreeNodeData): void;
(e: 'update:focusNodeKey', val: string | number): void;
(e: 'moreActionsClose'): void;
}>();
const treeRef: Ref = ref(null);
const originalTreeData = ref<MsTreeNodeData[]>([]);
function init() {
originalTreeData.value = mapTree<MsTreeNodeData>(props.data, (node: MsTreeNodeData) => {
if (!props.showLine) {
// 线线 switcherIcon switcherIcon
node.icon = () => h('span', { class: 'hidden' });
}
if (
node[props.fieldNames.isLeaf] ||
!node[props.fieldNames.children] ||
node[props.fieldNames.children]?.length === 0
) {
// icon线 switcherIcon 线 icon
node[props.showLine ? 'switcherIcon' : 'icon'] = () => h('span', { class: 'hidden' });
}
node.disabled = false;
return node;
});
}
onBeforeMount(() => {
init();
});
watch(
() => props.data,
() => {
init();
}
);
const expandedKeys = ref<(string | number)[]>([]);
/**
* 根据关键字过滤树节点
* @param keyword 搜索关键字
*/
function searchData(keyword: string) {
const search = (data: MsTreeNodeData[]) => {
const result: MsTreeNodeData[] = [];
data.forEach((item) => {
if (item[props.fieldNames.title].toLowerCase().indexOf(keyword.toLowerCase()) > -1) {
result.push({ ...item });
} else if (item[props.fieldNames.children]) {
const filterData = search(item[props.fieldNames.children]);
if (filterData.length) {
result.push({
...item,
[props.fieldNames.children]: filterData,
});
}
}
});
expandedKeys.value = result.map((item) => item[props.fieldNames.key]); //
return result;
};
return search(originalTreeData.value);
}
const treeData = ref<MsTreeNodeData[]>([]);
//
const updateDebouncedSearch = debounce(() => {
if (props.keyword) {
treeData.value = searchData(props.keyword);
}
}, props.searchDebounce);
watchEffect(() => {
if (!props.keyword) {
treeData.value = originalTreeData.value;
} else {
updateDebouncedSearch();
}
});
function loop(
_data: MsTreeNodeData[],
key: string | number | undefined,
callback: (item: MsTreeNodeData, index: number, arr: MsTreeNodeData[]) => void
) {
_data.some((item, index, arr) => {
if (item[props.fieldNames.key] === key) {
callback(item, index, arr);
return true;
}
if (item[props.fieldNames.children]) {
return loop(item[props.fieldNames.children], key, callback);
}
return false;
});
}
/**
* 处理拖拽结束
*/
function onDrop({
dragNode,
dropNode,
dropPosition,
}: {
dragNode: MsTreeNodeData; //
dropNode: MsTreeNodeData; //
dropPosition: number; // -1 1 0
}) {
loop(originalTreeData.value, dragNode.key, (item, index, arr) => {
arr.splice(index, 1);
});
if (dropPosition === 0) {
//
loop(originalTreeData.value, dropNode.key, (item) => {
item.children = item.children || [];
item.children.push(dragNode);
});
dropNode.isLeaf = false;
if (props.showLine) {
delete dropNode.switcherIcon; // children switcherIcon
}
} else {
//
loop(originalTreeData.value, dropNode.key, (item, index, arr) => {
arr.splice(dropPosition < 0 ? index : index + 1, 0, dragNode);
});
}
emit('dragOver', originalTreeData.value, dragNode, dropNode, dropPosition);
}
/**
* 处理树节点选中非复选框
*/
function select(selectedKeys: Array<string | number>, data: MsTreeSelectedData) {
emit('select', selectedKeys, data.selectedNodes);
}
const innerFocusNodeKey = ref(props.focusNodeKey || ''); //
watch(
() => props.focusNodeKey,
(val) => {
innerFocusNodeKey.value = val || '';
}
);
watch(
() => innerFocusNodeKey.value,
(val) => {
emit('update:focusNodeKey', val);
}
);
const focusEl = ref<HTMLElement | null>(); //
watch(
() => innerFocusNodeKey.value,
(val) => {
if (val) {
focusEl.value = treeRef.value?.$el.querySelector(`[data-key=${val}]`);
if (focusEl.value) {
focusEl.value.style.backgroundColor = 'rgb(var(--primary-1))';
}
} else if (focusEl.value) {
focusEl.value.style.backgroundColor = '';
}
}
);
/**
* 处理树节点更多按钮事件
* @param item
*/
function handleNodeMoreSelect(item: ActionsItem, node: MsTreeNodeData) {
emit('moreActionSelect', item, node);
}
function moreActionsClose() {
emit('moreActionsClose');
}
watch(
() => props.expandAll,
(val) => {
if (typeof val === 'boolean') {
treeRef.value?.expandAll(val);
}
}
);
</script>
<style lang="less">
.ms-tree {
.arco-tree-node {
border-radius: var(--border-radius-small);
&:hover {
background-color: rgb(var(--primary-1));
}
.arco-tree-node-minus-icon,
.arco-tree-node-plus-icon {
border: 1px solid var(--color-text-4);
border-radius: var(--border-radius-mini);
background-color: white;
&::after,
&::before {
background-color: var(--color-text-4);
}
}
.arco-tree-node-switcher {
.arco-tree-node-switcher-icon {
@apply flex;
color: var(--color-text-4);
}
}
.arco-tree-node-title {
&:hover {
background-color: rgb(var(--primary-1));
+ .ms-tree-node-extra {
@apply block;
}
}
.arco-tree-node-drag-icon {
.arco-icon {
font-size: 14px;
}
}
}
.ms-tree-node-extra {
@apply relative hidden;
&:hover {
@apply block;
}
.ms-tree-node-extra__btn,
.ms-tree-node-extra__more {
padding: 4px;
&:hover {
background-color: rgb(var(--primary-9));
.arco-icon {
color: rgb(var(--primary-5));
}
}
}
.ms-tree-node-extra__more {
margin-right: 4px;
}
}
.ms-tree-node-extra--focus {
@apply block;
}
.arco-tree-node-custom-icon {
@apply hidden;
}
}
.arco-tree-node-selected {
.arco-tree-node-minus-icon,
.arco-tree-node-plus-icon {
border: 1px solid rgb(var(--primary-5));
border-radius: var(--border-radius-mini);
background-color: white;
&::after,
&::before {
background-color: rgb(var(--primary-5));
}
}
.arco-tree-node-switcher-icon .arco-icon,
.arco-tree-node-title {
font-weight: 500 !important;
color: rgb(var(--primary-5));
* {
color: rgb(var(--primary-5));
}
}
}
}
</style>

View File

@ -0,0 +1,28 @@
import type { TreeNodeData, TreeFieldNames } from '@arco-design/web-vue';
export interface MsTreeFieldNames extends TreeFieldNames {
key: string;
title: string;
children: string;
isLeaf: string;
}
export type MsTreeNodeData = {
[key: string]: any;
} & TreeNodeData;
export interface MsTreeNodeStatus {
loading: boolean;
checked: boolean;
selected: boolean;
indeterminate: boolean;
expanded: boolean;
isLeaf: boolean;
}
export interface MsTreeSelectedData {
selected?: boolean;
selectedNodes: MsTreeNodeData[];
node?: MsTreeNodeData;
e?: Event;
}

View File

@ -68,7 +68,9 @@
return currentOptions.value.filter((item) => props.value.includes(item.id)) || [];
});
const change = (value: string | number | Record<string, any> | (string | number | Record<string, any>)[]) => {
const change = (
value: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
) => {
const tmpArr = Array.isArray(value) ? value : [value];
const { valueKey } = props;
emit(

View File

@ -225,7 +225,7 @@
});
function execCommandFontFamily(
value: string | number | Record<string, any> | (string | number | Record<string, any>)[]
value: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
) {
if (value === t('minder.menu.font.font')) {
return;
@ -234,7 +234,7 @@
}
function execCommandFontSize(
value: string | number | Record<string, any> | (string | number | Record<string, any>)[]
value: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
) {
if (typeof value !== 'number') {
return;

View File

@ -0,0 +1,24 @@
<template>
<a-empty :description="t('common.noData')" :in-config-provider="props.inConfigProvider">
<template #image>
<span class="hidden"></span>
</template>
</a-empty>
</template>
<script setup lang="ts">
import { useI18n } from '@/hooks/useI18n';
const props = withDefaults(
defineProps<{
inConfigProvider?: boolean;
}>(),
{
inConfigProvider: false,
}
);
const { t } = useI18n();
</script>
<style lang="less" scoped></style>

View File

@ -38,7 +38,9 @@
const props = defineProps<PageOptionsProps>();
const handleChange = (value: string | number | Record<string, any> | (string | number | Record<string, any>)[]) => {
const handleChange = (
value: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
) => {
emit('change', value as number);
};

View File

@ -0,0 +1,120 @@
<template>
<a-split
v-model:size="innerWidth"
:min="props.min"
:max="props.max"
:class="['h-full', isExpandedLeft ? '' : 'expanded-panel', isExpandAnimating ? 'animating' : '']"
>
<template #first>
<div class="ms-split-box ms-split-box--left">
<slot name="left"></slot>
</div>
</template>
<template #second>
<div class="ms-split-box ms-split-box--right">
<div class="expand-icon" @click="changeFolderExpand">
<MsIcon
:type="isExpandedLeft ? 'icon-icon_up-left_outlined' : 'icon-icon_down-right_outlined'"
class="text-[var(--color-text-brand)]"
size="12"
/>
</div>
<slot name="right"></slot>
</div>
</template>
</a-split>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const props = withDefaults(
defineProps<{
width?: number | string;
min?: number | string;
max?: number | string;
}>(),
{
width: '300px',
min: '250px',
max: 0.5,
}
);
const emit = defineEmits(['update:width']);
const innerWidth = ref(props.width || '300px');
watch(
() => props.width,
(val) => {
if (val !== undefined) {
innerWidth.value = val;
}
}
);
watch(
() => innerWidth.value,
(val) => {
emit('update:width', val);
}
);
const isExpandedLeft = ref(true);
const isExpandAnimating = ref(false); //
function changeFolderExpand() {
isExpandAnimating.value = true;
isExpandedLeft.value = !isExpandedLeft.value;
if (isExpandedLeft.value) {
innerWidth.value = props.width || '300px';
} else {
innerWidth.value = '0px';
}
//
setTimeout(() => {
isExpandAnimating.value = false;
}, 300);
}
</script>
<style lang="less" scoped>
/* stylelint-disable value-keyword-case */
.expanded-panel {
:deep(.arco-split-trigger) {
@apply hidden;
}
:deep(.arco-split-pane) {
overflow: hidden;
}
}
.animating {
:deep(.arco-split-pane) {
overflow: hidden;
transition: flex 0.3s ease;
}
}
.ms-split-box {
@apply relative h-full overflow-auto;
.ms-scroll-bar();
}
.ms-split-box--left {
width: calc(v-bind(innerWidth) - 4px);
}
.ms-split-box--right {
@apply w-full;
.expand-icon {
@apply absolute cursor-pointer;
top: 50%;
transform: translateY(-50%);
padding: 8px 2px;
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
background-color: var(--color-text-n8);
}
}
:deep(.arco-split-trigger-icon) {
font-size: 14px;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<a-dropdown trigger="hover" @select="selectHandler">
<MsButton>
<slot><icon-more /></slot>
</MsButton>
<a-dropdown :trigger="props.trigger || 'hover'" @select="selectHandler" @popup-visible-change="visibleChange">
<slot>
<MsButton><icon-more /></MsButton>
</slot>
<template #content>
<template v-for="item of props.list">
<a-divider v-if="item.isDivider" :key="`${item.label}-divider`" class="ms-dropdown-divider" />
@ -21,14 +21,21 @@
const { t } = useI18n();
const props = defineProps<{
list: ActionsItem[];
trigger?: 'click' | 'hover' | 'focus' | 'contextMenu' | ('click' | 'hover' | 'focus' | 'contextMenu')[] | undefined;
}>();
const emit = defineEmits(['select']);
const emit = defineEmits(['select', 'close']);
function selectHandler(value: SelectedValue) {
const item = props.list.find((e: ActionsItem) => t(e.label || '') === value);
emit('select', item);
}
function visibleChange(val: boolean) {
if (!val) {
emit('close');
}
}
</script>
<style lang="less" scoped>

View File

@ -124,9 +124,11 @@
</template>
<template #empty>
<div class="flex h-[20px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('msTable.empty') }}</span>
</div>
<slot name="empty">
<div class="flex h-[20px] flex-col items-center justify-center">
<span class="text-[14px] text-[var(--color-text-4)]">{{ t('msTable.empty') }}</span>
</div>
</slot>
</template>
</a-table>
<div

View File

@ -220,7 +220,9 @@
return !NOT_SHOW_PROJECT_SELECT_MODULE.includes(route.fullPath.split('/')[1]);
});
function selectProject(value: string | number | Record<string, any> | undefined) {
function selectProject(
value: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
) {
appStore.setCurrentProjectId(value as string);
}

View File

@ -204,6 +204,13 @@ export const pathMap = [
},
],
},
{
key: 'SETTING_ORGANIZATION_MEMBER', // 项目管理-文件管理
locale: 'menu.projectManagement.fileManagement',
route: RouteEnum.PROJECT_MANAGEMENT_FILE_MANAGEMENT,
permission: [],
level: MENU_LEVEL[2],
},
],
},
];

View File

@ -16,7 +16,7 @@ export enum PerformanceTestRouteEnum {
export enum ProjectManagementRouteEnum {
PROJECT_MANAGEMENT = 'projectManagement',
PROJECT_MANAGEMENT_INDEX = 'projectManagementIndex',
PROJECT_MANAGEMENT_FILE_MANAGEMENT = 'projectManagementFileManageMent',
PROJECT_MANAGEMENT_LOG = 'projectManagementLog',
PROJECT_MANAGEMENT_PERMISSION = 'projectManagementPermission',
PROJECT_MANAGEMENT_PERMISSION_BASIC_INFO = 'projectManagementPermissionBasicInfo',

View File

@ -8,7 +8,7 @@ import { useUserStore, useVisitStore } from '@/store';
export default function useVisit(key: string, needTimeStamp = false) {
const userStore = useUserStore();
const visitStore = useVisitStore();
const localKey = `${userStore.accountId}-${key}-${needTimeStamp ? new Date().getTime() : ''}`;
const localKey = `${userStore.id}-${key}-${needTimeStamp ? new Date().getTime() : ''}`;
const addVisited = () => {
visitStore.addVisitedKey(localKey);
};

View File

@ -47,4 +47,5 @@ export default {
'common.unSaveLeaveContent': 'The system may not save your changes',
'common.leave': 'Leave',
'common.rename': 'Rename',
'common.noData': 'No data',
};

View File

@ -20,13 +20,14 @@ Object.keys(_Vmodules).forEach((key) => {
export default {
message: {
'menu.workbench': 'Workbench',
'menu.testPlan': 'Test plan',
'menu.bugManagement': 'Bug management',
'menu.featureTest': 'Feature test',
'menu.apiTest': 'API test',
'menu.uiTest': 'UI test',
'menu.performanceTest': 'Performance test',
'menu.projectManagement': 'Project management',
'menu.testPlan': 'Test Plan',
'menu.bugManagement': 'Bug',
'menu.featureTest': 'Feature Test',
'menu.apiTest': 'API Test',
'menu.uiTest': 'UI Test',
'menu.performanceTest': 'Performance Test',
'menu.projectManagement': 'Project',
'menu.projectManagement.fileManagement': 'File Management',
'menu.projectManagement.projectPermission': 'Project Permission',
'menu.projectManagement.log': 'Log',
'menu.settings': 'Settings',
@ -39,7 +40,7 @@ export default {
'menu.settings.system.resourcePool': 'Resource Pool',
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
'menu.settings.system.parameter': 'System parameter',
'menu.settings.system.parameter': 'System Parameter',
'menu.settings.system.log': 'Log',
'menu.settings.organization': 'Organization',
'menu.settings.organization.member': 'Member',

View File

@ -49,4 +49,5 @@ export default {
'common.unSaveLeaveContent': '系统可能不会保存你所做的更改',
'common.leave': '离开',
'common.rename': '重命名',
'common.noData': '暂无数据',
};

View File

@ -27,6 +27,7 @@ export default {
'menu.performanceTest': '性能测试',
'menu.projectManagement': '项目管理',
'menu.projectManagement.log': '日志',
'menu.projectManagement.fileManagement': '文件管理',
'menu.projectManagement.projectPermission': '项目与权限',
'menu.settings': '系统设置',
'menu.settings.system': '系统',

View File

@ -2,11 +2,14 @@ import type { Router } from 'vue-router';
import { setRouteEmitter } from '@/utils/route-listener';
import setupUserLoginInfoGuard from './userLoginInfo';
import setupPermissionGuard from './permission';
import { AxiosCanceler } from '@/api/http/axiosCancel';
function setupPageGuard(router: Router) {
const axiosCanceler = new AxiosCanceler();
router.beforeEach(async (to) => {
// 监听路由变化
setRouteEmitter(to);
axiosCanceler.removeAllPending();
});
}

View File

@ -15,14 +15,6 @@ const ProjectManagement: AppRouteRecordRaw = {
hideChildrenInMenu: true,
},
children: [
{
path: 'index',
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_INDEX,
component: () => import('@/views/project-management/index.vue'),
meta: {
roles: ['*'],
},
},
// 项目与权限
{
path: 'permission',
@ -87,6 +79,17 @@ const ProjectManagement: AppRouteRecordRaw = {
},
],
},
// 文件管理
{
path: 'fileManagement',
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_FILE_MANAGEMENT,
component: () => import('@/views/project-management/fileManagement/index.vue'),
meta: {
locale: 'menu.projectManagement.fileManagement',
roles: ['*'],
isTopMenu: true,
},
},
// 项目日志
{
path: 'log',

View File

@ -26,7 +26,7 @@ const useUserStore = defineStore('user', {
locationName: undefined,
phone: undefined,
registrationDate: undefined,
accountId: undefined,
id: undefined,
certification: undefined,
role: '',
salt: '',

View File

@ -13,7 +13,7 @@ export interface UserState {
locationName?: string;
phone?: string;
registrationDate?: string;
accountId?: string;
id?: string;
certification?: number;
role: RoleType;
lastOrganizationId?: string;

View File

@ -72,7 +72,7 @@
</MsCard>
<div class="log-card">
<div class="log-card-header">
<div class="text-[var(--color-text-000)]">{{ t('system.log.log') }}</div>
<div class="font-medium text-[var(--color-text-000)]">{{ t('system.log.log') }}</div>
</div>
<ms-base-table v-bind="propsRes" no-disable sticky-header v-on="propsEvent">
<template #range="{ record }">
@ -381,7 +381,7 @@
function resetFilter() {
operUser.value = '';
operateRange.value = [MENU_LEVEL[0]];
operateRange.value = [props.mode];
type.value = '';
_module.value = '';
content.value = '';

View File

@ -150,8 +150,9 @@
</template>
<a-popconfirm
v-if="!getIsVisited()"
class="ms-pop-confirm"
position="br"
class="ms-pop-confirm--hidden-cancel"
position="bl"
popup-container="#typeRadioGroupRef"
:ok-text="t('system.resourcePool.batchAddTipConfirm')"
@popup-visible-change="handlePopChange"
>
@ -166,10 +167,12 @@
{{ t('system.resourcePool.changeAddTypeTip') }}
</div>
</template>
<a-radio-group v-model:model-value="form.addType" type="button" @change="handleTypeChange">
<a-radio value="single">{{ t('system.resourcePool.singleAdd') }}</a-radio>
<a-radio value="multiple">{{ t('system.resourcePool.batchAdd') }}</a-radio>
</a-radio-group>
<div id="typeRadioGroupRef" class="relative">
<a-radio-group v-model:model-value="form.addType" type="button" @change="handleTypeChange">
<a-radio value="single">{{ t('system.resourcePool.singleAdd') }}</a-radio>
<a-radio value="multiple">{{ t('system.resourcePool.batchAdd') }}</a-radio>
</a-radio-group>
</div>
</a-popconfirm>
<a-radio-group v-else v-model:model-value="form.addType" type="button" @change="handleTypeChange">
<a-radio value="single">{{ t('system.resourcePool.singleAdd') }}</a-radio>

View File

@ -2,7 +2,7 @@ export default {
'system.user.createUser': 'Create User',
'system.user.emailInvite': 'Email Invite',
'system.user.importUser': 'Import User',
'system.user.searchUser': 'Search by name or email address',
'system.user.searchUser': 'Search by name or email',
'system.user.editUser': 'Edit',
'system.user.resetPassword': 'Reset PSW',
'system.user.disable': 'Disable',
@ -53,7 +53,7 @@ export default {
'system.user.createUserPhonePlaceholder': 'Please enter an 11-digit mobile number',
'system.user.createUserOrganization': 'Organization',
'system.user.createUserOrganizationPlaceholder': 'Please select user organization',
'system.user.createUserUserGroup': 'UserGroup',
'system.user.createUserUserGroup': 'User Group',
'system.user.createUserUserGroupPlaceholder': 'Please select a user group',
'system.user.editUserModalTitle': 'Edit the user',
'system.user.editUserModalCancelCreate': 'Cancel',
@ -103,7 +103,7 @@ export default {
'system.user.tableColumnName': 'Name',
'system.user.tableColumnPhone': 'Phone',
'system.user.tableColumnOrg': 'Organization',
'system.user.tableColumnUserGroup': 'UserGroup',
'system.user.tableColumnUserGroup': 'User Group',
'system.user.tableColumnStatus': 'Status',
'system.user.tableColumnActions': 'Actions',
};

View File

@ -1,20 +1,15 @@
export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined;
export interface RequestOptions {
// 是否需要处理请求结果
isTransformResponse?: boolean;
// 是否需要返回原生响应头
isReturnNativeResponse?: boolean;
isTransformResponse?: boolean; // 是否需要处理请求结果
isReturnNativeResponse?: boolean; // 是否需要返回原生响应头
handleError?: boolean;
// post请求时是否使用URLSearchParams
joinParamsToUrl?: boolean;
// Error message prompt type
errorMessageMode?: ErrorMessageMode;
// Whether to add a timestamp
joinTime?: boolean;
ignoreCancelToken?: boolean;
// Whether to send token in header
withToken?: boolean;
joinParamsToUrl?: boolean; // post请求时是否使用URLSearchParams
noErrorTip?: boolean;
errorMessageMode?: ErrorMessageMode; // 错误信息提示模式,none不提示
joinTime?: boolean; // 是否加入时间戳
ignoreCancelToken?: boolean; // 是否不记录取消请求的token不记录则请求不会被取消默认为记录在路由切换时会清除上个页面未完成的请求
withToken?: boolean; // 是否携带token
}
export interface Result<T = any> {